summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/bcast-relay/udp-broadcast-relay.tmpl7
-rw-r--r--data/templates/dhcp-relay/config.tmpl17
-rw-r--r--data/templates/dhcp-server/daemon.tmpl8
-rw-r--r--data/templates/dhcp-server/dhcpd.conf.tmpl195
-rw-r--r--data/templates/dhcpv6-relay/config.tmpl4
-rw-r--r--data/templates/dhcpv6-server/daemon.tmpl8
-rw-r--r--data/templates/dhcpv6-server/dhcpdv6.conf.tmpl78
-rw-r--r--data/templates/dns-forwarding/recursor.conf.tmpl44
-rw-r--r--data/templates/dynamic-dns/ddclient.conf.tmpl49
-rw-r--r--data/templates/frr-bfd/bfd.frr.tmpl16
-rw-r--r--data/templates/https/nginx.default.tmpl61
-rw-r--r--data/templates/igmp-proxy/igmpproxy.conf.tmpl37
-rw-r--r--data/templates/igmp/igmp.frr.tmpl41
-rw-r--r--data/templates/ipoe-server/chap-secrets.tmpl18
-rw-r--r--data/templates/ipoe-server/ipoe.config.tmpl123
-rw-r--r--data/templates/ipsec/ipsec.conf.tmpl3
-rw-r--r--data/templates/ipsec/ipsec.secrets.tmpl7
-rw-r--r--data/templates/ipsec/remote-access.tmpl28
-rw-r--r--data/templates/l2tp/chap-secrets.tmpl11
-rw-r--r--data/templates/l2tp/l2tp.config.tmpl173
-rw-r--r--data/templates/lldp/lldpd.tmpl3
-rw-r--r--data/templates/lldp/vyos.conf.tmpl20
-rw-r--r--data/templates/mdns-repeater/mdns-repeater.tmpl2
-rw-r--r--data/templates/mpls/ldpd.frr.tmpl58
-rw-r--r--data/templates/netflow/uacctd.conf.tmpl69
-rw-r--r--data/templates/ntp/ntp.conf.tmpl38
-rw-r--r--data/templates/openvpn/client.conf.tmpl16
-rw-r--r--data/templates/openvpn/server.conf.tmpl223
-rw-r--r--data/templates/pim/pimd.frr.tmpl34
-rw-r--r--data/templates/pppoe-server/chap-secrets.tmpl11
-rw-r--r--data/templates/pppoe-server/pppoe.config.tmpl228
-rw-r--r--data/templates/pppoe/ip-down.script.tmpl26
-rw-r--r--data/templates/pppoe/ip-pre-up.script.tmpl19
-rw-r--r--data/templates/pppoe/ip-up.script.tmpl47
-rw-r--r--data/templates/pppoe/ipv6-up.script.tmpl41
-rw-r--r--data/templates/pppoe/peer.tmpl63
-rw-r--r--data/templates/pptp/chap-secrets.tmpl6
-rw-r--r--data/templates/pptp/pptp.config.tmpl87
-rw-r--r--data/templates/router-advert/radvd.conf.tmpl37
-rw-r--r--data/templates/salt-minion/minion.tmpl63
-rw-r--r--data/templates/snmp/etc.snmp.conf.tmpl4
-rw-r--r--data/templates/snmp/etc.snmpd.conf.tmpl118
-rw-r--r--data/templates/snmp/usr.snmpd.conf.tmpl6
-rw-r--r--data/templates/snmp/var.snmpd.conf.tmpl16
-rw-r--r--data/templates/ssh/sshd_config.tmpl125
-rw-r--r--data/templates/sstp/chap-secrets.tmpl10
-rw-r--r--data/templates/sstp/sstp.config.tmpl114
-rw-r--r--data/templates/syslog/logrotate.tmpl12
-rw-r--r--data/templates/syslog/rsyslog.conf.tmpl44
-rw-r--r--data/templates/system-login/pam_radius_auth.conf.tmpl13
-rw-r--r--data/templates/tftp-server/default.tmpl2
-rw-r--r--data/templates/vrf/vrf.conf.tmpl8
-rw-r--r--data/templates/vrrp/daemon.tmpl5
-rw-r--r--data/templates/vrrp/keepalived.conf.tmpl97
-rw-r--r--data/templates/wifi/cfg80211.conf.tmpl3
-rw-r--r--data/templates/wifi/crda.tmpl3
-rw-r--r--data/templates/wifi/hostapd.conf.tmpl728
-rw-r--r--data/templates/wifi/wpa_supplicant.conf.tmpl9
-rw-r--r--data/templates/wwan/chat.tmpl6
-rw-r--r--data/templates/wwan/ip-down.script.tmpl26
-rw-r--r--data/templates/wwan/ip-pre-up.script.tmpl23
-rw-r--r--data/templates/wwan/ip-up.script.tmpl25
-rw-r--r--data/templates/wwan/peer.tmpl30
-rw-r--r--interface-definitions/flow-accounting-conf.xml.in2
-rw-r--r--interface-definitions/interfaces-pppoe.xml.in3
-rw-r--r--interface-definitions/interfaces-wireguard.xml.in32
-rw-r--r--interface-definitions/interfaces-wireless.xml.in6
-rw-r--r--interface-definitions/interfaces-wirelessmodem.xml.in2
-rw-r--r--interface-definitions/protocols-igmp.xml.in27
-rw-r--r--interface-definitions/protocols-pim.xml.in12
-rw-r--r--interface-definitions/vrrp.xml.in39
-rw-r--r--python/vyos/ifconfig/control.py30
-rw-r--r--python/vyos/ifconfig/ethernet.py27
-rw-r--r--python/vyos/ifconfig/interface.py25
-rw-r--r--python/vyos/ifconfig/l2tpv3.py16
-rw-r--r--python/vyos/util.py50
-rwxr-xr-xsrc/conf_mode/accel_l2tp.py846
-rwxr-xr-xsrc/conf_mode/bcast_relay.py59
-rwxr-xr-xsrc/conf_mode/dhcp_relay.py66
-rwxr-xr-xsrc/conf_mode/dhcp_server.py240
-rwxr-xr-xsrc/conf_mode/dhcpv6_relay.py31
-rwxr-xr-xsrc/conf_mode/dhcpv6_server.py122
-rwxr-xr-xsrc/conf_mode/dns_forwarding.py133
-rwxr-xr-xsrc/conf_mode/dynamic_dns.py77
-rwxr-xr-xsrc/conf_mode/flow_accounting_conf.py113
-rwxr-xr-xsrc/conf_mode/https.py87
-rwxr-xr-xsrc/conf_mode/igmp_proxy.py94
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py265
-rwxr-xr-xsrc/conf_mode/interfaces-pppoe.py201
-rwxr-xr-xsrc/conf_mode/interfaces-vxlan.py3
-rwxr-xr-xsrc/conf_mode/interfaces-wireguard.py27
-rwxr-xr-xsrc/conf_mode/interfaces-wireless.py790
-rwxr-xr-xsrc/conf_mode/interfaces-wirelessmodem.py128
-rwxr-xr-xsrc/conf_mode/ipsec-settings.py152
-rwxr-xr-xsrc/conf_mode/lldp.py96
-rwxr-xr-xsrc/conf_mode/mdns_repeater.py44
-rwxr-xr-xsrc/conf_mode/ntp.py63
-rwxr-xr-xsrc/conf_mode/protocols_bfd.py60
-rwxr-xr-xsrc/conf_mode/protocols_igmp.py61
-rwxr-xr-xsrc/conf_mode/protocols_mpls.py85
-rwxr-xr-xsrc/conf_mode/protocols_pim.py63
-rwxr-xr-xsrc/conf_mode/salt-minion.py113
-rwxr-xr-xsrc/conf_mode/service-ipoe.py202
-rwxr-xr-xsrc/conf_mode/service-pppoe.py297
-rwxr-xr-xsrc/conf_mode/service-router-advert.py59
-rwxr-xr-xsrc/conf_mode/snmp.py188
-rwxr-xr-xsrc/conf_mode/ssh.py149
-rwxr-xr-xsrc/conf_mode/system-login.py32
-rwxr-xr-xsrc/conf_mode/system-syslog.py96
-rwxr-xr-xsrc/conf_mode/system-wifi-regdom.py41
-rwxr-xr-xsrc/conf_mode/tftp_server.py64
-rwxr-xr-xsrc/conf_mode/vpn-pptp.py151
-rwxr-xr-xsrc/conf_mode/vpn_sstp.py150
-rwxr-xr-xsrc/conf_mode/vrf.py35
-rwxr-xr-xsrc/conf_mode/vrrp.py170
-rwxr-xr-xsrc/etc/ppp/ip-down.d/0020-wirelessmodem18
-rwxr-xr-xsrc/etc/ppp/ip-up.d/0020-wirelessmodem18
-rwxr-xr-xsrc/migration-scripts/interfaces/6-to-72
-rwxr-xr-xsrc/migration-scripts/interfaces/7-to-858
-rwxr-xr-xsrc/op_mode/show_openvpn.py4
-rwxr-xr-xsrc/op_mode/wireguard.py2
121 files changed, 4904 insertions, 4568 deletions
diff --git a/data/templates/bcast-relay/udp-broadcast-relay.tmpl b/data/templates/bcast-relay/udp-broadcast-relay.tmpl
new file mode 100644
index 000000000..3d8c3fe94
--- /dev/null
+++ b/data/templates/bcast-relay/udp-broadcast-relay.tmpl
@@ -0,0 +1,7 @@
+### Autogenerated by bcast_relay.py ###
+
+# UDP broadcast relay configuration for instance {{ id }}
+{%- if description %}
+# Comment: {{ description }}
+{% endif %}
+DAEMON_ARGS="{% if address %}-s {{ address }} {% endif %}{{ id }} {{ port }} {{ interfaces | join(' ') }}"
diff --git a/data/templates/dhcp-relay/config.tmpl b/data/templates/dhcp-relay/config.tmpl
new file mode 100644
index 000000000..7203ae9fb
--- /dev/null
+++ b/data/templates/dhcp-relay/config.tmpl
@@ -0,0 +1,17 @@
+### Autogenerated by dhcp_relay.py ###
+
+# Defaults for isc-dhcp-relay initscript
+# sourced by /etc/init.d/isc-dhcp-relay
+
+#
+# This is a POSIX shell fragment
+#
+
+# What servers should the DHCP relay forward requests to?
+SERVERS="{{ server | join(' ') }}"
+
+# On what interfaces should the DHCP relay (dhrelay) serve DHCP requests?
+INTERFACES="{{ interface | join(' ') }}"
+
+# Additional options that are passed to the DHCP relay daemon?
+OPTIONS="-4 {{ options | join(' ') }}"
diff --git a/data/templates/dhcp-server/daemon.tmpl b/data/templates/dhcp-server/daemon.tmpl
new file mode 100644
index 000000000..f88032d38
--- /dev/null
+++ b/data/templates/dhcp-server/daemon.tmpl
@@ -0,0 +1,8 @@
+### Autogenerated by dhcp_server.py ###
+
+# sourced by /etc/init.d/isc-dhcpv4-server
+
+DHCPD_CONF={{ config_file }}
+DHCPD_PID={{ pid_file }}
+OPTIONS="-4 -lf {{ lease_file }}"
+INTERFACES=""
diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl
new file mode 100644
index 000000000..5f5129451
--- /dev/null
+++ b/data/templates/dhcp-server/dhcpd.conf.tmpl
@@ -0,0 +1,195 @@
+
+### Autogenerated by dhcp_server.py ###
+
+# For options please consult the following website:
+# https://www.isc.org/wp-content/uploads/2017/08/dhcp43options.html
+#
+# log-facility local7;
+
+{% if hostfile_update %}
+on release {
+ set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name);
+ set ClientIp = binary-to-ascii(10, 8, ".",leased-address);
+ set ClientMac = binary-to-ascii(16, 8, ":",substring(hardware, 1, 6));
+ set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!");
+ execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", ClientName, ClientIp, ClientMac, ClientDomain);
+}
+
+on expiry {
+ set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name);
+ set ClientIp = binary-to-ascii(10, 8, ".",leased-address);
+ set ClientMac = binary-to-ascii(16, 8, ":",substring(hardware, 1, 6));
+ set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!");
+ execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", ClientName, ClientIp, ClientMac, ClientDomain);
+}
+{% endif %}
+{%- if host_decl_name %}
+use-host-decl-names on;
+{%- endif %}
+ddns-update-style {% if ddns_enable -%} interim {%- else -%} none {%- endif %};
+{% if static_route -%}
+option rfc3442-static-route code 121 = array of integer 8;
+option windows-static-route code 249 = array of integer 8;
+{%- endif %}
+{% if wpad -%}
+option wpad-url code 252 = text;
+{% endif %}
+
+{%- if global_parameters %}
+# The following {{ global_parameters | length }} line(s) were added as global-parameters in the CLI and have not been validated
+{%- for param in global_parameters %}
+{{ param }}
+{%- endfor -%}
+{%- endif %}
+
+# Failover configuration
+{% for network in shared_network %}
+{%- if not network.disabled -%}
+{%- for subnet in network.subnet %}
+{%- if subnet.failover_name -%}
+failover peer "{{ subnet.failover_name }}" {
+{%- if subnet.failover_status == 'primary' %}
+ primary;
+ mclt 1800;
+ split 128;
+{%- elif subnet.failover_status == 'secondary' %}
+ secondary;
+{%- endif %}
+ address {{ subnet.failover_local_addr }};
+ port 520;
+ peer address {{ subnet.failover_peer_addr }};
+ peer port 520;
+ max-response-delay 30;
+ max-unacked-updates 10;
+ load balance max seconds 3;
+}
+{% endif -%}
+{% endfor -%}
+{% endif -%}
+{% endfor %}
+
+# Shared network configration(s)
+{% for network in shared_network %}
+{%- if not network.disabled -%}
+shared-network {{ network.name }} {
+ {%- if network.authoritative %}
+ authoritative;
+ {%- endif %}
+ {%- if network.network_parameters %}
+ # The following {{ network.network_parameters | length }} line(s) were added as shared-network-parameters in the CLI and have not been validated
+ {%- for param in network.network_parameters %}
+ {{ param }}
+ {%- endfor %}
+ {%- endif %}
+ {%- for subnet in network.subnet %}
+ subnet {{ subnet.address }} netmask {{ subnet.netmask }} {
+ {%- if subnet.dns_server %}
+ option domain-name-servers {{ subnet.dns_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.domain_search %}
+ option domain-search {{ subnet.domain_search | join(', ') }};
+ {%- endif %}
+ {%- if subnet.ntp_server %}
+ option ntp-servers {{ subnet.ntp_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.pop_server %}
+ option pop-server {{ subnet.pop_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.smtp_server %}
+ option smtp-server {{ subnet.smtp_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.time_server %}
+ option time-servers {{ subnet.time_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.wins_server %}
+ option netbios-name-servers {{ subnet.wins_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.static_route %}
+ option rfc3442-static-route {{ subnet.static_route }}{% if subnet.rfc3442_default_router %}, {{ subnet.rfc3442_default_router }}{% endif %};
+ option windows-static-route {{ subnet.static_route }};
+ {%- endif %}
+ {%- if subnet.ip_forwarding %}
+ option ip-forwarding true;
+ {%- endif -%}
+ {%- if subnet.default_router %}
+ option routers {{ subnet.default_router }};
+ {%- endif -%}
+ {%- if subnet.server_identifier %}
+ option dhcp-server-identifier {{ subnet.server_identifier }};
+ {%- endif -%}
+ {%- if subnet.domain_name %}
+ option domain-name "{{ subnet.domain_name }}";
+ {%- endif -%}
+ {%- if subnet.subnet_parameters %}
+ # The following {{ subnet.subnet_parameters | length }} line(s) were added as subnet-parameters in the CLI and have not been validated
+ {%- for param in subnet.subnet_parameters %}
+ {{ param }}
+ {%- endfor -%}
+ {%- endif %}
+ {%- if subnet.tftp_server %}
+ option tftp-server-name "{{ subnet.tftp_server }}";
+ {%- endif -%}
+ {%- if subnet.bootfile_name %}
+ option bootfile-name "{{ subnet.bootfile_name }}";
+ filename "{{ subnet.bootfile_name }}";
+ {%- endif -%}
+ {%- if subnet.bootfile_server %}
+ next-server {{ subnet.bootfile_server }};
+ {%- endif -%}
+ {%- if subnet.time_offset %}
+ option time-offset {{ subnet.time_offset }};
+ {%- endif -%}
+ {%- if subnet.wpad_url %}
+ option wpad-url "{{ subnet.wpad_url }}";
+ {%- endif -%}
+ {%- if subnet.client_prefix_length %}
+ option subnet-mask {{ subnet.client_prefix_length }};
+ {%- endif -%}
+ {% if subnet.lease %}
+ default-lease-time {{ subnet.lease }};
+ max-lease-time {{ subnet.lease }};
+ {%- endif -%}
+ {%- for host in subnet.static_mapping %}
+ {% if not host.disabled -%}
+ host {% if host_decl_name -%} {{ host.name }} {%- else -%} {{ network.name }}_{{ host.name }} {%- endif %} {
+ {%- if host.ip_address %}
+ fixed-address {{ host.ip_address }};
+ {%- endif %}
+ hardware ethernet {{ host.mac_address }};
+ {%- if host.static_parameters %}
+ # The following {{ host.static_parameters | length }} line(s) were added as static-mapping-parameters in the CLI and have not been validated
+ {%- for param in host.static_parameters %}
+ {{ param }}
+ {%- endfor -%}
+ {%- endif %}
+ }
+ {%- endif %}
+ {%- endfor %}
+ {%- if subnet.failover_name %}
+ pool {
+ failover peer "{{ subnet.failover_name }}";
+ deny dynamic bootp clients;
+ {%- for range in subnet.range %}
+ range {{ range.start }} {{ range.stop }};
+ {%- endfor %}
+ }
+ {%- else %}
+ {%- for range in subnet.range %}
+ range {{ range.start }} {{ range.stop }};
+ {%- endfor %}
+ {%- endif %}
+ }
+ {%- endfor %}
+ on commit {
+ set shared-networkname = "{{ network.name }}";
+ {% if hostfile_update -%}
+ set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name);
+ set ClientIp = binary-to-ascii(10, 8, ".", leased-address);
+ set ClientMac = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));
+ set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!");
+ execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "commit", ClientName, ClientIp, ClientMac, ClientDomain);
+ {%- endif %}
+ }
+}
+{%- endif %}
+{% endfor %}
diff --git a/data/templates/dhcpv6-relay/config.tmpl b/data/templates/dhcpv6-relay/config.tmpl
new file mode 100644
index 000000000..28f7a1a58
--- /dev/null
+++ b/data/templates/dhcpv6-relay/config.tmpl
@@ -0,0 +1,4 @@
+### Autogenerated by dhcpv6_relay.py ###
+
+# Defaults for isc-dhcpv6-relay initscript sourced by /etc/init.d/isc-dhcpv6-relay
+OPTIONS="-6 -l {{ listen_addr | join(' -l ') }} -u {{ upstream_addr | join(' -u ') }} {{ options | join(' ') }}"
diff --git a/data/templates/dhcpv6-server/daemon.tmpl b/data/templates/dhcpv6-server/daemon.tmpl
new file mode 100644
index 000000000..a4967e7c3
--- /dev/null
+++ b/data/templates/dhcpv6-server/daemon.tmpl
@@ -0,0 +1,8 @@
+### Autogenerated by dhcpv6_server.py ###
+
+# sourced by /etc/init.d/isc-dhcpv6-server
+
+DHCPD_CONF={{ config_file }}
+DHCPD_PID={{ pid_file }}
+OPTIONS="-6 -lf {{ lease_file }}"
+INTERFACES=""
diff --git a/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl b/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl
new file mode 100644
index 000000000..80d620fcf
--- /dev/null
+++ b/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl
@@ -0,0 +1,78 @@
+### Autogenerated by dhcpv6_server.py ###
+
+# For options please consult the following website:
+# https://www.isc.org/wp-content/uploads/2017/08/dhcp43options.html
+
+log-facility local7;
+{%- if preference %}
+option dhcp6.preference {{ preference }};
+{%- endif %}
+
+# Shared network configration(s)
+{% for network in shared_network %}
+{%- if not network.disabled -%}
+shared-network {{ network.name }} {
+ {%- for subnet in network.subnet %}
+ subnet6 {{ subnet.network }} {
+ {%- for range in subnet.range6_prefix %}
+ range6 {{ range.prefix }}{{ " temporary" if range.temporary }};
+ {%- endfor %}
+ {%- for range in subnet.range6 %}
+ range6 {{ range.start }} {{ range.stop }};
+ {%- endfor %}
+ {%- if subnet.domain_search %}
+ option dhcp6.domain-search {{ subnet.domain_search | join(', ') }};
+ {%- endif %}
+ {%- if subnet.lease_def %}
+ default-lease-time {{ subnet.lease_def }};
+ {%- endif %}
+ {%- if subnet.lease_max %}
+ max-lease-time {{ subnet.lease_max }};
+ {%- endif %}
+ {%- if subnet.lease_min %}
+ min-lease-time {{ subnet.lease_min }};
+ {%- endif %}
+ {%- if subnet.dns_server %}
+ option dhcp6.name-servers {{ subnet.dns_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.nis_domain %}
+ option dhcp6.nis-domain-name "{{ subnet.nis_domain }}";
+ {%- endif %}
+ {%- if subnet.nis_server %}
+ option dhcp6.nis-servers {{ subnet.nis_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.nisp_domain %}
+ option dhcp6.nisp-domain-name "{{ subnet.nisp_domain }}";
+ {%- endif %}
+ {%- if subnet.nisp_server %}
+ option dhcp6.nisp-servers {{ subnet.nisp_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.sip_address %}
+ option dhcp6.sip-servers-addresses {{ subnet.sip_address | join(', ') }};
+ {%- endif %}
+ {%- if subnet.sip_hostname %}
+ option dhcp6.sip-servers-names {{ subnet.sip_hostname | join(', ') }};
+ {%- endif %}
+ {%- if subnet.sntp_server %}
+ option dhcp6.sntp-servers {{ subnet.sntp_server | join(', ') }};
+ {%- endif %}
+ {%- for host in subnet.static_mapping %}
+ {% if not host.disabled -%}
+ host {{ network.name }}_{{ host.name }} {
+ {%- if host.client_identifier %}
+ host-identifier option dhcp6.client-id {{ host.client_identifier }};
+ {%- endif %}
+ {%- if host.ipv6_address %}
+ fixed-address6 {{ host.ipv6_address }};
+ {%- endif %}
+ }
+ {%- endif %}
+ {%- endfor %}
+ }
+ {%- endfor %}
+ on commit {
+ set shared-networkname = "{{ network.name }}";
+ }
+}
+{%- endif %}
+{% endfor %}
diff --git a/data/templates/dns-forwarding/recursor.conf.tmpl b/data/templates/dns-forwarding/recursor.conf.tmpl
new file mode 100644
index 000000000..9d1e019fa
--- /dev/null
+++ b/data/templates/dns-forwarding/recursor.conf.tmpl
@@ -0,0 +1,44 @@
+### Autogenerated by dns_forwarding.py ###
+
+# XXX: pdns recursor doesn't like whitespace near entry separators,
+# especially in the semicolon-separated lists of name servers.
+# Please be careful if you edit the template.
+
+# Non-configurable defaults
+daemon=yes
+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=::
+
+# cache-size
+max-cache-entries={{ cache_size }}
+
+# negative TTL for NXDOMAIN
+max-negative-ttl={{ negative_ttl }}
+
+# ignore-hosts-file
+export-etc-hosts={{ export_hosts_file }}
+
+# listen-on
+local-address={{ listen_on | join(',') }}
+
+# dnssec
+dnssec={{ dnssec }}
+
+# forward-zones / recursion
+#
+# statement is only inserted if either one forwarding domain or nameserver is configured
+# if nothing is given at all, powerdns will act as a real recursor and resolve all requests by its own
+#
+{% if name_servers or domains %}forward-zones-recurse=
+{%- for d in domains %}
+{{ d.name }}={{ d.servers | join(";") }}
+{{- ", " if not loop.last -}}
+{%- endfor -%}
+{%- if name_servers -%}
+{%- if domains -%}, {% endif -%}.={{ name_servers | join(';') }}
+{% endif %}
+{% endif %}
diff --git a/data/templates/dynamic-dns/ddclient.conf.tmpl b/data/templates/dynamic-dns/ddclient.conf.tmpl
new file mode 100644
index 000000000..22cb38f4e
--- /dev/null
+++ b/data/templates/dynamic-dns/ddclient.conf.tmpl
@@ -0,0 +1,49 @@
+
+### Autogenerated by dynamic_dns.py ###
+daemon=1m
+syslog=yes
+ssl=yes
+pid={{ pid_file }}
+cache={{ cache_file }}
+
+{% for interface in interfaces -%}
+
+#
+# ddclient configuration for interface "{{ interface.interface }}":
+#
+{% if interface.web_url -%}
+use=web, web='{{ interface.web_url}}' {%- if interface.web_skip %}, web-skip='{{ interface.web_skip }}'{% endif %}
+{% else -%}
+use=if, if={{ interface.interface }}
+{% endif -%}
+
+{% for rfc in interface.rfc2136 -%}
+{% for record in rfc.record %}
+# RFC2136 dynamic DNS configuration for {{ record }}.{{ rfc.zone }}
+server={{ rfc.server }}
+protocol=nsupdate
+password={{ rfc.keyfile }}
+ttl={{ rfc.ttl }}
+zone={{ rfc.zone }}
+{{ record }}
+{% endfor -%}
+{% endfor -%}
+
+{% for srv in interface.service %}
+{% for host in srv.host %}
+# DynDNS provider configuration for {{ host }}
+protocol={{ srv.protocol }},
+max-interval=28d,
+login={{ srv.login }},
+password='{{ srv.password }}',
+{% if srv.server -%}
+server={{ srv.server }},
+{% endif -%}
+{% if srv.zone -%}
+zone={{ srv.zone }},
+{% endif -%}
+{{ host }}
+{% endfor %}
+{% endfor %}
+
+{% endfor %}
diff --git a/data/templates/frr-bfd/bfd.frr.tmpl b/data/templates/frr-bfd/bfd.frr.tmpl
new file mode 100644
index 000000000..7df4bfd01
--- /dev/null
+++ b/data/templates/frr-bfd/bfd.frr.tmpl
@@ -0,0 +1,16 @@
+!
+bfd
+{% for peer in old_peers -%}
+ no peer {{ peer.remote }}{% if peer.multihop %} multihop{% endif %}{% if peer.src_addr %} local-address {{ peer.src_addr }}{% endif %}{% if peer.src_if %} interface {{ peer.src_if }}{% endif %}
+{% endfor -%}
+!
+{% for peer in new_peers -%}
+ peer {{ peer.remote }}{% if peer.multihop %} multihop{% endif %}{% if peer.src_addr %} local-address {{ peer.src_addr }}{% endif %}{% if peer.src_if %} interface {{ peer.src_if }}{% endif %}
+ detect-multiplier {{ peer.multiplier }}
+ receive-interval {{ peer.rx_interval }}
+ transmit-interval {{ peer.tx_interval }}
+ {% if peer.echo_mode %}echo-mode{% endif %}
+ {% if peer.echo_interval != '' %}echo-interval {{ peer.echo_interval }}{% endif %}
+ {% if not peer.shutdown %}no {% endif %}shutdown
+{% endfor -%}
+!
diff --git a/data/templates/https/nginx.default.tmpl b/data/templates/https/nginx.default.tmpl
new file mode 100644
index 000000000..b6d3aaf83
--- /dev/null
+++ b/data/templates/https/nginx.default.tmpl
@@ -0,0 +1,61 @@
+### Autogenerated by https.py ###
+# Default server configuration
+#
+server {
+ listen 80 default_server;
+ listen [::]:80 default_server;
+ server_name _;
+ return 301 https://$server_name$request_uri;
+}
+
+{% for server in server_block_list %}
+server {
+
+ # SSL configuration
+ #
+{% if server.address == '*' %}
+ listen {{ server.port }} ssl;
+ listen [::]:{{ server.port }} ssl;
+{% else %}
+ listen {{ server.address }}:{{ server.port }} ssl;
+{% endif %}
+
+{% for name in server.name %}
+ server_name {{ name }};
+{% endfor %}
+
+{% if server.certbot %}
+ ssl_certificate /etc/letsencrypt/live/{{ server.certbot_dir }}/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/{{ server.certbot_dir }}/privkey.pem;
+ include /etc/letsencrypt/options-ssl-nginx.conf;
+ ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
+{% elif server.vyos_cert %}
+ include {{ server.vyos_cert.conf }};
+{% else %}
+ #
+ # Self signed certs generated by the ssl-cert package
+ # Don't use them in a production server!
+ #
+ include snippets/snakeoil.conf;
+{% endif %}
+
+ # proxy settings for HTTP API, if enabled; 503, if not
+ location ~ /(retrieve|configure|config-file|image|generate|show) {
+{% if server.api %}
+ proxy_pass http://localhost:{{ server.api.port }};
+ proxy_buffering off;
+{% else %}
+ return 503;
+{% endif %}
+ }
+
+ error_page 501 502 503 =200 @50*_json;
+
+ location @50*_json {
+ default_type application/json;
+ return 200 '{"error": "Start service in configuration mode: set service https api"}';
+ }
+
+}
+
+{% endfor %}
diff --git a/data/templates/igmp-proxy/igmpproxy.conf.tmpl b/data/templates/igmp-proxy/igmpproxy.conf.tmpl
new file mode 100644
index 000000000..c7fc5cef5
--- /dev/null
+++ b/data/templates/igmp-proxy/igmpproxy.conf.tmpl
@@ -0,0 +1,37 @@
+########################################################
+#
+# autogenerated by igmp_proxy.py
+#
+# The configuration file must define one upstream
+# interface, and one or more downstream interfaces.
+#
+# If multicast traffic originates outside the
+# upstream subnet, the "altnet" option can be
+# used in order to define legal multicast sources.
+# (Se example...)
+#
+# The "quickleave" should be used to avoid saturation
+# of the upstream link. The option should only
+# be used if it's absolutely nessecary to
+# accurately imitate just one Client.
+#
+########################################################
+
+{% if not disable_quickleave -%}
+quickleave
+{% endif -%}
+
+{% for interface in interfaces %}
+# Configuration for {{ interface.name }} ({{ interface.role }} interface)
+{% if interface.role == 'disabled' -%}
+phyint {{ interface.name }} disabled
+{%- else -%}
+phyint {{ interface.name }} {{ interface.role }} ratelimit 0 threshold {{ interface.threshold }}
+{%- endif -%}
+{%- for subnet in interface.alt_subnet %}
+ altnet {{ subnet }}
+{%- endfor %}
+{%- for subnet in interface.whitelist %}
+ whitelist {{ subnet }}
+{%- endfor %}
+{% endfor %}
diff --git a/data/templates/igmp/igmp.frr.tmpl b/data/templates/igmp/igmp.frr.tmpl
new file mode 100644
index 000000000..de4696c1f
--- /dev/null
+++ b/data/templates/igmp/igmp.frr.tmpl
@@ -0,0 +1,41 @@
+!
+{% for iface in old_ifaces -%}
+interface {{ iface }}
+{% for group in old_ifaces[iface].gr_join -%}
+{% if old_ifaces[iface].gr_join[group] -%}
+{% for source in old_ifaces[iface].gr_join[group] -%}
+no ip igmp join {{ group }} {{ source }}
+{% endfor -%}
+{% else -%}
+no ip igmp join {{ group }}
+{% endif -%}
+{% endfor -%}
+no ip igmp
+!
+{% endfor -%}
+{% for iface in ifaces -%}
+interface {{ iface }}
+{% if ifaces[iface].version -%}
+ip igmp version {{ ifaces[iface].version }}
+{% else -%}
+{# IGMP default version 3 #}
+ip igmp
+{% endif -%}
+{% if ifaces[iface].query_interval -%}
+ip igmp query-interval {{ ifaces[iface].query_interval }}
+{% endif -%}
+{% if ifaces[iface].query_max_resp_time -%}
+ip igmp query-max-response-time {{ ifaces[iface].query_max_resp_time }}
+{% endif -%}
+{% for group in ifaces[iface].gr_join -%}
+{% if ifaces[iface].gr_join[group] -%}
+{% for source in ifaces[iface].gr_join[group] -%}
+ip igmp join {{ group }} {{ source }}
+{% endfor -%}
+{% else -%}
+ip igmp join {{ group }}
+{% endif -%}
+{% endfor -%}
+!
+{% endfor -%}
+!
diff --git a/data/templates/ipoe-server/chap-secrets.tmpl b/data/templates/ipoe-server/chap-secrets.tmpl
new file mode 100644
index 000000000..707718e94
--- /dev/null
+++ b/data/templates/ipoe-server/chap-secrets.tmpl
@@ -0,0 +1,18 @@
+# username server password acceptable local IP addresses shaper
+{% for aifc in auth['auth_if'] %}
+{% for mac in auth['auth_if'][aifc] %}
+{% if (auth['auth_if'][aifc][mac]['up']) and (auth['auth_if'][aifc][mac]['down']) %}
+{% if auth['auth_if'][aifc][mac]['vlan'] %}
+{{aifc}}.{{auth['auth_if'][aifc][mac]['vlan']}}\t*\t{{mac.lower()}}\t*\t{{auth['auth_if'][aifc][mac]['down']}}/{{auth['auth_if'][aifc][mac]['up']}}
+{% else %}
+{{aifc}}\t*\t{{mac.lower()}}\t*\t{{auth['auth_if'][aifc][mac]['down']}}/{{auth['auth_if'][aifc][mac]['up']}}
+{% endif %}
+{% else %}
+{% if auth['auth_if'][aifc][mac]['vlan'] %}
+{{aifc}}.{{auth['auth_if'][aifc][mac]['vlan']}}\t*\t{{mac.lower()}}\t*
+{% else %}
+{{aifc}}\t*\t{{mac.lower()}}\t*
+{% endif %}
+{% endif %}
+{% endfor %}
+{% endfor %}
diff --git a/data/templates/ipoe-server/ipoe.config.tmpl b/data/templates/ipoe-server/ipoe.config.tmpl
new file mode 100644
index 000000000..109bc0d92
--- /dev/null
+++ b/data/templates/ipoe-server/ipoe.config.tmpl
@@ -0,0 +1,123 @@
+### generated by ipoe.py ###
+[modules]
+log_syslog
+ipoe
+shaper
+ipv6pool
+ipv6_nd
+ipv6_dhcp
+{% if auth['mech'] == 'radius' %}
+radius
+{% endif -%}
+ippool
+{% if auth['mech'] == 'local' %}
+chap-secrets
+{% endif %}
+
+[core]
+thread-count={{thread_cnt}}
+
+[log]
+syslog=accel-ipoe,daemon
+copy=1
+level=5
+
+[ipoe]
+verbose=1
+{% for intfc in interfaces %}
+{% if interfaces[intfc]['vlan_mon'] %}
+interface=re:{{intfc}}\.\d+,\
+{% else %}
+interface={{intfc}},\
+{% endif %}
+shared={{interfaces[intfc]['shared']}},\
+mode={{interfaces[intfc]['mode']}},\
+ifcfg={{interfaces[intfc]['ifcfg']}},\
+range={{interfaces[intfc]['range']}},\
+start={{interfaces[intfc]['sess_start']}},\
+ipv6=1
+{% endfor %}
+{% if auth['mech'] == 'noauth' %}
+noauth=1
+{% endif %}
+{% if auth['mech'] == 'local' %}
+username=ifname
+password=csid
+{% endif %}
+
+{%- for intfc in interfaces %}
+{% if (interfaces[intfc]['shared'] == '0') and (interfaces[intfc]['vlan_mon']) %}
+vlan-mon={{intfc}},{{interfaces[intfc]['vlan_mon']|join(',')}}
+{% endif %}
+{% endfor %}
+
+{% if (dns['server1']) or (dns['server2']) %}
+[dns]
+{% if dns['server1'] %}
+dns1={{dns['server1']}}
+{% endif -%}
+{% if dns['server2'] %}
+dns2={{dns['server2']}}
+{% endif -%}
+{% endif -%}
+
+{% if (dnsv6['server1']) or (dnsv6['server2']) or (dnsv6['server3']) %}
+[dnsv6]
+dns={{dnsv6['server1']}}
+dns={{dnsv6['server2']}}
+dns={{dnsv6['server3']}}
+{% endif %}
+
+[ipv6-nd]
+verbose=1
+
+[ipv6-dhcp]
+verbose=1
+
+{% if ipv6['prfx'] %}
+[ipv6-pool]
+{% for prfx in ipv6['prfx'] %}
+{{prfx}}
+{% endfor %}
+{% for pd in ipv6['pd'] %}
+delegate={{pd}}
+{% endfor %}
+{% endif %}
+
+{% if auth['mech'] == 'local' %}
+[chap-secrets]
+chap-secrets=/etc/accel-ppp/ipoe/chap-secrets
+{% endif %}
+
+{% if auth['mech'] == 'radius' %}
+[radius]
+verbose=1
+{% for srv in auth['radius'] %}
+server={{srv}},{{auth['radius'][srv]['secret']}},\
+req-limit={{auth['radius'][srv]['req-limit']}},\
+fail-time={{auth['radius'][srv]['fail-time']}}
+{% endfor %}
+{% if auth['radsettings']['dae-server']['ip-address'] %}
+dae-server={{auth['radsettings']['dae-server']['ip-address']}}:\
+{{auth['radsettings']['dae-server']['port']}},\
+{{auth['radsettings']['dae-server']['secret']}}
+{% endif -%}
+{% if auth['radsettings']['acct-timeout'] %}
+acct-timeout={{auth['radsettings']['acct-timeout']}}
+{% endif -%}
+{% if auth['radsettings']['max-try'] %}
+max-try={{auth['radsettings']['max-try']}}
+{% endif -%}
+{% if auth['radsettings']['timeout'] %}
+timeout={{auth['radsettings']['timeout']}}
+{% endif -%}
+{% if auth['radsettings']['nas-ip-address'] %}
+nas-ip-address={{auth['radsettings']['nas-ip-address']}}
+{% endif -%}
+{% if auth['radsettings']['nas-identifier'] %}
+nas-identifier={{auth['radsettings']['nas-identifier']}}
+{% endif -%}
+{% endif %}
+
+[cli]
+tcp=127.0.0.1:2002
diff --git a/data/templates/ipsec/ipsec.conf.tmpl b/data/templates/ipsec/ipsec.conf.tmpl
new file mode 100644
index 000000000..d0b60765b
--- /dev/null
+++ b/data/templates/ipsec/ipsec.conf.tmpl
@@ -0,0 +1,3 @@
+{{delim_ipsec_l2tp_begin}}
+include {{ipsec_ra_conn_file}}
+{{delim_ipsec_l2tp_end}}
diff --git a/data/templates/ipsec/ipsec.secrets.tmpl b/data/templates/ipsec/ipsec.secrets.tmpl
new file mode 100644
index 000000000..55c010a3b
--- /dev/null
+++ b/data/templates/ipsec/ipsec.secrets.tmpl
@@ -0,0 +1,7 @@
+{{delim_ipsec_l2tp_begin}}
+{% if ipsec_l2tp_auth_mode == 'pre-shared-secret' %}
+{{outside_addr}} %any : PSK "{{ipsec_l2tp_secret}}"
+{% elif ipsec_l2tp_auth_mode == 'x509' %}
+: RSA {{server_key_file_copied}}
+{% endif%}
+{{delim_ipsec_l2tp_end}}
diff --git a/data/templates/ipsec/remote-access.tmpl b/data/templates/ipsec/remote-access.tmpl
new file mode 100644
index 000000000..fae48232f
--- /dev/null
+++ b/data/templates/ipsec/remote-access.tmpl
@@ -0,0 +1,28 @@
+{{delim_ipsec_l2tp_begin}}
+conn {{ra_conn_name}}
+ type=transport
+ left={{outside_addr}}
+ leftsubnet=%dynamic[/1701]
+ rightsubnet=%dynamic
+ mark_in=%unique
+ auto=add
+ ike=aes256-sha1-modp1024,3des-sha1-modp1024,3des-sha1-modp1024!
+ dpddelay=15
+ dpdtimeout=45
+ dpdaction=clear
+ esp=aes256-sha1,3des-sha1!
+ rekey=no
+{% if ipsec_l2tp_auth_mode == 'pre-shared-secret' %}
+ authby=secret
+ leftauth=psk
+ rightauth=psk
+{% elif ipsec_l2tp_auth_mode == 'x509' %}
+ authby=rsasig
+ leftrsasigkey=%cert
+ rightrsasigkey=%cert
+ rightca=%same
+ leftcert={{server_cert_file_copied}}
+{% endif %}
+ ikelifetime={{ipsec_l2tp_ike_lifetime}}
+ keylife={{ipsec_l2tp_lifetime}}
+{{delim_ipsec_l2tp_end}}
diff --git a/data/templates/l2tp/chap-secrets.tmpl b/data/templates/l2tp/chap-secrets.tmpl
new file mode 100644
index 000000000..323528b3c
--- /dev/null
+++ b/data/templates/l2tp/chap-secrets.tmpl
@@ -0,0 +1,11 @@
+# username server password acceptable local IP addresses shaper
+{% for user in authentication['local-users'] %}
+{% if authentication['local-users'][user]['state'] == 'enabled' %}
+{% if (authentication['local-users'][user]['upload']) and (authentication['local-users'][user]['download']) %}
+{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}\t\
+{{authentication['local-users'][user]['download']}}/{{authentication['local-users'][user]['upload']}}
+{% else %}
+{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}
+{% endif %}
+{% endif %}
+{% endfor %}
diff --git a/data/templates/l2tp/l2tp.config.tmpl b/data/templates/l2tp/l2tp.config.tmpl
new file mode 100644
index 000000000..b8637e256
--- /dev/null
+++ b/data/templates/l2tp/l2tp.config.tmpl
@@ -0,0 +1,173 @@
+### generated by accel_l2tp.py ###
+[modules]
+log_syslog
+l2tp
+chap-secrets
+{% for proto in authentication['auth_proto']: %}
+{{proto}}
+{% endfor%}
+{% if authentication['mode'] == 'radius' %}
+radius
+{% endif -%}
+ippool
+shaper
+ipv6pool
+ipv6_nd
+ipv6_dhcp
+
+[core]
+thread-count={{thread_cnt}}
+
+[log]
+syslog=accel-l2tp,daemon
+copy=1
+level=5
+
+{% if dns %}
+[dns]
+{% if dns[0] %}
+dns1={{dns[0]}}
+{% endif %}
+{% if dns[1] %}
+dns2={{dns[1]}}
+{% endif %}
+{% endif -%}
+
+{% if dnsv6 %}
+[ipv6-dns]
+{% for srv in dnsv6: %}
+{{srv}}
+{% endfor %}
+{% endif %}
+
+{% if wins %}
+[wins]
+{% if wins[0] %}
+wins1={{wins[0]}}
+{% endif %}
+{% if wins[1] %}
+wins2={{wins[1]}}
+{% endif %}
+{% endif -%}
+
+[l2tp]
+verbose=1
+ifname=l2tp%d
+ppp-max-mtu={{mtu}}
+mppe={{authentication['mppe']}}
+{% if outside_addr %}
+bind={{outside_addr}}
+{% endif %}
+{% if lns_shared_secret %}
+secret={{lns_shared_secret}}
+{% endif %}
+
+[client-ip-range]
+0.0.0.0/0
+
+{% if (client_ip_pool) or (client_ip_subnets) %}
+[ip-pool]
+{% if client_ip_pool %}
+{{client_ip_pool}}
+{% endif -%}
+{% if client_ip_subnets %}
+{% for sn in client_ip_subnets %}
+{{sn}}
+{% endfor -%}
+{% endif %}
+{% endif %}
+{% if gateway_address %}
+gw-ip-address={{gateway_address}}
+{% endif %}
+
+{% if authentication['mode'] == 'local' %}
+[chap-secrets]
+chap-secrets=/etc/accel-ppp/l2tp/chap-secrets
+{% if gateway_address %}
+gw-ip-address={{gateway_address}}
+{% endif %}
+{% endif %}
+
+[ppp]
+verbose=1
+check-ip=1
+single-session=replace
+{% if idle_timeout %}
+lcp-echo-timeout={{idle_timeout}}
+{% endif %}
+{% if ppp_options['lcp-echo-interval'] %}
+lcp-echo-interval={{ppp_options['lcp-echo-interval']}}
+{% else %}
+lcp-echo-interval=30
+{% endif %}
+{% if ppp_options['lcp-echo-failure'] %}
+lcp-echo-failure={{ppp_options['lcp-echo-failure']}}
+{% else %}
+lcp-echo-failure=3
+{% endif %}
+{% if ccp_disable %}
+ccp=0
+{% endif %}
+{% if client_ipv6_pool %}
+ipv6=allow
+{% endif %}
+
+{% if authentication['mode'] == 'radius' %}
+[radius]
+{% for rsrv in authentication['radiussrv']: %}
+server={{rsrv}},{{authentication['radiussrv'][rsrv]['secret']}},\
+req-limit={{authentication['radiussrv'][rsrv]['req-limit']}},\
+fail-time={{authentication['radiussrv'][rsrv]['fail-time']}}
+{% endfor %}
+{% if authentication['radiusopt']['timeout'] %}
+timeout={{authentication['radiusopt']['timeout']}}
+{% endif %}
+{% if authentication['radiusopt']['acct-timeout'] %}
+acct-timeout={{authentication['radiusopt']['acct-timeout']}}
+{% endif %}
+{% if authentication['radiusopt']['max-try'] %}
+max-try={{authentication['radiusopt']['max-try']}}
+{% endif %}
+{% if authentication['radiusopt']['nas-id'] %}
+nas-identifier={{authentication['radiusopt']['nas-id']}}
+{% endif %}
+{% if authentication['radius_source_address'] %}
+nas-ip-address={{authentication['radius_source_address']}}
+{% endif -%}
+{% if authentication['radiusopt']['dae-srv'] %}
+dae-server={{authentication['radiusopt']['dae-srv']['ip-addr']}}:\
+{{authentication['radiusopt']['dae-srv']['port']}},\
+{{authentication['radiusopt']['dae-srv']['secret']}}
+{% endif -%}
+gw-ip-address={{gateway_address}}
+verbose=1
+{% endif -%}
+
+{% if client_ipv6_pool %}
+[ipv6-pool]
+{% for prfx in client_ipv6_pool.prefix: %}
+{{prfx}}
+{% endfor %}
+{% for prfx in client_ipv6_pool.delegate_prefix: %}
+delegate={{prfx}}
+{% endfor %}
+{% endif %}
+
+{% if client_ipv6_pool['delegate_prefix'] %}
+[ipv6-dhcp]
+verbose=1
+{% endif %}
+
+{% if authentication['radiusopt']['shaper'] %}
+[shaper]
+verbose=1
+attr={{authentication['radiusopt']['shaper']['attr']}}
+{% if authentication['radiusopt']['shaper']['vendor'] %}
+vendor={{authentication['radiusopt']['shaper']['vendor']}}
+{% endif -%}
+{% endif %}
+
+[cli]
+tcp=127.0.0.1:2004
+sessions-columns=ifname,username,calling-sid,ip,{{ip6_column}}{{ip6_dp_column}}rate-limit,type,comp,state,rx-bytes,tx-bytes,uptime
+
diff --git a/data/templates/lldp/lldpd.tmpl b/data/templates/lldp/lldpd.tmpl
new file mode 100644
index 000000000..3db955b48
--- /dev/null
+++ b/data/templates/lldp/lldpd.tmpl
@@ -0,0 +1,3 @@
+### Autogenerated by lldp.py ###
+DAEMON_ARGS="-M 4{% if options.snmp %} -x{% endif %}{% if options.cdp %} -c{% endif %}{% if options.edp %} -e{% endif %}{% if options.fdp %} -f{% endif %}{% if options.sonmp %} -s{% endif %}"
+
diff --git a/data/templates/lldp/vyos.conf.tmpl b/data/templates/lldp/vyos.conf.tmpl
new file mode 100644
index 000000000..e724f42c6
--- /dev/null
+++ b/data/templates/lldp/vyos.conf.tmpl
@@ -0,0 +1,20 @@
+### Autogenerated by lldp.py ###
+
+configure system platform VyOS
+configure system description "VyOS {{ options.description }}"
+{% if options.listen_on -%}
+configure system interface pattern "{{ ( options.listen_on | select('equalto','all') | map('replace','all','*') | list + options.listen_on | select('equalto','!all') | map('replace','!all','!*') | list + options.listen_on | reject('equalto','all') | reject('equalto','!all') | list ) | unique | join(",") }}"
+{%- endif %}
+{% if options.mgmt_addr -%}
+configure system ip management pattern {{ options.mgmt_addr | join(",") }}
+{%- endif %}
+{%- for loc in location -%}
+{%- if loc.elin %}
+configure ports {{ loc.name }} med location elin "{{ loc.elin }}"
+{%- endif %}
+{%- if loc.coordinate_based %}
+configure ports {{ loc.name }} med location coordinate {% if loc.coordinate_based.latitude %}latitude {{ loc.coordinate_based.latitude }}{% endif %} {% if loc.coordinate_based.longitude %}longitude {{ loc.coordinate_based.longitude }}{% endif %} {% if loc.coordinate_based.altitude %}altitude {{ loc.coordinate_based.altitude }} m{% endif %} {% if loc.coordinate_based.datum %}datum {{ loc.coordinate_based.datum }}{% endif %}
+{%- endif %}
+
+
+{% endfor %}
diff --git a/data/templates/mdns-repeater/mdns-repeater.tmpl b/data/templates/mdns-repeater/mdns-repeater.tmpl
new file mode 100644
index 000000000..3fc4db67e
--- /dev/null
+++ b/data/templates/mdns-repeater/mdns-repeater.tmpl
@@ -0,0 +1,2 @@
+### Autogenerated by mdns_repeater.py ###
+DAEMON_ARGS="{{ interfaces | join(' ') }}"
diff --git a/data/templates/mpls/ldpd.frr.tmpl b/data/templates/mpls/ldpd.frr.tmpl
new file mode 100644
index 000000000..bbff88ae5
--- /dev/null
+++ b/data/templates/mpls/ldpd.frr.tmpl
@@ -0,0 +1,58 @@
+!
+{% if mpls_ldp -%}
+mpls ldp
+{% if old_router_id -%}
+no router-id {{ old_router_id }}
+{% endif -%}
+{% if router_id -%}
+router-id {{ router_id }}
+{% endif -%}
+{% for neighbor_id in old_ldp.neighbors -%}
+no neighbor {{neighbor_id}} password {{old_ldp.neighbors[neighbor_id].password}}
+{% endfor -%}
+{% for neighbor_id in ldp.neighbors -%}
+neighbor {{neighbor_id}} password {{ldp.neighbors[neighbor_id].password}}
+{% endfor -%}
+address-family ipv4
+label local allocate host-routes
+{% if old_ldp.d_transp_ipv4 -%}
+no discovery transport-address {{ old_ldp.d_transp_ipv4 }}
+{% endif -%}
+{% if ldp.d_transp_ipv4 -%}
+discovery transport-address {{ ldp.d_transp_ipv4 }}
+{% endif -%}
+{% for interface in old_ldp.interfaces -%}
+no interface {{interface}}
+{% endfor -%}
+{% for interface in ldp.interfaces -%}
+interface {{interface}}
+{% endfor -%}
+!
+!
+exit-address-family
+!
+{% if ldp.d_transp_ipv6 -%}
+address-family ipv6
+label local allocate host-routes
+{% if old_ldp.d_transp_ipv6 -%}
+no discovery transport-address {{ old_ldp.d_transp_ipv6 }}
+{% endif -%}
+{% if ldp.d_transp_ipv6 -%}
+discovery transport-address {{ ldp.d_transp_ipv6 }}
+{% endif -%}
+{% for interface in old_ldp.interfaces -%}
+no interface {{interface}}
+{% endfor -%}
+{% for interface in ldp.interfaces -%}
+interface {{interface}}
+{% endfor -%}
+!
+exit-address-family
+{% else -%}
+no address-family ipv6
+{% endif -%}
+!
+{% else -%}
+no mpls ldp
+{% endif -%}
+!
diff --git a/data/templates/netflow/uacctd.conf.tmpl b/data/templates/netflow/uacctd.conf.tmpl
new file mode 100644
index 000000000..d8615566f
--- /dev/null
+++ b/data/templates/netflow/uacctd.conf.tmpl
@@ -0,0 +1,69 @@
+# Genereated from VyOS configuration
+daemonize: true
+promisc: false
+pidfile: /var/run/uacctd.pid
+uacctd_group: 2
+uacctd_nl_size: 2097152
+snaplen: {{ snaplen }}
+aggregate: in_iface,src_mac,dst_mac,vlan,src_host,dst_host,src_port,dst_port,proto,tos,flows
+plugin_pipe_size: {{ templatecfg['plugin_pipe_size'] }}
+plugin_buffer_size: {{ templatecfg['plugin_buffer_size'] }}
+{%- if templatecfg['syslog-facility'] != none %}
+syslog: {{ templatecfg['syslog-facility'] }}
+{%- endif %}
+{%- if templatecfg['disable-imt'] == none %}
+imt_path: /tmp/uacctd.pipe
+imt_mem_pools_number: 169
+{%- endif %}
+plugins:
+{%- if templatecfg['netflow']['servers'] != none -%}
+ {% for server in templatecfg['netflow']['servers'] %}
+ {%- if loop.last -%}nfprobe[nf_{{ server['address'] }}]{%- else %}nfprobe[nf_{{ server['address'] }}],{%- endif %}
+ {%- endfor -%}
+ {% set plugins_presented = true %}
+{%- endif %}
+{%- if templatecfg['sflow']['servers'] != none -%}
+ {% if plugins_presented -%}
+ {%- for server in templatecfg['sflow']['servers'] -%}
+ ,sfprobe[sf_{{ server['address'] }}]
+ {%- endfor %}
+ {%- else %}
+ {%- for server in templatecfg['sflow']['servers'] %}
+ {%- if loop.last -%}sfprobe[sf_{{ server['address'] }}]{%- else %}sfprobe[sf_{{ server['address'] }}],{%- endif %}
+ {%- endfor %}
+ {%- endif -%}
+ {% set plugins_presented = true %}
+{%- endif %}
+{%- if templatecfg['disable-imt'] == none %}
+ {%- if plugins_presented -%},memory{%- else %}memory{%- endif %}
+{%- endif %}
+{%- if templatecfg['netflow']['servers'] != none %}
+{%- for server in templatecfg['netflow']['servers'] %}
+nfprobe_receiver[nf_{{ server['address'] }}]: {{ server['address'] }}:{{ server['port'] }}
+nfprobe_version[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['version'] }}
+{%- if templatecfg['netflow']['engine-id'] != none %}
+nfprobe_engine[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['engine-id'] }}
+{%- endif %}
+{%- if templatecfg['netflow']['max-flows'] != none %}
+nfprobe_maxflows[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['max-flows'] }}
+{%- endif %}
+{%- if templatecfg['netflow']['sampling-rate'] != none %}
+sampling_rate[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['sampling-rate'] }}
+{%- endif %}
+{%- if templatecfg['netflow']['source-ip'] != none %}
+nfprobe_source_ip[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['source-ip'] }}
+{%- endif %}
+{%- if templatecfg['netflow']['timeout_string'] != '' %}
+nfprobe_timeouts[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['timeout_string'] }}
+{%- endif %}
+{%- endfor %}
+{%- endif %}
+{%- if templatecfg['sflow']['servers'] != none %}
+{%- for server in templatecfg['sflow']['servers'] %}
+sfprobe_receiver[sf_{{ server['address'] }}]: {{ server['address'] }}:{{ server['port'] }}
+sfprobe_agentip[sf_{{ server['address'] }}]: {{ templatecfg['sflow']['agent-address'] }}
+{%- if templatecfg['sflow']['sampling-rate'] != none %}
+sampling_rate[sf_{{ server['address'] }}]: {{ templatecfg['sflow']['sampling-rate'] }}
+{%- endif %}
+{%- endfor %}
+{% endif %}
diff --git a/data/templates/ntp/ntp.conf.tmpl b/data/templates/ntp/ntp.conf.tmpl
new file mode 100644
index 000000000..52042d218
--- /dev/null
+++ b/data/templates/ntp/ntp.conf.tmpl
@@ -0,0 +1,38 @@
+### Autogenerated by ntp.py ###
+
+#
+# Non-configurable defaults
+#
+driftfile /var/lib/ntp/ntp.drift
+# By default, only allow ntpd to query time sources, ignore any incoming requests
+restrict default noquery nopeer notrap nomodify
+# Local users have unrestricted access, allowing reconfiguration via ntpdc
+restrict 127.0.0.1
+restrict -6 ::1
+
+#
+# Configurable section
+#
+
+{% if servers -%}
+{% for s in servers -%}
+# Server configuration for: {{ s.name }}
+server {{ s.name }} iburst {{ s.options | join(" ") }}
+{% endfor -%}
+{% endif %}
+
+{% if allowed_networks -%}
+{% for n in allowed_networks -%}
+# Client configuration for network: {{ n.network }}
+restrict {{ n.address }} mask {{ n.netmask }} nomodify notrap nopeer
+
+{% endfor -%}
+{% endif %}
+
+{% if listen_address -%}
+# NTP should listen on configured addresses only
+interface ignore wildcard
+{% for a in listen_address -%}
+interface listen {{ a }}
+{% endfor -%}
+{% endif %}
diff --git a/data/templates/openvpn/client.conf.tmpl b/data/templates/openvpn/client.conf.tmpl
new file mode 100644
index 000000000..3099f2ca7
--- /dev/null
+++ b/data/templates/openvpn/client.conf.tmpl
@@ -0,0 +1,16 @@
+### Autogenerated by interfaces-openvpn.py ###
+
+{% if ip -%}
+ifconfig-push {{ ip }} {{ remote_netmask }}
+{% endif -%}
+{% for route in push_route -%}
+push "route {{ route }}"
+{% endfor -%}
+
+{% for net in subnet -%}
+iroute {{ net }}
+{% endfor -%}
+
+{% if disable -%}
+disable
+{% endif -%}
diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl
new file mode 100644
index 000000000..e7715dfb5
--- /dev/null
+++ b/data/templates/openvpn/server.conf.tmpl
@@ -0,0 +1,223 @@
+### Autogenerated by interfaces-openvpn.py ###
+#
+# See https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
+# for individual keyword definition
+
+{% if description %}
+# {{ description }}
+{% endif %}
+
+verb 3
+status /opt/vyatta/etc/openvpn/status/{{ intf }}.status 30
+writepid /var/run/openvpn/{{ intf }}.pid
+
+dev-type {{ type }}
+dev {{ intf }}
+user {{ uid }}
+group {{ gid }}
+persist-key
+iproute /usr/libexec/vyos/system/unpriv-ip
+
+proto {% if 'tcp-active' in protocol -%}tcp-client{% elif 'tcp-passive' in protocol -%}tcp-server{% else %}udp{% endif %}
+
+{%- if local_host %}
+local {{ local_host }}
+{% endif %}
+
+{%- if mode == 'server' and protocol == 'udp' and not local_host %}
+multihome
+{% endif %}
+
+{%- if local_port %}
+lport {{ local_port }}
+{% endif %}
+
+{%- if remote_port %}
+rport {{ remote_port }}
+{% endif %}
+
+{%- if remote_host %}
+{% for remote in remote_host -%}
+remote {{ remote }}
+{% endfor -%}
+{% endif %}
+
+{%- if shared_secret_file %}
+secret {{ shared_secret_file }}
+{% endif %}
+
+{%- if persistent_tunnel %}
+persist-tun
+{% endif %}
+
+{%- if mode %}
+{%- if 'client' in mode %}
+#
+# OpenVPN Client mode
+#
+client
+nobind
+{%- elif 'server' in mode %}
+#
+# OpenVPN Server mode
+#
+mode server
+tls-server
+keepalive {{ ping_interval }} {{ ping_restart }}
+management /tmp/openvpn-mgmt-intf unix
+
+{%- if server_topology %}
+topology {% if 'point-to-point' in server_topology %}p2p{% else %}subnet{% endif %}
+{% endif %}
+
+{% for ns in server_dns_nameserver -%}
+push "dhcp-option DNS {{ ns }}"
+{% endfor -%}
+
+{% for route in server_push_route -%}
+push "route {{ route }}"
+{% endfor -%}
+
+{%- if server_domain %}
+push "dhcp-option DOMAIN {{ server_domain }}"
+{% endif %}
+
+{%- if server_max_conn %}
+max-clients {{ server_max_conn }}
+{% endif %}
+
+{%- if bridge_member %}
+server-bridge nogw
+{%- else %}
+server {{ server_subnet }}
+{% endif %}
+
+{%- if server_reject_unconfigured %}
+ccd-exclusive
+{% endif %}
+
+{%- else %}
+#
+# OpenVPN site-2-site mode
+#
+ping {{ ping_interval }}
+ping-restart {{ ping_restart }}
+
+{%- if local_address_subnet %}
+ifconfig {{ local_address }} {{ local_address_subnet }}
+{% elif remote_address %}
+ifconfig {{ local_address }} {{ remote_address }}
+{% endif %}
+
+{% endif %}
+{% endif %}
+
+{%- if tls_ca_cert %}
+ca {{ tls_ca_cert }}
+{% endif %}
+
+{%- if tls_cert %}
+cert {{ tls_cert }}
+{% endif %}
+
+{%- if tls_key %}
+key {{ tls_key }}
+{% endif %}
+
+{%- if tls_crypt %}
+tls-crypt {{ tls_crypt }}
+{% endif %}
+
+{%- if tls_crl %}
+crl-verify {{ tls_crl }}
+{% endif %}
+
+{%- if tls_version_min %}
+tls-version-min {{tls_version_min}}
+{% endif %}
+
+{%- if tls_dh %}
+dh {{ tls_dh }}
+{% endif %}
+
+{%- if tls_auth %}
+tls-auth {{tls_auth}}
+{% endif %}
+
+{%- if 'active' in tls_role %}
+tls-client
+{%- elif 'passive' in tls_role %}
+tls-server
+{% endif %}
+
+{%- if redirect_gateway %}
+push "redirect-gateway {{ redirect_gateway }}"
+{% endif %}
+
+{%- if compress_lzo %}
+compress lzo
+{% endif %}
+
+{%- if hash %}
+auth {{ hash }}
+{% endif %}
+
+{%- if encryption %}
+{%- if 'des' in encryption %}
+cipher des-cbc
+{%- elif '3des' in encryption %}
+cipher des-ede3-cbc
+{%- elif 'bf128' in encryption %}
+cipher bf-cbc
+keysize 128
+{%- elif 'bf256' in encryption %}
+cipher bf-cbc
+keysize 25
+{%- elif 'aes128gcm' in encryption %}
+cipher aes-128-gcm
+{%- elif 'aes128' in encryption %}
+cipher aes-128-cbc
+{%- elif 'aes192gcm' in encryption %}
+cipher aes-192-gcm
+{%- elif 'aes192' in encryption %}
+cipher aes-192-cbc
+{%- elif 'aes256gcm' in encryption %}
+cipher aes-256-gcm
+{%- elif 'aes256' in encryption %}
+cipher aes-256-cbc
+{% endif %}
+{% endif %}
+
+{%- if ncp_ciphers %}
+ncp-ciphers {{ncp_ciphers}}
+{% endif %}
+{%- if disable_ncp %}
+ncp-disable
+{% endif %}
+
+{%- if auth %}
+auth-user-pass /tmp/openvpn-{{ intf }}-pw
+auth-retry nointeract
+{% endif %}
+
+{%- if client %}
+client-config-dir /opt/vyatta/etc/openvpn/ccd/{{ intf }}
+{% endif %}
+
+# DEPRECATED This option will be removed in OpenVPN 2.5
+# Until OpenVPN v2.3 the format of the X.509 Subject fields was formatted like this:
+# /C=US/L=Somewhere/CN=John Doe/emailAddress=john@example.com In addition the old
+# behaviour was to remap any character other than alphanumeric, underscore ('_'),
+# dash ('-'), dot ('.'), and slash ('/') to underscore ('_'). The X.509 Subject
+# string as returned by the tls_id environmental variable, could additionally
+# contain colon (':') or equal ('='). When using the --compat-names option, this
+# old formatting and remapping will be re-enabled again. This is purely implemented
+# for compatibility reasons when using older plug-ins or scripts which does not
+# handle the new formatting or UTF-8 characters.
+#
+# See https://phabricator.vyos.net/T1512
+compat-names
+
+{% for option in options -%}
+{{ option }}
+{% endfor -%}
diff --git a/data/templates/pim/pimd.frr.tmpl b/data/templates/pim/pimd.frr.tmpl
new file mode 100644
index 000000000..1d1532c60
--- /dev/null
+++ b/data/templates/pim/pimd.frr.tmpl
@@ -0,0 +1,34 @@
+!
+{% for rp_addr in old_pim.rp -%}
+{% for group in old_pim.rp[rp_addr] -%}
+no ip pim rp {{ rp_addr }} {{ group }}
+{% endfor -%}
+{% endfor -%}
+{% if old_pim.rp_keep_alive -%}
+no ip pim rp keep-alive-timer {{ old_pim.rp_keep_alive }}
+{% endif -%}
+{% for iface in old_pim.ifaces -%}
+interface {{ iface }}
+no ip pim
+!
+{% endfor -%}
+{% for iface in pim.ifaces -%}
+interface {{ iface }}
+ip pim
+{% if pim.ifaces[iface].dr_prio -%}
+ip pim drpriority {{ pim.ifaces[iface].dr_prio }}
+{% endif -%}
+{% if pim.ifaces[iface].hello -%}
+ip pim hello {{ pim.ifaces[iface].hello }}
+{% endif -%}
+!
+{% endfor -%}
+{% for rp_addr in pim.rp -%}
+{% for group in pim.rp[rp_addr] -%}
+ip pim rp {{ rp_addr }} {{ group }}
+{% endfor -%}
+{% endfor -%}
+{% if pim.rp_keep_alive -%}
+ip pim rp keep-alive-timer {{ pim.rp_keep_alive }}
+{% endif -%}
+!
diff --git a/data/templates/pppoe-server/chap-secrets.tmpl b/data/templates/pppoe-server/chap-secrets.tmpl
new file mode 100644
index 000000000..808debccb
--- /dev/null
+++ b/data/templates/pppoe-server/chap-secrets.tmpl
@@ -0,0 +1,11 @@
+# username server password acceptable local IP addresses shaper
+{% for user in authentication['local-users'] %}
+{% if authentication['local-users'][user]['state'] == 'enabled' %}
+{% if (authentication['local-users'][user]['upload']) and (authentication['local-users'][user]['download']) %}
+{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}\t\
+{{authentication['local-users'][user]['download']}}/{{authentication['local-users'][user]['upload']}}
+{% else %}
+{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}
+{% endif %}
+{% endif %}
+{% endfor %}
diff --git a/data/templates/pppoe-server/pppoe.config.tmpl b/data/templates/pppoe-server/pppoe.config.tmpl
new file mode 100644
index 000000000..d44c0aa93
--- /dev/null
+++ b/data/templates/pppoe-server/pppoe.config.tmpl
@@ -0,0 +1,228 @@
+
+### generated by accel_pppoe.py ###
+[modules]
+log_syslog
+pppoe
+{% if authentication['mode'] == 'radius' %}
+radius
+{% endif %}
+ippool
+{% if ppp_options['ipv6'] != 'deny' %}
+ipv6pool
+ipv6_nd
+ipv6_dhcp
+{% endif %}
+chap-secrets
+auth_pap
+auth_chap_md5
+auth_mschap_v1
+auth_mschap_v2
+#pppd_compat
+shaper
+{% if snmp == 'enable' or snmp == 'enable-ma' %}
+net-snmp
+{% endif %}
+{% if limits %}
+connlimit
+{% endif %}
+
+[core]
+thread-count={{thread_cnt}}
+
+[log]
+syslog=accel-pppoe,daemon
+copy=1
+level=5
+
+{% if snmp == 'enable-ma' %}
+[snmp]
+master=1
+{% endif -%}
+
+[client-ip-range]
+disable
+
+{% if ppp_gw %}
+[ip-pool]
+gw-ip-address={{ppp_gw}}
+{% if client_ip_pool %}
+{{client_ip_pool}}
+{% endif -%}
+
+{% if client_ip_subnets %}
+{% for sn in client_ip_subnets %}
+{{sn}}
+{% endfor %}
+{% endif %}
+{% endif -%}
+
+{% if client_ipv6_pool %}
+[ipv6-pool]
+{% for prfx in client_ipv6_pool['prefix']: %}
+{{prfx}}
+{% endfor %}
+{% for prfx in client_ipv6_pool['delegate-prefix']: %}
+delegate={{prfx}}
+{% endfor %}
+{% endif %}
+
+{% if dns %}
+[dns]
+{% if dns[0] %}
+dns1={{dns[0]}}
+{% endif -%}
+{% if dns[1] %}
+dns2={{dns[1]}}
+{% endif -%}
+{% endif %}
+
+{% if dnsv6 %}
+[ipv6-dns]
+{% for srv in dnsv6: %}
+{{srv}}
+{% endfor %}
+{% endif %}
+
+{% if wins %}
+[wins]
+{% if wins[0] %}
+wins1={{wins[0]}}
+{% endif %}
+{% if wins[1] %}
+wins2={{wins[1]}}
+{% endif -%}
+{% endif -%}
+
+{% if authentication['mode'] == 'local' %}
+[chap-secrets]
+chap-secrets=/etc/accel-ppp/pppoe/chap-secrets
+{% endif -%}
+
+{% if authentication['mode'] == 'radius' %}
+[radius]
+{% for rsrv in authentication['radiussrv']: %}
+server={{rsrv}},{{authentication['radiussrv'][rsrv]['secret']}},\
+req-limit={{authentication['radiussrv'][rsrv]['req-limit']}},\
+fail-time={{authentication['radiussrv'][rsrv]['fail-time']}}
+{% endfor %}
+{% if authentication['radiusopt']['timeout'] %}
+timeout={{authentication['radiusopt']['timeout']}}
+{% endif %}
+{% if authentication['radiusopt']['acct-timeout'] %}
+acct-timeout={{authentication['radiusopt']['acct-timeout']}}
+{% endif %}
+{% if authentication['radiusopt']['max-try'] %}
+max-try={{authentication['radiusopt']['max-try']}}
+{% endif %}
+{% if authentication['radiusopt']['nas-id'] %}
+nas-identifier={{authentication['radiusopt']['nas-id']}}
+{% endif %}
+{% if authentication['radiusopt']['nas-ip'] %}
+nas-ip-address={{authentication['radiusopt']['nas-ip']}}
+{% endif -%}
+{% if authentication['radiusopt']['dae-srv'] %}
+dae-server={{authentication['radiusopt']['dae-srv']['ip-addr']}}:\
+{{authentication['radiusopt']['dae-srv']['port']}},\
+{{authentication['radiusopt']['dae-srv']['secret']}}
+{% endif -%}
+gw-ip-address={{ppp_gw}}
+verbose=1
+
+{% if authentication['radiusopt']['shaper'] %}
+[shaper]
+verbose=1
+attr={{authentication['radiusopt']['shaper']['attr']}}
+{% if authentication['radiusopt']['shaper']['vendor'] %}
+vendor={{authentication['radiusopt']['shaper']['vendor']}}
+{% endif -%}
+{% endif -%}
+{% endif %}
+
+[ppp]
+verbose=1
+check-ip=1
+{% if not sesscrtl == 'disable' %}
+single-session={{sesscrtl}}
+{% endif -%}
+{% if ppp_options['ccp'] %}
+ccp=1
+{% endif %}
+{% if ppp_options['min-mtu'] %}
+min-mtu={{ppp_options['min-mtu']}}
+{% else %}
+min-mtu={{mtu}}
+{% endif %}
+{% if ppp_options['mru'] %}
+mru={{ppp_options['mru']}}
+{% endif %}
+{% if ppp_options['mppe'] %}
+mppe={{ppp_options['mppe']}}
+{% else %}
+mppe=prefer
+{% endif %}
+{% if ppp_options['lcp-echo-interval'] %}
+lcp-echo-interval={{ppp_options['lcp-echo-interval']}}
+{% else %}
+lcp-echo-interval=30
+{% endif %}
+{% if ppp_options['lcp-echo-timeout'] %}
+lcp-echo-timeout={{ppp_options['lcp-echo-timeout']}}
+{% endif %}
+{% if ppp_options['lcp-echo-failure'] %}
+lcp-echo-failure={{ppp_options['lcp-echo-failure']}}
+{% else %}
+lcp-echo-failure=3
+{% endif %}
+{% if ppp_options['ipv4'] %}
+ipv4={{ppp_options['ipv4']}}
+{% endif %}
+{% if client_ipv6_pool %}
+ipv6=allow
+{% endif %}
+
+{% if ppp_options['ipv6'] %}
+ipv6={{ppp_options['ipv6']}}
+{% if ppp_options['ipv6-intf-id'] %}
+ipv6-intf-id={{ppp_options['ipv6-intf-id']}}
+{% endif %}
+{% if ppp_options['ipv6-peer-intf-id'] %}
+ipv6-peer-intf-id={{ppp_options['ipv6-peer-intf-id']}}
+{% endif %}
+{% if ppp_options['ipv6-accept-peer-intf-id'] %}
+ipv6-accept-peer-intf-id={{ppp_options['ipv6-accept-peer-intf-id']}}
+{% endif %}
+{% endif %}
+mtu={{mtu}}
+
+[pppoe]
+verbose=1
+{% if concentrator %}
+ac-name={{concentrator}}
+{% endif %}
+{% if interface %}
+{% for int in interface %}
+interface={{int}}
+{% if interface[int]['vlans'] %}
+vlan-mon={{int}},{{interface[int]['vlans']|join(',')}}
+interface=re:{{int}}\.\d+
+{% endif %}
+{% endfor -%}
+{% endif -%}
+
+{% if svc_name %}
+service-name={{svc_name|join(',')}}
+{% endif -%}
+
+{% if pado_delay %}
+pado-delay={{pado_delay}}
+{% endif %}
+
+{% if limits %}
+[connlimit]
+limit={{limits['conn-limit']}}
+burst={{limits['burst']}}
+timeout={{limits['timeout']}}
+{% endif %}
+
+[cli]
+tcp=127.0.0.1:2001
diff --git a/data/templates/pppoe/ip-down.script.tmpl b/data/templates/pppoe/ip-down.script.tmpl
new file mode 100644
index 000000000..e76875f12
--- /dev/null
+++ b/data/templates/pppoe/ip-down.script.tmpl
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# As PPPoE is an "on demand" interface we need to re-configure it when it
+# becomes up
+if [ "$6" != "{{ intf }}" ]; then
+ exit
+fi
+
+# add some info to syslog
+DIALER_PID=$(cat /var/run/{{ intf }}.pid)
+logger -t pppd[$DIALER_PID] "executing $0"
+
+# Determine if we are enslaved to a VRF, this is needed to properly insert
+# the default route
+VRF_NAME=""
+if [ -d /sys/class/net/{{ intf }}/upper_* ]; then
+ # Determine upper (VRF) interface
+ VRF=$(basename $(ls -d /sys/class/net/{{ intf }}/upper_*))
+ # Remove upper_ prefix from result string
+ VRF=${VRF#"upper_"}
+ # Populate variable to run in VR context
+ VRF_NAME="vrf ${VRF_NAME}"
+fi
+
+# Always delete default route when interface goes down
+vtysh -c "conf t" ${VRF_NAME} -c "no ip route 0.0.0.0/0 {{ intf }} ${VRF_NAME}"
diff --git a/data/templates/pppoe/ip-pre-up.script.tmpl b/data/templates/pppoe/ip-pre-up.script.tmpl
new file mode 100644
index 000000000..9a7ba7d57
--- /dev/null
+++ b/data/templates/pppoe/ip-pre-up.script.tmpl
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# As PPPoE is an "on demand" interface we need to re-configure it when it
+# becomes up
+
+if [ "$6" != "{{ intf }}" ]; then
+ exit
+fi
+
+# add some info to syslog
+DIALER_PID=$(cat /var/run/{{ intf }}.pid)
+logger -t pppd[$DIALER_PID] "executing $0"
+
+echo "{{ description }}" > /sys/class/net/{{ intf }}/ifalias
+
+{% if vrf -%}
+logger -t pppd[$DIALER_PID] "configuring dialer interface $6 for VRF {{ vrf }}"
+ip link set dev {{ intf }} master {{ vrf }}
+{% endif %}
diff --git a/data/templates/pppoe/ip-up.script.tmpl b/data/templates/pppoe/ip-up.script.tmpl
new file mode 100644
index 000000000..4cc779914
--- /dev/null
+++ b/data/templates/pppoe/ip-up.script.tmpl
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+# As PPPoE is an "on demand" interface we need to re-configure it when it
+# becomes up
+if [ "$6" != "{{ intf }}" ]; then
+ exit
+fi
+
+set -x
+
+# add some info to syslog
+DIALER_PID=$(cat /var/run/{{ intf }}.pid)
+logger -t pppd[$DIALER_PID] "executing $0"
+
+SED_OPT="ip route"
+VRF_NAME=""
+if [ -d /sys/class/net/{{ intf }}/upper_* ]; then
+ # Determine upper (VRF) interface
+ VRF=$(basename $(ls -d /sys/class/net/{{ intf }}/upper_*))
+ # Remove upper_ prefix from result string
+ VRF=${VRF#"upper_"}
+ # generate new SED command
+ SED_OPT="vrf ${VRF}"
+ # generate vtysh option
+ VRF_NAME="vrf ${VRF}"
+fi
+
+# Debian PPP version has no support for replacing an existing default route
+# thus we emulate this ba an ip-up script https://phabricator.vyos.net/T2220.
+{% if 'auto' in default_route -%}
+# only insert a new default route if there is no default route configured
+routes=$(vtysh -c "show running-config" | sed -n "/${SED_OPT}/,/!/p" | grep 0.0.0.0/0 | wc -l)
+if [ "$routes" -ne 0 ]; then
+ exit 1
+fi
+
+{% elif 'force' in default_route -%}
+# Retrieve current static default routes and remove it from the routing table
+vtysh -c "show running-config" | sed -n "/${SED_OPT}/,/!/p" | grep 0.0.0.0/0 | while read route ; do
+ vtysh -c "conf t" ${VTY_OPT} -c "no ${route} ${VRF_NAME}"
+done
+{% endif %}
+
+# Add default route to default or VRF routing table
+vtysh -c "conf t" ${VTY_OPT} -c "ip route 0.0.0.0/0 {{ intf }} ${VRF_NAME}"
+logger -t pppd[$DIALER_PID] "added default route via {{ intf }} ${VRF_NAME}"
+
diff --git a/data/templates/pppoe/ipv6-up.script.tmpl b/data/templates/pppoe/ipv6-up.script.tmpl
new file mode 100644
index 000000000..a4b08ddaf
--- /dev/null
+++ b/data/templates/pppoe/ipv6-up.script.tmpl
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# As PPPoE is an "on demand" interface we need to re-configure it when it
+# becomes up
+
+if [ "$6" != "{{ intf }}" ]; then
+ exit
+fi
+
+{% if ipv6_autoconf -%}
+# add some info to syslog
+DIALER_PID=$(cat /var/run/{{ intf }}.pid)
+logger -t pppd[$DIALER_PID] "executing $0"
+logger -t pppd[$DIALER_PID] "configuring interface {{ intf }} via {{ source_interface }}"
+
+# Configure interface-specific Host/Router behaviour.
+# Note: It is recommended to have the same setting on all interfaces; mixed
+# router/host scenarios are rather uncommon. Possible values are:
+#
+# 0 Forwarding disabled
+# 1 Forwarding enabled
+#
+echo 1 > /proc/sys/net/ipv6/conf/{{ intf }}/forwarding
+
+# Accept Router Advertisements; autoconfigure using them.
+#
+# It also determines whether or not to transmit Router
+# Solicitations. If and only if the functional setting is to
+# accept Router Advertisements, Router Solicitations will be
+# transmitted. Possible values are:
+#
+# 0 Do not accept Router Advertisements.
+# 1 Accept Router Advertisements if forwarding is disabled.
+# 2 Overrule forwarding behaviour. Accept Router Advertisements
+# even if forwarding is enabled.
+#
+echo 2 > /proc/sys/net/ipv6/conf/{{ intf }}/accept_ra
+
+# Autoconfigure addresses using Prefix Information in Router Advertisements.
+echo 1 > /proc/sys/net/ipv6/conf/{{ intf }}/autoconfigure
+{% endif %}
diff --git a/data/templates/pppoe/peer.tmpl b/data/templates/pppoe/peer.tmpl
new file mode 100644
index 000000000..8651f12a5
--- /dev/null
+++ b/data/templates/pppoe/peer.tmpl
@@ -0,0 +1,63 @@
+### Autogenerated by interfaces-pppoe.py ###
+
+{% if description %}
+# {{ description }}
+{% endif %}
+
+# Require peer to provide the local IP address if it is not
+# specified explicitly in the config file.
+noipdefault
+
+# Don't show the password in logfiles:
+hide-password
+
+# Standard Link Control Protocol (LCP) parameters:
+lcp-echo-interval 20
+lcp-echo-failure 3
+
+# RFC 2516, paragraph 7 mandates that the following options MUST NOT be
+# requested and MUST be rejected if requested by the peer:
+# Address-and-Control-Field-Compression (ACFC)
+noaccomp
+
+# Asynchronous-Control-Character-Map (ACCM)
+default-asyncmap
+
+# Override any connect script that may have been set in /etc/ppp/options.
+connect /bin/true
+
+# Don't try to authenticate the remote node
+noauth
+
+# Don't try to proxy ARP for the remote endpoint. User can set proxy
+# arp entries up manually if they wish. More importantly, having
+# the "proxyarp" parameter set disables the "defaultroute" option.
+noproxyarp
+
+# Unlimited connection attempts
+maxfail 0
+
+plugin rp-pppoe.so
+{{ source_interface }}
+persist
+ifname {{ intf }}
+ipparam {{ intf }}
+debug
+logfile {{ logfile }}
+mtu {{ mtu }}
+mru {{ mtu }}
+user "{{ auth_username }}"
+password "{{ auth_password }}"
+{% if name_server -%}
+usepeerdns
+{% endif %}
+{% if ipv6_enable -%}
++ipv6
+ipv6cp-use-ipaddr
+{% endif %}
+{% if service_name -%}
+rp_pppoe_service "{{ service_name }}"
+{% endif %}
+{% if on_demand %}
+demand
+{% endif %}
diff --git a/data/templates/pptp/chap-secrets.tmpl b/data/templates/pptp/chap-secrets.tmpl
new file mode 100644
index 000000000..6bfa2d64e
--- /dev/null
+++ b/data/templates/pptp/chap-secrets.tmpl
@@ -0,0 +1,6 @@
+# username server password acceptable local IP addresses
+{% for user in authentication['local-users'] %}
+{% if authentication['local-users'][user]['state'] == 'enabled' %}
+{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}
+{% endif %}
+{% endfor %}
diff --git a/data/templates/pptp/pptp.config.tmpl b/data/templates/pptp/pptp.config.tmpl
new file mode 100644
index 000000000..2596507af
--- /dev/null
+++ b/data/templates/pptp/pptp.config.tmpl
@@ -0,0 +1,87 @@
+
+### generated by accel_pptp.py ###
+[modules]
+log_syslog
+pptp
+ippool
+chap-secrets
+{% if authentication['auth_proto'] %}
+{{ authentication['auth_proto'] }}
+{% else %}
+auth_mschap_v2
+{% endif %}
+{% if authentication['mode'] == 'radius' %}
+radius
+{% endif -%}
+
+[core]
+thread-count={{thread_cnt}}
+
+[log]
+syslog=accel-pptp,daemon
+copy=1
+level=5
+
+{% if dns %}
+[dns]
+{% if dns[0] %}
+dns1={{dns[0]}}
+{% endif %}
+{% if dns[1] %}
+dns2={{dns[1]}}
+{% endif %}
+{% endif %}
+
+{% if wins %}
+[wins]
+{% if wins[0] %}
+wins1={{wins[0]}}
+{% endif %}
+{% if wins[1] %}
+wins2={{wins[1]}}
+{% endif %}
+{% endif %}
+
+[pptp]
+ifname=pptp%d
+{% if outside_addr %}
+bind={{outside_addr}}
+{% endif %}
+verbose=1
+ppp-max-mtu={{mtu}}
+mppe={{authentication['mppe']}}
+echo-interval=10
+echo-failure=3
+
+
+[client-ip-range]
+0.0.0.0/0
+
+[ip-pool]
+tunnel={{client_ip_pool}}
+gw-ip-address={{gw_ip}}
+
+{% if authentication['mode'] == 'local' %}
+[chap-secrets]
+chap-secrets=/etc/accel-ppp/pptp/chap-secrets
+{% endif %}
+
+[ppp]
+verbose=5
+check-ip=1
+single-session=replace
+
+{% if authentication['mode'] == 'radius' %}
+[radius]
+{% for rsrv in authentication['radiussrv']: %}
+server={{rsrv}},{{authentication['radiussrv'][rsrv]['secret']}},\
+req-limit={{authentication['radiussrv'][rsrv]['req-limit']}},\
+fail-time={{authentication['radiussrv'][rsrv]['fail-time']}}
+{% endfor %}
+timeout=30
+acct-timeout=30
+max-try=3
+{%endif %}
+
+[cli]
+tcp=127.0.0.1:2003
diff --git a/data/templates/router-advert/radvd.conf.tmpl b/data/templates/router-advert/radvd.conf.tmpl
new file mode 100644
index 000000000..2768f6f2e
--- /dev/null
+++ b/data/templates/router-advert/radvd.conf.tmpl
@@ -0,0 +1,37 @@
+### Autogenerated by service-router-advert.py ###
+
+{% for i in interfaces -%}
+interface {{ i.name }} {
+ IgnoreIfMissing on;
+ AdvDefaultPreference {{ i.default_preference }};
+ AdvManagedFlag {{ i.managed_flag }};
+ MaxRtrAdvInterval {{ i.interval_max }};
+{% if i.interval_min %}
+ MinRtrAdvInterval {{ i.interval_min }};
+{% endif %}
+ AdvReachableTime {{ i.reachable_time }};
+ AdvIntervalOpt {{ i.send_advert }};
+ AdvSendAdvert {{ i.send_advert }};
+{% if i.default_lifetime %}
+ AdvDefaultLifetime {{ i.default_lifetime }};
+{% endif %}
+{% if i.link_mtu %}
+ AdvLinkMTU {{ i.link_mtu }};
+{% endif %}
+ AdvOtherConfigFlag {{ i.other_config_flag }};
+ AdvRetransTimer {{ i.retrans_timer }};
+ AdvCurHopLimit {{ i.hop_limit }};
+{% for p in i.prefixes %}
+ prefix {{ p.prefix }} {
+ AdvAutonomous {{ p.autonomous_flag }};
+ AdvValidLifetime {{ p.valid_lifetime }};
+ AdvOnLink {{ p.on_link }};
+ AdvPreferredLifetime {{ p.preferred_lifetime }};
+ };
+{% endfor %}
+{% if i.name_server %}
+ RDNSS {{ i.name_server | join(" ") }} {
+ };
+{% endif %}
+};
+{% endfor -%}
diff --git a/data/templates/salt-minion/minion.tmpl b/data/templates/salt-minion/minion.tmpl
new file mode 100644
index 000000000..5e50d588c
--- /dev/null
+++ b/data/templates/salt-minion/minion.tmpl
@@ -0,0 +1,63 @@
+### Autogenerated by salt-minion.py ###
+
+##### Primary configuration settings #####
+##########################################
+
+# The hash_type is the hash to use when discovering the hash of a file on
+# the master server. The default is sha256, but md5, sha1, sha224, sha384 and
+# sha512 are also supported.
+#
+# WARNING: While md5 and sha1 are also supported, do not use them due to the
+# high chance of possible collisions and thus security breach.
+#
+# Prior to changing this value, the master should be stopped and all Salt
+# caches should be cleared.
+hash_type: {{ hash_type }}
+
+##### Logging settings #####
+##########################################
+# The location of the minion log file
+# The minion log can be sent to a regular file, local path name, or network
+# location. Remote logging works best when configured to use rsyslogd(8) (e.g.:
+# ``file:///dev/log``), with rsyslogd(8) configured for network logging. The URI
+# format is: <file|udp|tcp>://<host|socketpath>:<port-if-required>/<log-facility>
+#log_file: /var/log/salt/minion
+#log_file: file:///dev/log
+#log_file: udp://loghost:10514
+#
+log_file: {{ log_file }}
+
+# The level of messages to send to the console.
+# One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'.
+#
+# The following log levels are considered INSECURE and may log sensitive data:
+# ['garbage', 'trace', 'debug']
+#
+# Default: 'warning'
+log_level: {{ log_level }}
+
+# Set the location of the salt master server, if the master server cannot be
+# resolved, then the minion will fail to start.
+master:
+{% for host in master -%}
+- {{ host }}
+{% endfor %}
+
+# The user to run salt
+user: {{ user }}
+
+# The directory to store the pki information in
+pki_dir: /config/salt/pki/minion
+
+# Explicitly declare the id for this minion to use, if left commented the id
+# will be the hostname as returned by the python call: socket.getfqdn()
+# Since salt uses detached ids it is possible to run multiple minions on the
+# same machine but with different ids, this can be useful for salt compute
+# clusters.
+id: {{ salt_id }}
+
+
+# The number of minutes between mine updates.
+mine_interval: {{ mine_interval }}
+
+verify_master_pubkey_sign: {{ verify_master_pubkey_sign }}
diff --git a/data/templates/snmp/etc.snmp.conf.tmpl b/data/templates/snmp/etc.snmp.conf.tmpl
new file mode 100644
index 000000000..159578906
--- /dev/null
+++ b/data/templates/snmp/etc.snmp.conf.tmpl
@@ -0,0 +1,4 @@
+### Autogenerated by snmp.py ###
+{% if trap_source -%}
+clientaddr {{ trap_source }}
+{% endif %}
diff --git a/data/templates/snmp/etc.snmpd.conf.tmpl b/data/templates/snmp/etc.snmpd.conf.tmpl
new file mode 100644
index 000000000..1659abf93
--- /dev/null
+++ b/data/templates/snmp/etc.snmpd.conf.tmpl
@@ -0,0 +1,118 @@
+### Autogenerated by snmp.py ###
+
+# non configurable defaults
+sysObjectID 1.3.6.1.4.1.44641
+sysServices 14
+master agentx
+agentXPerms 0777 0777
+pass .1.3.6.1.2.1.31.1.1.1.18 /opt/vyatta/sbin/if-mib-alias
+smuxpeer .1.3.6.1.2.1.83
+smuxpeer .1.3.6.1.2.1.157
+smuxsocket localhost
+
+# linkUp/Down configure the Event MIB tables to monitor
+# the ifTable for network interfaces being taken up or down
+# for making internal queries to retrieve any necessary information
+iquerySecName {{ vyos_user }}
+
+# Modified from the default linkUpDownNotification
+# to include more OIDs and poll more frequently
+notificationEvent linkUpTrap linkUp ifIndex ifDescr ifType ifAdminStatus ifOperStatus
+notificationEvent linkDownTrap linkDown ifIndex ifDescr ifType ifAdminStatus ifOperStatus
+monitor -r 10 -e linkUpTrap "Generate linkUp" ifOperStatus != 2
+monitor -r 10 -e linkDownTrap "Generate linkDown" ifOperStatus == 2
+
+########################
+# configurable section #
+########################
+
+# Default system description is VyOS version
+sysDescr VyOS {{ version }}
+
+{% if description %}
+# Description
+SysDescr {{ description }}
+{%- endif %}
+
+# Listen
+agentaddress unix:/run/snmpd.socket{% if listen_on %}{% for li in listen_on %},{{ li }}{% endfor %}{% else %},udp:161{% if ipv6_enabled %},udp6:161{% endif %}{% endif %}
+
+# SNMP communities
+{%- for c in communities %}
+
+{%- if c.network_v4 %}
+{%- for network in c.network_v4 %}
+{{ c.authorization }}community {{ c.name }} {{ network }}
+{%- endfor %}
+{%- elif not c.has_source %}
+{{ c.authorization }}community {{ c.name }}
+{%- endif %}
+
+{%- if c.network_v6 %}
+{%- for network in c.network_v6 %}
+{{ c.authorization }}community6 {{ c.name }} {{ network }}
+{%- endfor %}
+{%- elif not c.has_source %}
+{{ c.authorization }}community6 {{ c.name }}
+{%- endif %}
+
+{%- endfor %}
+
+{% if contact %}
+# system contact information
+SysContact {{ contact }}
+{%- endif %}
+
+{% if location %}
+# system location information
+SysLocation {{ location }}
+{%- endif %}
+
+{% if smux_peers -%}
+# additional smux peers
+{%- for sp in smux_peers %}
+smuxpeer {{ sp }}
+{%- endfor %}
+{%- endif %}
+
+{% if trap_targets -%}
+# if there is a problem - tell someone!
+{%- for t in trap_targets %}
+trap2sink {{ t.target }}{% if t.port -%}:{{ t.port }}{% endif %} {{ t.community }}
+{%- endfor %}
+{%- endif %}
+
+{%- if v3_enabled %}
+#
+# SNMPv3 stuff goes here
+#
+# views
+{%- for v in v3_views %}
+{%- for oid in v.oids %}
+view {{ v.name }} included .{{ oid.oid }}
+{%- endfor %}
+{%- endfor %}
+
+# access
+# context sec.model sec.level match read write notif
+{%- for g in v3_groups %}
+access {{ g.name }} "" usm {{ g.seclevel }} exact {{ g.view }} {% if g.mode == 'ro' %}none{% else %}{{ g.view }}{% endif %} none
+{%- endfor %}
+
+# trap-target
+{%- for t in v3_traps %}
+trapsess -v 3 {{ '-Ci' if t.type == 'inform' }} -e {{ v3_engineid }} -u {{ t.secName }} -l {{ t.secLevel }} -a {{ t.authProtocol }} {% if t.authPassword %}-A {{ t.authPassword }}{% elif t.authMasterKey %}-3m {{ t.authMasterKey }}{% endif %} -x {{ t.privProtocol }} {% if t.privPassword %}-X {{ t.privPassword }}{% elif t.privMasterKey %}-3M {{ t.privMasterKey }}{% endif %} {{ t.ipProto }}:{{ t.ipAddr }}:{{ t.ipPort }}
+{%- endfor %}
+
+# group
+{%- for u in v3_users %}
+group {{ u.group }} usm {{ u.name }}
+{% endfor %}
+{%- endif %}
+
+{% if script_ext %}
+# extension scripts
+{%- for ext in script_ext|sort(attribute='name') %}
+extend {{ ext.name }} {{ ext.script }}
+{%- endfor %}
+{% endif %}
diff --git a/data/templates/snmp/usr.snmpd.conf.tmpl b/data/templates/snmp/usr.snmpd.conf.tmpl
new file mode 100644
index 000000000..9c0337fa8
--- /dev/null
+++ b/data/templates/snmp/usr.snmpd.conf.tmpl
@@ -0,0 +1,6 @@
+### Autogenerated by snmp.py ###
+{%- for u in v3_users %}
+{{ u.mode }}user {{ u.name }}
+{%- endfor %}
+
+rwuser {{ vyos_user }}
diff --git a/data/templates/snmp/var.snmpd.conf.tmpl b/data/templates/snmp/var.snmpd.conf.tmpl
new file mode 100644
index 000000000..0b8e9f291
--- /dev/null
+++ b/data/templates/snmp/var.snmpd.conf.tmpl
@@ -0,0 +1,16 @@
+### Autogenerated by snmp.py ###
+# user
+{%- for u in v3_users %}
+{%- if u.authOID == 'none' %}
+createUser {{ u.name }}
+{%- elif u.authPassword %}
+createUser {{ u.name }} {{ u.authProtocol | upper }} "{{ u.authPassword }}" {{ u.privProtocol | upper }} {{ u.privPassword }}
+{%- else %}
+usmUser 1 3 {{ v3_engineid }} "{{ u.name }}" "{{ u.name }}" NULL {{ u.authOID }} {{ u.authMasterKey }} {{ u.privOID }} {{ u.privMasterKey }} 0x
+{%- endif %}
+{%- endfor %}
+
+createUser {{ vyos_user }} MD5 "{{ vyos_user_pass }}" DES
+{%- if v3_engineid %}
+oldEngineID {{ v3_engineid }}
+{%- endif %}
diff --git a/data/templates/ssh/sshd_config.tmpl b/data/templates/ssh/sshd_config.tmpl
new file mode 100644
index 000000000..5deb5232a
--- /dev/null
+++ b/data/templates/ssh/sshd_config.tmpl
@@ -0,0 +1,125 @@
+### Autogenerated by ssh.py ###
+
+# Non-configurable defaults
+Protocol 2
+HostKey /etc/ssh/ssh_host_rsa_key
+HostKey /etc/ssh/ssh_host_dsa_key
+HostKey /etc/ssh/ssh_host_ecdsa_key
+HostKey /etc/ssh/ssh_host_ed25519_key
+SyslogFacility AUTH
+LoginGraceTime 120
+StrictModes yes
+PubkeyAuthentication yes
+IgnoreRhosts yes
+HostbasedAuthentication no
+PermitEmptyPasswords no
+ChallengeResponseAuthentication no
+X11Forwarding yes
+X11DisplayOffset 10
+PrintMotd no
+PrintLastLog yes
+TCPKeepAlive yes
+Banner /etc/issue.net
+Subsystem sftp /usr/lib/openssh/sftp-server
+UsePAM yes
+HostKey /etc/ssh/ssh_host_rsa_key
+
+# Specifies whether sshd should look up the remote host name,
+# and to check that the resolved host name for the remote IP
+# address maps back to the very same IP address.
+UseDNS {{ host_validation }}
+
+# Specifies the port number that sshd listens on. The default is 22.
+# Multiple options of this type are permitted.
+{% if mport|length != 0 %}
+{% for p in mport %}
+Port {{ p }}
+{% endfor %}
+{% else %}
+Port {{ port }}
+{% endif %}
+
+# Gives the verbosity level that is used when logging messages from sshd
+LogLevel {{ log_level }}
+
+# Specifies whether root can log in using ssh
+PermitRootLogin no
+
+# Specifies whether password authentication is allowed
+PasswordAuthentication {{ password_authentication }}
+
+{% if listen_on %}
+# Specifies the local addresses sshd should listen on
+{% for a in listen_on %}
+ListenAddress {{ a }}
+{% endfor %}
+{{ "\n" }}
+{% endif %}
+
+{%- if ciphers %}
+# Specifies the ciphers allowed. Multiple ciphers must be comma-separated.
+#
+# NOTE: As of now, there is no 'multi' node for 'ciphers', thus we have only one :/
+Ciphers {{ ciphers | join(",") }}
+{{ "\n" }}
+{% endif %}
+
+{%- if mac %}
+# Specifies the available MAC (message authentication code) algorithms. The MAC
+# algorithm is used for data integrity protection. Multiple algorithms must be
+# comma-separated.
+#
+# NOTE: As of now, there is no 'multi' node for 'mac', thus we have only one :/
+MACs {{ mac | join(",") }}
+{{ "\n" }}
+{% endif %}
+
+{%- if key_exchange %}
+# Specifies the available KEX (Key Exchange) algorithms. Multiple algorithms must
+# be comma-separated.
+#
+# NOTE: As of now, there is no 'multi' node for 'key-exchange', thus we have only one :/
+KexAlgorithms {{ key_exchange | join(",") }}
+{{ "\n" }}
+{% endif %}
+
+{%- if allow_users %}
+# This keyword can be followed by a list of user name patterns, separated by spaces.
+# If specified, login is allowed only for user names that match one of the patterns.
+# Only user names are valid, a numerical user ID is not recognized.
+AllowUsers {{ allow_users | join(" ") }}
+{{ "\n" }}
+{% endif %}
+
+{%- if allow_groups %}
+# This keyword can be followed by a list of group name patterns, separated by spaces.
+# If specified, login is allowed only for users whose primary group or supplementary
+# group list matches one of the patterns. Only group names are valid, a numerical group
+# ID is not recognized.
+AllowGroups {{ allow_groups | join(" ") }}
+{{ "\n" }}
+{% endif %}
+
+{%- if deny_users %}
+# This keyword can be followed by a list of user name patterns, separated by spaces.
+# Login is disallowed for user names that match one of the patterns. Only user names
+# are valid, a numerical user ID is not recognized.
+DenyUsers {{ deny_users | join(" ") }}
+{{ "\n" }}
+{% endif %}
+
+{%- if deny_groups %}
+# This keyword can be followed by a list of group name patterns, separated by spaces.
+# Login is disallowed for users whose primary group or supplementary group list matches
+# one of the patterns. Only group names are valid, a numerical group ID is not recognized.
+DenyGroups {{ deny_groups | join(" ") }}
+{{ "\n" }}
+{% endif %}
+
+{%- if client_keepalive %}
+# Sets a timeout interval in seconds after which if no data has been received from the client,
+# sshd will send a message through the encrypted channel to request a response from the client.
+# The default is 0, indicating that these messages will not be sent to the client.
+# This option applies to protocol version 2 only.
+ClientAliveInterval {{ client_keepalive }}
+{% endif %}
diff --git a/data/templates/sstp/chap-secrets.tmpl b/data/templates/sstp/chap-secrets.tmpl
new file mode 100644
index 000000000..dd00d7bd0
--- /dev/null
+++ b/data/templates/sstp/chap-secrets.tmpl
@@ -0,0 +1,10 @@
+# username server password acceptable local IP addresses shaper
+{% for user in local_users %}
+{% if user.state == 'enabled' %}
+{% if user.upload and user.download %}
+{{ "%-12s" | format(user.name) }} * {{ "%-16s" | format(user.password) }} {{ "%-16s" | format(user.ip) }} {{ user.download }} / {{ user.upload }}
+{% else %}
+{{ "%-12s" | format(user.name) }} * {{ "%-16s" | format(user.password) }} {{ "%-16s" | format(user.ip) }}
+{% endif %}
+{% endif %}
+{% endfor %}
diff --git a/data/templates/sstp/sstp.config.tmpl b/data/templates/sstp/sstp.config.tmpl
new file mode 100644
index 000000000..19805358e
--- /dev/null
+++ b/data/templates/sstp/sstp.config.tmpl
@@ -0,0 +1,114 @@
+### generated by vpn_sstp.py ###
+[modules]
+log_syslog
+sstp
+shaper
+{% if auth_mode == 'local' %}
+chap-secrets
+{% elif auth_mode == 'radius' %}
+radius
+{% endif -%}
+ippool
+
+{% for proto in auth_proto %}
+{{proto}}
+{% endfor %}
+
+[core]
+thread-count={{thread_cnt}}
+
+[common]
+single-session=replace
+
+[log]
+syslog=accel-sstp,daemon
+copy=1
+level=5
+
+[client-ip-range]
+disable
+
+[sstp]
+verbose=1
+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 %}
+
+{% if dnsv4 %}
+[dns]
+{% for dns in dnsv4 -%}
+dns{{ loop.index }}={{ dns }}
+{% endfor -%}
+{% endif %}
+
+{% if auth_mode == 'local' %}
+[chap-secrets]
+chap-secrets=/etc/accel-ppp/sstp/chap-secrets
+{% elif auth_mode == 'radius' %}
+[radius]
+verbose=1
+{% for r in radius_server %}
+server={{ r.server }},{{ r.key }},auth-port={{ r.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 %}
+
+[ppp]
+verbose=1
+check-ip=1
+{% if mtu %}
+mtu={{ mtu }}
+{% endif -%}
+
+{% 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 radius_shaper_attr %}
+[shaper]
+verbose=1
+attr={{ radius_shaper_attr }}
+{% if radius_shaper_vendor %}
+vendor={{ radius_shaper_vendor }}
+{% endif -%}
+{% endif %}
+
+[cli]
+tcp=127.0.0.1:2005
+
diff --git a/data/templates/syslog/logrotate.tmpl b/data/templates/syslog/logrotate.tmpl
new file mode 100644
index 000000000..f758265e4
--- /dev/null
+++ b/data/templates/syslog/logrotate.tmpl
@@ -0,0 +1,12 @@
+{% for file in files %}
+{{files[file]['log-file']}} {
+ missingok
+ notifempty
+ create
+ rotate {{files[file]['max-files']}}
+ size={{files[file]['max-size']//1024}}k
+ postrotate
+ invoke-rc.d rsyslog rotate > /dev/null
+ endscript
+}
+{% endfor %}
diff --git a/data/templates/syslog/rsyslog.conf.tmpl b/data/templates/syslog/rsyslog.conf.tmpl
new file mode 100644
index 000000000..bc3f7667b
--- /dev/null
+++ b/data/templates/syslog/rsyslog.conf.tmpl
@@ -0,0 +1,44 @@
+## generated by syslog.py ##
+## file based logging
+{% if files['global']['marker'] -%}
+$ModLoad immark
+{% if files['global']['marker-interval'] %}
+$MarkMessagePeriod {{files['global']['marker-interval']}}
+{% endif %}
+{% endif -%}
+{% if files['global']['preserver_fqdn'] -%}
+$PreserveFQDN on
+{% endif -%}
+{% for file in files %}
+$outchannel {{file}},{{files[file]['log-file']}},{{files[file]['max-size']}},{{files[file]['action-on-max-size']}}
+{{files[file]['selectors']}} :omfile:${{file}}
+{% endfor %}
+{% if console %}
+## console logging
+{% for con in console %}
+{{console[con]['selectors']}} /dev/console
+{% endfor %}
+{% endif %}
+{% if hosts %}
+## remote logging
+{% for host in hosts %}
+{% if hosts[host]['proto'] == 'tcp' %}
+{% if hosts[host]['port'] %}
+{{hosts[host]['selectors']}} @@{{host}}:{{hosts[host]['port']}}
+{% else %}
+{{hosts[host]['selectors']}} @@{{host}}
+{% endif %}
+{% else %}
+{% if hosts[host]['port'] %}
+{{hosts[host]['selectors']}} @{{host}}:{{hosts[host]['port']}}
+{% else %}
+{{hosts[host]['selectors']}} @{{host}}
+{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if user %}
+{% for u in user %}
+{{user[u]['selectors']}} :omusrmsg:{{u}}
+{% endfor %}
+{% endif %}
diff --git a/data/templates/system-login/pam_radius_auth.conf.tmpl b/data/templates/system-login/pam_radius_auth.conf.tmpl
new file mode 100644
index 000000000..6cff67867
--- /dev/null
+++ b/data/templates/system-login/pam_radius_auth.conf.tmpl
@@ -0,0 +1,13 @@
+# Automatically generated by VyOS
+# RADIUS configuration file
+{%- if radius_server %}
+# server[:port] shared_secret timeout (s) source_ip
+{% for s in radius_server %}
+{%- if not s.disabled -%}
+{{ s.address }}:{{ s.port }} {{ s.key }} {{ s.timeout }} {% if radius_source_address -%}{{ radius_source_address }}{% endif %}
+{% endif %}
+{%- endfor %}
+
+priv-lvl 15
+mapped_priv_user radius_priv_user
+{% endif %}
diff --git a/data/templates/tftp-server/default.tmpl b/data/templates/tftp-server/default.tmpl
new file mode 100644
index 000000000..18fee35d1
--- /dev/null
+++ b/data/templates/tftp-server/default.tmpl
@@ -0,0 +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 }}"
diff --git a/data/templates/vrf/vrf.conf.tmpl b/data/templates/vrf/vrf.conf.tmpl
new file mode 100644
index 000000000..761b0bb6f
--- /dev/null
+++ b/data/templates/vrf/vrf.conf.tmpl
@@ -0,0 +1,8 @@
+### Autogenerated by vrf.py ###
+#
+# Routing table ID to name mapping reference
+
+# id vrf name comment
+{% for vrf in vrf_add -%}
+{{ "%-10s" | format(vrf.table) }} {{ "%-16s" | format(vrf.name) }} # {{ vrf.description }}
+{% endfor -%}
diff --git a/data/templates/vrrp/daemon.tmpl b/data/templates/vrrp/daemon.tmpl
new file mode 100644
index 000000000..62d0ba63b
--- /dev/null
+++ b/data/templates/vrrp/daemon.tmpl
@@ -0,0 +1,5 @@
+# Autogenerated by VyOS
+# Options to pass to keepalived
+
+# DAEMON_ARGS are appended to the keepalived command-line
+DAEMON_ARGS="--snmp"
diff --git a/data/templates/vrrp/keepalived.conf.tmpl b/data/templates/vrrp/keepalived.conf.tmpl
new file mode 100644
index 000000000..08b821f70
--- /dev/null
+++ b/data/templates/vrrp/keepalived.conf.tmpl
@@ -0,0 +1,97 @@
+# Autogenerated by VyOS
+# Do not edit this file, all your changes will be lost
+# on next commit or reboot
+
+global_defs {
+ dynamic_interfaces
+ script_user root
+ notify_fifo /run/keepalived_notify_fifo
+ notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py
+}
+
+{% for group in groups -%}
+
+{% if group.health_check_script -%}
+vrrp_script healthcheck_{{ group.name }} {
+ script "{{ group.health_check_script }}"
+ interval {{ group.health_check_interval }}
+ fall {{ group.health_check_count }}
+ rise 1
+
+}
+{% endif %}
+
+vrrp_instance {{ group.name }} {
+ {% if group.description -%}
+ # {{ group.description }}
+ {% endif -%}
+
+ state BACKUP
+ interface {{ group.interface }}
+ virtual_router_id {{ group.vrid }}
+ priority {{ group.priority }}
+ advert_int {{ group.advertise_interval }}
+
+ {% if group.preempt -%}
+ preempt_delay {{ group.preempt_delay }}
+ {% else -%}
+ nopreempt
+ {% endif -%}
+
+ {% if group.peer_address -%}
+ unicast_peer { {{ group.peer_address }} }
+ {% endif -%}
+
+ {% if group.hello_source -%}
+ {%- if group.peer_address -%}
+ unicast_src_ip {{ group.hello_source }}
+ {%- else -%}
+ mcast_src_ip {{ group.hello_source }}
+ {%- endif %}
+ {% endif -%}
+
+ {% if group.use_vmac and group.peer_address -%}
+ use_vmac {{group.interface}}v{{group.vrid}}
+ vmac_xmit_base
+ {% elif group.use_vmac -%}
+ use_vmac {{group.interface}}v{{group.vrid}}
+ {% endif -%}
+
+ {% if group.auth_password -%}
+ authentication {
+ auth_pass "{{ group.auth_password }}"
+ auth_type {{ group.auth_type }}
+ }
+ {% endif -%}
+
+ virtual_ipaddress {
+ {% for addr in group.virtual_addresses -%}
+ {{ addr }}
+ {% endfor -%}
+ }
+
+ {% if group.health_check_script -%}
+ track_script {
+ healthcheck_{{ group.name }}
+ }
+ {% endif -%}
+}
+
+{% endfor -%}
+
+{% for sync_group in sync_groups -%}
+vrrp_sync_group {{ sync_group.name }} {
+ group {
+ {% for member in sync_group.members -%}
+ {{ member }}
+ {% endfor -%}
+ }
+
+ {% if sync_group.conntrack_sync -%}
+ notify_master "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh master {{ sync_group.name }}"
+ notify_backup "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh backup {{ sync_group.name }}"
+ notify_fault "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh fault {{ sync_group.name }}"
+ {% endif -%}
+}
+
+{% endfor -%}
diff --git a/data/templates/wifi/cfg80211.conf.tmpl b/data/templates/wifi/cfg80211.conf.tmpl
new file mode 100644
index 000000000..b21bacc1e
--- /dev/null
+++ b/data/templates/wifi/cfg80211.conf.tmpl
@@ -0,0 +1,3 @@
+{%- if regdom -%}
+options cfg80211 ieee80211_regdom={{ regdom }}
+{% endif %}
diff --git a/data/templates/wifi/crda.tmpl b/data/templates/wifi/crda.tmpl
new file mode 100644
index 000000000..750ad86ee
--- /dev/null
+++ b/data/templates/wifi/crda.tmpl
@@ -0,0 +1,3 @@
+{%- if regdom -%}
+REGDOMAIN={{ regdom }}
+{% endif %}
diff --git a/data/templates/wifi/hostapd.conf.tmpl b/data/templates/wifi/hostapd.conf.tmpl
new file mode 100644
index 000000000..031fb6c90
--- /dev/null
+++ b/data/templates/wifi/hostapd.conf.tmpl
@@ -0,0 +1,728 @@
+### Autogenerated by interfaces-wireless.py ###
+{% if description %}
+# Description: {{ description }}
+# User-friendly description of device; up to 32 octets encoded in UTF-8
+device_name={{ description | truncate(32, True) }}
+{% endif %}
+
+# AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for
+# management frames with the Host AP driver); wlan0 with many nl80211 drivers
+# Note: This attribute can be overridden by the values supplied with the '-i'
+# command line parameter.
+interface={{ intf }}
+
+# Driver interface type (hostap/wired/none/nl80211/bsd);
+# default: hostap). nl80211 is used with all Linux mac80211 drivers.
+# Use driver=none if building hostapd as a standalone RADIUS server that does
+# not control any wireless/wired driver.
+driver=nl80211
+
+# Levels (minimum value for logged events):
+# 0 = verbose debugging
+# 1 = debugging
+# 2 = informational messages
+# 3 = notification
+# 4 = warning
+logger_syslog=-1
+logger_syslog_level=0
+logger_stdout=-1
+logger_stdout_level=0
+
+{%- if country_code %}
+
+# Country code (ISO/IEC 3166-1). Used to set regulatory domain.
+# Set as needed to indicate country in which device is operating.
+# This can limit available channels and transmit power.
+country_code={{ country_code }}
+
+# Enable IEEE 802.11d. This advertises the country_code and the set of allowed
+# channels and transmit power levels based on the regulatory limits. The
+# country_code setting must be configured with the correct country for
+# IEEE 802.11d functions.
+ieee80211d=1
+{% endif %}
+
+{%- if ssid %}
+
+# SSID to be used in IEEE 802.11 management frames
+ssid={{ ssid }}
+{% endif %}
+
+{%- if channel %}
+
+# Channel number (IEEE 802.11)
+# (default: 0, i.e., not set)
+# Please note that some drivers do not use this value from hostapd and the
+# channel will need to be configured separately with iwconfig.
+#
+# If CONFIG_ACS build option is enabled, the channel can be selected
+# automatically at run time by setting channel=acs_survey or channel=0, both of
+# which will enable the ACS survey based algorithm.
+channel={{ channel }}
+{% endif %}
+
+{%- if mode %}
+
+# Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz),
+# g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used
+# with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this
+# needs to be set to hw_mode=a. For IEEE 802.11ax (HE) on 6 GHz this needs
+# to be set to hw_mode=a. When using ACS (see channel parameter), a
+# special value "any" can be used to indicate that any support band can be used.
+# This special case is currently supported only with drivers with which
+# offloaded ACS is used.
+{% if 'n' in mode -%}
+hw_mode=g
+ieee80211n=1
+{% elif 'ac' in mode -%}
+hw_mode=a
+ieee80211h=1
+ieee80211ac=1
+{% else -%}
+hw_mode={{ mode }}
+{% endif %}
+{% endif %}
+
+# ieee80211w: Whether management frame protection (MFP) is enabled
+# 0 = disabled (default)
+# 1 = optional
+# 2 = required
+{% if 'disabled' in mgmt_frame_protection -%}
+ieee80211w=0
+{% elif 'optional' in mgmt_frame_protection -%}
+ieee80211w=1
+{% elif 'required' in mgmt_frame_protection -%}
+ieee80211w=2
+{% endif %}
+
+# ht_capab: HT capabilities (list of flags)
+# LDPC coding capability: [LDPC] = supported
+# Supported channel width set: [HT40-] = both 20 MHz and 40 MHz with secondary
+# channel below the primary channel; [HT40+] = both 20 MHz and 40 MHz
+# with secondary channel above the primary channel
+# (20 MHz only if neither is set)
+# Note: There are limits on which channels can be used with HT40- and
+# HT40+. Following table shows the channels that may be available for
+# HT40- and HT40+ use per IEEE 802.11n Annex J:
+# freq HT40- HT40+
+# 2.4 GHz 5-13 1-7 (1-9 in Europe/Japan)
+# 5 GHz 40,48,56,64 36,44,52,60
+# (depending on the location, not all of these channels may be available
+# for use)
+# Please note that 40 MHz channels may switch their primary and secondary
+# channels if needed or creation of 40 MHz channel maybe rejected based
+# on overlapping BSSes. These changes are done automatically when hostapd
+# is setting up the 40 MHz channel.
+# Spatial Multiplexing (SM) Power Save: [SMPS-STATIC] or [SMPS-DYNAMIC]
+# (SMPS disabled if neither is set)
+# HT-greenfield: [GF] (disabled if not set)
+# Short GI for 20 MHz: [SHORT-GI-20] (disabled if not set)
+# Short GI for 40 MHz: [SHORT-GI-40] (disabled if not set)
+# Tx STBC: [TX-STBC] (disabled if not set)
+# Rx STBC: [RX-STBC1] (one spatial stream), [RX-STBC12] (one or two spatial
+# streams), or [RX-STBC123] (one, two, or three spatial streams); Rx STBC
+# disabled if none of these set
+# HT-delayed Block Ack: [DELAYED-BA] (disabled if not set)
+# Maximum A-MSDU length: [MAX-AMSDU-7935] for 7935 octets (3839 octets if not
+# set)
+# DSSS/CCK Mode in 40 MHz: [DSSS_CCK-40] = allowed (not allowed if not set)
+# 40 MHz intolerant [40-INTOLERANT] (not advertised if not set)
+# L-SIG TXOP protection support: [LSIG-TXOP-PROT] (disabled if not set)
+{% if cap_ht %}
+ht_capab=
+{%- endif -%}
+
+{%- if cap_ht_40mhz_incapable -%}
+[40-INTOLERANT]
+{%- endif -%}
+
+{%- if cap_ht_delayed_block_ack -%}
+[DELAYED-BA]
+{%- endif -%}
+
+{%- if cap_ht_dsss_cck_40 -%}
+[DSSS_CCK-40]
+{%- endif -%}
+
+{%- if cap_ht_greenfield -%}
+[GF]
+{%- endif -%}
+
+{%- if cap_ht_ldpc -%}
+[LDPC]
+{%- endif -%}
+
+{%- if cap_ht_lsig_protection -%}
+[LSIG-TXOP-PROT]
+{%- endif -%}
+
+{%- if cap_ht_max_amsdu -%}
+[MAX-AMSDU-{{ cap_ht_max_amsdu }}]
+{%- endif -%}
+
+{%- if cap_ht_smps -%}
+[SMPS-{{ cap_ht_smps | upper }}]
+{%- endif -%}
+
+{%- if cap_ht_chan_set_width -%}
+{%- for csw in cap_ht_chan_set_width -%}
+[{{ csw | upper }}]
+{%- endfor -%}
+{%- endif -%}
+
+{%- if cap_ht_short_gi -%}
+{%- for gi in cap_ht_short_gi -%}
+[SHORT-GI-{{ gi }}]
+{%- endfor -%}
+{%- endif -%}
+
+{%- if cap_ht_stbc_tx -%}
+[TX-STBC]
+{%- endif -%}
+{%- if cap_ht_stbc_rx -%}
+[RX-STBC{{ cap_ht_stbc_rx }}]
+{%- endif %}
+
+# Required for full HT and VHT functionality
+wme_enabled=1
+
+{% if cap_ht_powersave -%}
+# WMM-PS Unscheduled Automatic Power Save Delivery [U-APSD]
+# Enable this flag if U-APSD supported outside hostapd (eg., Firmware/driver)
+uapsd_advertisement_enabled=1
+{%- endif %}
+
+{% if cap_req_ht -%}
+# Require stations to support HT PHY (reject association if they do not)
+require_ht=1
+{% endif %}
+
+{%- if cap_vht_chan_set_width -%}
+vht_oper_chwidth={{ cap_vht_chan_set_width }}
+{%- endif %}
+
+# vht_capab: VHT capabilities (list of flags)
+#
+# vht_max_mpdu_len: [MAX-MPDU-7991] [MAX-MPDU-11454]
+# Indicates maximum MPDU length
+# 0 = 3895 octets (default)
+# 1 = 7991 octets
+# 2 = 11454 octets
+# 3 = reserved
+#
+# supported_chan_width: [VHT160] [VHT160-80PLUS80]
+# Indicates supported Channel widths
+# 0 = 160 MHz & 80+80 channel widths are not supported (default)
+# 1 = 160 MHz channel width is supported
+# 2 = 160 MHz & 80+80 channel widths are supported
+# 3 = reserved
+#
+# Rx LDPC coding capability: [RXLDPC]
+# Indicates support for receiving LDPC coded pkts
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Short GI for 80 MHz: [SHORT-GI-80]
+# Indicates short GI support for reception of packets transmitted with TXVECTOR
+# params format equal to VHT and CBW = 80Mhz
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Short GI for 160 MHz: [SHORT-GI-160]
+# Indicates short GI support for reception of packets transmitted with TXVECTOR
+# params format equal to VHT and CBW = 160Mhz
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Tx STBC: [TX-STBC-2BY1]
+# Indicates support for the transmission of at least 2x1 STBC
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Rx STBC: [RX-STBC-1] [RX-STBC-12] [RX-STBC-123] [RX-STBC-1234]
+# Indicates support for the reception of PPDUs using STBC
+# 0 = Not supported (default)
+# 1 = support of one spatial stream
+# 2 = support of one and two spatial streams
+# 3 = support of one, two and three spatial streams
+# 4 = support of one, two, three and four spatial streams
+# 5,6,7 = reserved
+#
+# SU Beamformer Capable: [SU-BEAMFORMER]
+# Indicates support for operation as a single user beamformer
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# SU Beamformee Capable: [SU-BEAMFORMEE]
+# Indicates support for operation as a single user beamformee
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Compressed Steering Number of Beamformer Antennas Supported:
+# [BF-ANTENNA-2] [BF-ANTENNA-3] [BF-ANTENNA-4]
+# Beamformee's capability indicating the maximum number of beamformer
+# antennas the beamformee can support when sending compressed beamforming
+# feedback
+# If SU beamformer capable, set to maximum value minus 1
+# else reserved (default)
+#
+# Number of Sounding Dimensions:
+# [SOUNDING-DIMENSION-2] [SOUNDING-DIMENSION-3] [SOUNDING-DIMENSION-4]
+# Beamformer's capability indicating the maximum value of the NUM_STS parameter
+# in the TXVECTOR of a VHT NDP
+# If SU beamformer capable, set to maximum value minus 1
+# else reserved (default)
+#
+# MU Beamformer Capable: [MU-BEAMFORMER]
+# Indicates support for operation as an MU beamformer
+# 0 = Not supported or sent by Non-AP STA (default)
+# 1 = Supported
+#
+# VHT TXOP PS: [VHT-TXOP-PS]
+# Indicates whether or not the AP supports VHT TXOP Power Save Mode
+# or whether or not the STA is in VHT TXOP Power Save mode
+# 0 = VHT AP doesn't support VHT TXOP PS mode (OR) VHT STA not in VHT TXOP PS
+# mode
+# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT STA is in VHT TXOP power save
+# mode
+#
+# +HTC-VHT Capable: [HTC-VHT]
+# Indicates whether or not the STA supports receiving a VHT variant HT Control
+# field.
+# 0 = Not supported (default)
+# 1 = supported
+#
+# Maximum A-MPDU Length Exponent: [MAX-A-MPDU-LEN-EXP0]..[MAX-A-MPDU-LEN-EXP7]
+# Indicates the maximum length of A-MPDU pre-EOF padding that the STA can recv
+# This field is an integer in the range of 0 to 7.
+# The length defined by this field is equal to
+# 2 pow(13 + Maximum A-MPDU Length Exponent) -1 octets
+#
+# VHT Link Adaptation Capable: [VHT-LINK-ADAPT2] [VHT-LINK-ADAPT3]
+# Indicates whether or not the STA supports link adaptation using VHT variant
+# HT Control field
+# If +HTC-VHTcapable is 1
+# 0 = (no feedback) if the STA does not provide VHT MFB (default)
+# 1 = reserved
+# 2 = (Unsolicited) if the STA provides only unsolicited VHT MFB
+# 3 = (Both) if the STA can provide VHT MFB in response to VHT MRQ and if the
+# STA provides unsolicited VHT MFB
+# Reserved if +HTC-VHTcapable is 0
+#
+# Rx Antenna Pattern Consistency: [RX-ANTENNA-PATTERN]
+# Indicates the possibility of Rx antenna pattern change
+# 0 = Rx antenna pattern might change during the lifetime of an association
+# 1 = Rx antenna pattern does not change during the lifetime of an association
+#
+# Tx Antenna Pattern Consistency: [TX-ANTENNA-PATTERN]
+# Indicates the possibility of Tx antenna pattern change
+# 0 = Tx antenna pattern might change during the lifetime of an association
+# 1 = Tx antenna pattern does not change during the lifetime of an association
+{% if cap_vht %}
+vht_capab=
+{%- endif -%}
+
+{%- if cap_vht_max_mpdu -%}
+[MAX-MPDU-{{ cap_vht_max_mpdu }}]
+{%- endif -%}
+
+{%- if cap_vht_max_mpdu_exp -%}
+[MAX-A-MPDU-LEN-EXP{{ cap_vht_max_mpdu_exp }}]
+{%- endif -%}
+
+{%- if cap_vht_chan_set_width -%}
+{%- if '2' in cap_vht_chan_set_width -%}
+[VHT160]
+{%- elif '3' in cap_vht_chan_set_width -%}
+[VHT160-80PLUS80]
+{%- endif -%}
+{%- endif -%}
+
+{%- if cap_vht_stbc_tx -%}
+[TX-STBC-2BY1]
+{%- endif -%}
+
+{%- if cap_vht_stbc_rx -%}
+[RX-STBC-{{ cap_vht_stbc_rx }}]
+{%- endif -%}
+
+{%- if cap_vht_link_adaptation -%}
+{%- if 'unsolicited' in cap_vht_link_adaptation -%}
+[VHT-LINK-ADAPT2]
+{%- elif 'both' in cap_vht_link_adaptation -%}
+[VHT-LINK-ADAPT3]
+{%- endif -%}
+{%- endif -%}
+
+{%- if cap_vht_short_gi -%}
+{%- for gi in cap_vht_short_gi -%}
+[SHORT-GI-{{ gi }}]
+{%- endfor -%}
+{%- endif -%}
+
+{%- if cap_vht_ldpc -%}
+[RXLDPC]
+{%- endif -%}
+
+{%- if cap_vht_tx_powersave -%}
+[VHT-TXOP-PS]
+{%- endif -%}
+
+{%- if cap_vht_vht_cf -%}
+[HTC-VHT]
+{%- endif -%}
+
+{%- if cap_vht_beamform -%}
+{%- for beamform in cap_vht_beamform -%}
+{%- if 'single-user-beamformer' in beamform -%}
+[SU-BEAMFORMER]
+{%- elif 'single-user-beamformee' in beamform -%}
+[SU-BEAMFORMEE]
+{%- elif 'multi-user-beamformer' in beamform -%}
+[MU-BEAMFORMER]
+{%- elif 'multi-user-beamformee' in beamform -%}
+[MU-BEAMFORMEE]
+{%- endif -%}
+{%- endfor -%}
+{%- endif -%}
+
+{%- if cap_vht_antenna_fixed -%}
+[RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN]
+{%- endif -%}
+
+{%- if cap_vht_antenna_cnt -%}
+{%- if cap_vht_antenna_cnt|int > 1 -%}
+{%- if cap_vht_beamform -%}
+{%- for beamform in cap_vht_beamform -%}
+{%- if 'single-user-beamformer' in beamform -%}
+{%- if cap_vht_antenna_cnt|int < 6 -%}
+[BF-ANTENNA-{{ cap_vht_antenna_cnt|int -1 }}][SOUNDING-DIMENSION-{{ cap_vht_antenna_cnt|int -1}}]
+{%- endif -%}
+{%- else -%}
+{%- if cap_vht_antenna_cnt|int < 5 -%}
+[BF-ANTENNA-{{ cap_vht_antenna_cnt }}][SOUNDING-DIMENSION-{{ cap_vht_antenna_cnt }}]
+{%- endif -%}
+{%- endif -%}
+{%- endfor -%}
+{%- else -%}
+{%- if cap_vht_antenna_cnt|int < 5 -%}
+[BF-ANTENNA-{{ cap_vht_antenna_cnt }}][SOUNDING-DIMENSION-{{ cap_vht_antenna_cnt }}]
+{%- endif -%}
+{%- endif -%}
+{%- endif -%}
+{%- endif %}
+
+# ieee80211n: Whether IEEE 802.11n (HT) is enabled
+# 0 = disabled (default)
+# 1 = enabled
+# Note: You will also need to enable WMM for full HT functionality.
+# Note: hw_mode=g (2.4 GHz) and hw_mode=a (5 GHz) is used to specify the band.
+{% if cap_req_vht -%}
+ieee80211n=0
+# Require stations to support VHT PHY (reject association if they do not)
+require_vht=1
+{% endif %}
+
+{% if cap_vht_center_freq_1 -%}
+# center freq = 5 GHz + (5 * index)
+# So index 42 gives center freq 5.210 GHz
+# which is channel 42 in 5G band
+vht_oper_centr_freq_seg0_idx={{ cap_vht_center_freq_1 }}
+{% endif %}
+
+{% if cap_vht_center_freq_2 -%}
+# center freq = 5 GHz + (5 * index)
+# So index 159 gives center freq 5.795 GHz
+# which is channel 159 in 5G band
+vht_oper_centr_freq_seg1_idx={{ cap_vht_center_freq_2 }}
+{% endif %}
+
+{% if disable_broadcast_ssid -%}
+# Send empty SSID in beacons and ignore probe request frames that do not
+# specify full SSID, i.e., require stations to know SSID.
+# default: disabled (0)
+# 1 = send empty (length=0) SSID in beacon and ignore probe request for
+# broadcast SSID
+# 2 = clear SSID (ASCII 0), but keep the original length (this may be required
+# with some clients that do not support empty SSID) and ignore probe
+# requests for broadcast SSID
+ignore_broadcast_ssid=1
+{% endif %}
+
+# Station MAC address -based authentication
+# Please note that this kind of access control requires a driver that uses
+# hostapd to take care of management frame processing and as such, this can be
+# used with driver=hostap or driver=nl80211, but not with driver=atheros.
+# 0 = accept unless in deny list
+# 1 = deny unless in accept list
+# 2 = use external RADIUS server (accept/deny lists are searched first)
+macaddr_acl=0
+
+{% if max_stations -%}
+# Maximum number of stations allowed in station table. New stations will be
+# rejected after the station table is full. IEEE 802.11 has a limit of 2007
+# different association IDs, so this number should not be larger than that.
+# (default: 2007)
+max_num_sta={{ max_stations }}
+{% endif %}
+
+{% if isolate_stations -%}
+# Client isolation can be used to prevent low-level bridging of frames between
+# associated stations in the BSS. By default, this bridging is allowed.
+ap_isolate=1
+{% endif %}
+
+{% if reduce_transmit_power -%}
+# Add Power Constraint element to Beacon and Probe Response frames
+# This config option adds Power Constraint element when applicable and Country
+# element is added. Power Constraint element is required by Transmit Power
+# Control. This can be used only with ieee80211d=1.
+# Valid values are 0..255.
+local_pwr_constraint={{ reduce_transmit_power }}
+{% endif %}
+
+{% if expunge_failing_stations -%}
+# Disassociate stations based on excessive transmission failures or other
+# indications of connection loss. This depends on the driver capabilities and
+# may not be available with all drivers.
+disassoc_low_ack=1
+{% endif %}
+
+{% if sec_wep -%}
+# IEEE 802.11 specifies two authentication algorithms. hostapd can be
+# configured to allow both of these or only one. Open system authentication
+# should be used with IEEE 802.1X.
+# Bit fields of allowed authentication algorithms:
+# bit 0 = Open System Authentication
+# bit 1 = Shared Key Authentication (requires WEP)
+auth_algs=2
+
+# WEP rekeying (disabled if key lengths are not set or are set to 0)
+# Key lengths for default/broadcast and individual/unicast keys:
+# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits)
+# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits)
+wep_key_len_broadcast=5
+wep_key_len_unicast=5
+
+# Static WEP key configuration
+#
+# The key number to use when transmitting.
+# It must be between 0 and 3, and the corresponding key must be set.
+# default: not set
+wep_default_key=0
+
+# The WEP keys to use.
+# A key may be a quoted string or unquoted hexadecimal digits.
+# The key length should be 5, 13, or 16 characters, or 10, 26, or 32
+# digits, depending on whether 40-bit (64-bit), 104-bit (128-bit), or
+# 128-bit (152-bit) WEP is used.
+# Only the default key must be supplied; the others are optional.
+{% if sec_wep_key -%}
+{% for key in sec_wep_key -%}
+wep_key{{ loop.index -1 }}={{ key}}
+{% endfor %}
+{%- endif %}
+
+{% elif sec_wpa -%}
+##### WPA/IEEE 802.11i configuration ##########################################
+
+# Enable WPA. Setting this variable configures the AP to require WPA (either
+# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
+# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
+# Instead of wpa_psk / wpa_passphrase, wpa_psk_radius might suffice.
+# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
+# RADIUS authentication server must be configured, and WPA-EAP must be included
+# in wpa_key_mgmt.
+# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0)
+# and/or WPA2 (full IEEE 802.11i/RSN):
+# bit0 = WPA
+# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled)
+{% if 'both' in sec_wpa_mode -%}
+wpa=3
+{%- elif 'wpa2' in sec_wpa_mode -%}
+wpa=2
+{%- elif 'wpa' in sec_wpa_mode -%}
+wpa=1
+{%- endif %}
+
+{% if sec_wpa_cipher -%}
+# Set of accepted cipher suites (encryption algorithms) for pairwise keys
+# (unicast packets). This is a space separated list of algorithms:
+# CCMP = AES in Counter mode with CBC-MAC (CCMP-128)
+# TKIP = Temporal Key Integrity Protocol
+# CCMP-256 = AES in Counter mode with CBC-MAC with 256-bit key
+# GCMP = Galois/counter mode protocol (GCMP-128)
+# GCMP-256 = Galois/counter mode protocol with 256-bit key
+# Group cipher suite (encryption algorithm for broadcast and multicast frames)
+# is automatically selected based on this configuration. If only CCMP is
+# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise,
+# TKIP will be used as the group cipher. The optional group_cipher parameter can
+# be used to override this automatic selection.
+{% if 'wpa2' in sec_wpa_mode -%}
+# Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value)
+rsn_pairwise={{ sec_wpa_cipher | join(" ") }}
+{% else -%}
+# Pairwise cipher for WPA (v1) (default: TKIP)
+wpa_pairwise={{ sec_wpa_cipher | join(" ") }}
+{%- endif -%}
+{% endif %}
+
+{% if sec_wpa_passphrase -%}
+# IEEE 802.11 specifies two authentication algorithms. hostapd can be
+# configured to allow both of these or only one. Open system authentication
+# should be used with IEEE 802.1X.
+# Bit fields of allowed authentication algorithms:
+# bit 0 = Open System Authentication
+# bit 1 = Shared Key Authentication (requires WEP)
+auth_algs=1
+
+# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
+# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
+# (8..63 characters) that will be converted to PSK. This conversion uses SSID
+# so the PSK changes when ASCII passphrase is used and the SSID is changed.
+wpa_passphrase={{ sec_wpa_passphrase }}
+
+# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
+# entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
+# added to enable SHA256-based stronger algorithms.
+# WPA-PSK = WPA-Personal / WPA2-Personal
+# WPA-PSK-SHA256 = WPA2-Personal using SHA256
+wpa_key_mgmt=WPA-PSK
+
+{% elif sec_wpa_radius -%}
+##### IEEE 802.1X-2004 related configuration ##################################
+# Require IEEE 802.1X authorization
+ieee8021x=1
+
+# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
+# entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
+# added to enable SHA256-based stronger algorithms.
+# WPA-EAP = WPA-Enterprise / WPA2-Enterprise
+# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256
+wpa_key_mgmt=WPA-EAP
+
+{% if sec_wpa_radius_source -%}
+# RADIUS client forced local IP address for the access point
+# Normally the local IP address is determined automatically based on configured
+# IP addresses, but this field can be used to force a specific address to be
+# used, e.g., when the device has multiple IP addresses.
+radius_client_addr={{ sec_wpa_radius_source }}
+
+# The own IP address of the access point (used as NAS-IP-Address)
+own_ip_addr={{ sec_wpa_radius_source }}
+{% else %}
+# The own IP address of the access point (used as NAS-IP-Address)
+own_ip_addr=127.0.0.1
+{% endif %}
+
+{% for radius in sec_wpa_radius -%}
+{%- if not radius.disabled -%}
+# RADIUS authentication server
+auth_server_addr={{ radius.server }}
+auth_server_port={{ radius.port }}
+auth_server_shared_secret={{ radius.key }}
+{% if radius.acc_port -%}
+# RADIUS accounting server
+acct_server_addr={{ radius.server }}
+acct_server_port={{ radius.acc_port }}
+acct_server_shared_secret={{ radius.key }}
+{% endif %}
+{% endif %}
+{% endfor %}
+
+{% endif %}
+
+{% else %}
+# Open system
+auth_algs=1
+{% endif %}
+
+# TX queue parameters (EDCF / bursting)
+# tx_queue_<queue name>_<param>
+# queues: data0, data1, data2, data3
+# (data0 is the highest priority queue)
+# parameters:
+# aifs: AIFS (default 2)
+# cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191,
+# 16383, 32767)
+# cwmax: cwMax (same values as cwMin, cwMax >= cwMin)
+# burst: maximum length (in milliseconds with precision of up to 0.1 ms) for
+# bursting
+#
+# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
+# These parameters are used by the access point when transmitting frames
+# to the clients.
+#
+# Low priority / AC_BK = background
+tx_queue_data3_aifs=7
+tx_queue_data3_cwmin=15
+tx_queue_data3_cwmax=1023
+tx_queue_data3_burst=0
+# Note: for IEEE 802.11b mode: cWmin=31 cWmax=1023 burst=0
+#
+# Normal priority / AC_BE = best effort
+tx_queue_data2_aifs=3
+tx_queue_data2_cwmin=15
+tx_queue_data2_cwmax=63
+tx_queue_data2_burst=0
+# Note: for IEEE 802.11b mode: cWmin=31 cWmax=127 burst=0
+#
+# High priority / AC_VI = video
+tx_queue_data1_aifs=1
+tx_queue_data1_cwmin=7
+tx_queue_data1_cwmax=15
+tx_queue_data1_burst=3.0
+# Note: for IEEE 802.11b mode: cWmin=15 cWmax=31 burst=6.0
+#
+# Highest priority / AC_VO = voice
+tx_queue_data0_aifs=1
+tx_queue_data0_cwmin=3
+tx_queue_data0_cwmax=7
+tx_queue_data0_burst=1.5
+
+# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
+# for 802.11a or 802.11g networks
+# These parameters are sent to WMM clients when they associate.
+# The parameters will be used by WMM clients for frames transmitted to the
+# access point.
+#
+# note - txop_limit is in units of 32microseconds
+# note - acm is admission control mandatory flag. 0 = admission control not
+# required, 1 = mandatory
+# note - Here cwMin and cmMax are in exponent form. The actual cw value used
+# will be (2^n)-1 where n is the value given here. The allowed range for these
+# wmm_ac_??_{cwmin,cwmax} is 0..15 with cwmax >= cwmin.
+#
+wmm_enabled=1
+
+# Low priority / AC_BK = background
+wmm_ac_bk_cwmin=4
+wmm_ac_bk_cwmax=10
+wmm_ac_bk_aifs=7
+wmm_ac_bk_txop_limit=0
+wmm_ac_bk_acm=0
+# Note: for IEEE 802.11b mode: cWmin=5 cWmax=10
+#
+# Normal priority / AC_BE = best effort
+wmm_ac_be_aifs=3
+wmm_ac_be_cwmin=4
+wmm_ac_be_cwmax=10
+wmm_ac_be_txop_limit=0
+wmm_ac_be_acm=0
+# Note: for IEEE 802.11b mode: cWmin=5 cWmax=7
+#
+# High priority / AC_VI = video
+wmm_ac_vi_aifs=2
+wmm_ac_vi_cwmin=3
+wmm_ac_vi_cwmax=4
+wmm_ac_vi_txop_limit=94
+wmm_ac_vi_acm=0
+# Note: for IEEE 802.11b mode: cWmin=4 cWmax=5 txop_limit=188
+#
+# Highest priority / AC_VO = voice
+wmm_ac_vo_aifs=2
+wmm_ac_vo_cwmin=2
+wmm_ac_vo_cwmax=3
+wmm_ac_vo_txop_limit=47
+wmm_ac_vo_acm=0
+
diff --git a/data/templates/wifi/wpa_supplicant.conf.tmpl b/data/templates/wifi/wpa_supplicant.conf.tmpl
new file mode 100644
index 000000000..2784883f1
--- /dev/null
+++ b/data/templates/wifi/wpa_supplicant.conf.tmpl
@@ -0,0 +1,9 @@
+# WPA supplicant config
+network={
+ ssid="{{ ssid }}"
+{%- if sec_wpa_passphrase %}
+ psk="{{ sec_wpa_passphrase }}"
+{% else %}
+ key_mgmt=NONE
+{% endif %}
+}
diff --git a/data/templates/wwan/chat.tmpl b/data/templates/wwan/chat.tmpl
new file mode 100644
index 000000000..a3395c057
--- /dev/null
+++ b/data/templates/wwan/chat.tmpl
@@ -0,0 +1,6 @@
+ABORT 'NO DIAL TONE' ABORT 'NO ANSWER' ABORT 'NO CARRIER' ABORT DELAYED
+'' AT
+OK ATZ
+OK 'AT+CGDCONT=1,"IP","{{ apn }}"'
+OK ATD*99#
+CONNECT ''
diff --git a/data/templates/wwan/ip-down.script.tmpl b/data/templates/wwan/ip-down.script.tmpl
new file mode 100644
index 000000000..194f8d863
--- /dev/null
+++ b/data/templates/wwan/ip-down.script.tmpl
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+tty=$2
+
+# Only applicable for Wireless Modems (WWAN)
+if [ -z "$(echo $tty | egrep "tty(USB|ACM)")" ]; then
+ exit 0
+fi
+
+# Determine if we are enslaved to a VRF, this is needed to properly insert
+# the default route
+VRF_NAME=""
+if [ -d /sys/class/net/{{ intf }}/upper_* ]; then
+ # Determine upper (VRF) interface
+ VRF=$(basename $(ls -d /sys/class/net/{{ intf }}/upper_*))
+ # Remove upper_ prefix from result string
+ VRF=${VRF#"upper_"}
+ # Populate variable to run in VR context
+ VRF_NAME=" -c vrf ${VRF_NAME} "
+fi
+
+# Remove default route to either default or VRF routing table
+vtysh -c "conf t" ${VRF_NAME} -c "no ip route 0.0.0.0/0 {{ intf }} {{ metric }}"
+
+DIALER_PID=$(cat /var/run/{{ intf }}.pid)
+logger -t pppd[$DIALER_PID] "removed default route via {{ intf }} metric {{ metric }}"
diff --git a/data/templates/wwan/ip-pre-up.script.tmpl b/data/templates/wwan/ip-pre-up.script.tmpl
new file mode 100644
index 000000000..7a17a1c71
--- /dev/null
+++ b/data/templates/wwan/ip-pre-up.script.tmpl
@@ -0,0 +1,23 @@
+#!/bin/sh
+# As WWAN is an "on demand" interface we need to re-configure it when it
+# becomes 'up'
+
+ipparam=$6
+
+# device name and metric are received using ipparam
+device=`echo "$ipparam"|awk '{ print $1 }'`
+
+if [ "$device" != "{{ intf }}" ]; then
+ exit
+fi
+
+# add some info to syslog
+DIALER_PID=$(cat /var/run/{{ intf }}.pid)
+logger -t pppd[$DIALER_PID] "executing $0"
+
+echo "{{ description }}" > /sys/class/net/{{ intf }}/ifalias
+
+{% if vrf -%}
+logger -t pppd[$DIALER_PID] "configuring interface {{ intf }} for VRF {{ vrf }}"
+ip link set dev {{ intf }} master {{ vrf }}
+{% endif %}
diff --git a/data/templates/wwan/ip-up.script.tmpl b/data/templates/wwan/ip-up.script.tmpl
new file mode 100644
index 000000000..89e42a23a
--- /dev/null
+++ b/data/templates/wwan/ip-up.script.tmpl
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+tty=$2
+
+# Only applicable for Wireless Modems (WWAN)
+if [ -z "$(echo $tty | egrep "tty(USB|ACM)")" ]; then
+ exit 0
+fi
+
+DIALER_PID=$(cat /var/run/{{ intf }}.pid)
+
+# Determine if we are enslaved to a VRF, this is needed to properly insert
+# the default route
+VRF_NAME=""
+if [ -d /sys/class/net/{{ intf }}/upper_* ]; then
+ # Determine upper (VRF) interface
+ VRF=$(basename $(ls -d /sys/class/net/{{ intf }}/upper_*))
+ # Remove upper_ prefix from result string
+ VRF=${VRF#"upper_"}
+ VRF_NAME="vrf ${VRF}"
+fi
+
+# Apply default route to either default or VRF routing table
+vtysh -c "conf t" -c "ip route 0.0.0.0/0 {{ intf }} ${VRF_NAME} {{ metric }}"
+logger -t pppd[$DIALER_PID] "added default route via {{ intf }} metric {{ metric }} ${VRF_NAME}"
diff --git a/data/templates/wwan/peer.tmpl b/data/templates/wwan/peer.tmpl
new file mode 100644
index 000000000..04ab4f844
--- /dev/null
+++ b/data/templates/wwan/peer.tmpl
@@ -0,0 +1,30 @@
+### Autogenerated by interfaces-wirelessmodem.py ###
+
+{% if description %}
+# {{ description }}
+{% endif %}
+ifname {{ intf }}
+ipparam {{ intf }}
+linkname {{ intf }}
+{% if name_server -%}
+usepeerdns
+{%- endif %}
+# physical device
+/dev/{{ device }}
+lcp-echo-failure 0
+115200
+debug
+logfile {{ logfile }}
+nodefaultroute
+ipcp-max-failure 4
+ipcp-accept-local
+ipcp-accept-remote
+noauth
+crtscts
+lock
+persist
+{% if on_demand -%}
+demand
+{%- endif %}
+
+connect '/usr/sbin/chat -v -t6 -f {{ chat_script }}'
diff --git a/interface-definitions/flow-accounting-conf.xml.in b/interface-definitions/flow-accounting-conf.xml.in
index 5cf866b8e..239269235 100644
--- a/interface-definitions/flow-accounting-conf.xml.in
+++ b/interface-definitions/flow-accounting-conf.xml.in
@@ -217,7 +217,7 @@
</valueHelp>
<valueHelp>
<format>10</format>
- <description>IPFIX</description>
+ <description>Internet Protocol Flow Information Export (IPFIX)</description>
</valueHelp>
</properties>
</leafNode>
diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in
index bc7995918..d69e0b42c 100644
--- a/interface-definitions/interfaces-pppoe.xml.in
+++ b/interface-definitions/interfaces-pppoe.xml.in
@@ -8,12 +8,11 @@
<priority>321</priority>
<constraint>
<regex>^pppoe[0-9]+$</regex>
- <validator name="numeric" argument="--range 1-99"/>
</constraint>
<constraintErrorMessage>PPPoE interface must be named pppoeN</constraintErrorMessage>
<valueHelp>
<format>pppoeN</format>
- <description>PPPoE interface name (1-15)</description>
+ <description>PPPoE dialer interface name</description>
</valueHelp>
</properties>
<children>
diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in
index d461156b3..d3f084774 100644
--- a/interface-definitions/interfaces-wireguard.xml.in
+++ b/interface-definitions/interfaces-wireguard.xml.in
@@ -21,9 +21,13 @@
#include <include/interface-disable.xml.i>
<leafNode name="port">
<properties>
- <help>Local port number to accept connections</help>
+ <help>Local port to listen for incoming connections</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>Numeric IP port</description>
+ </valueHelp>
<constraint>
- <validator name="numeric" argument="--range 1024-65535"/>
+ <validator name="numeric" argument="--range 1-65535"/>
</constraint>
</properties>
</leafNode>
@@ -97,10 +101,28 @@
<multi/>
</properties>
</leafNode>
- <!-- eventually check format IP:port -->
- <leafNode name="endpoint">
+ <leafNode name="address">
+ <properties>
+ <help>IP address of tunnel remote end</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IP address to listen for incoming connections</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="port">
<properties>
- <help>Remote endpoint (IP:port)</help>
+ <help>Port number on tunnel remote end</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>Numeric IP port</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
</properties>
</leafNode>
<leafNode name="persistent-keepalive">
diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in
index 12736510b..194669f77 100644
--- a/interface-definitions/interfaces-wireless.xml.in
+++ b/interface-definitions/interfaces-wireless.xml.in
@@ -208,11 +208,11 @@
<properties>
<help>Number of antennas on this card</help>
<valueHelp>
- <format>1-9</format>
+ <format>1-8</format>
<description>Number of antennas for this card</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 1-9"/>
+ <validator name="numeric" argument="--range 1-8"/>
</constraint>
</properties>
</leafNode>
@@ -320,7 +320,7 @@
<properties>
<help>VHT link adaptation capabilities</help>
<completionHelp>
- <list>single-user-beamformer single-user-beamformee multi-user-beamformer multi-user-beamformee</list>
+ <list>unsolicited both</list>
</completionHelp>
<valueHelp>
<format>unsolicited</format>
diff --git a/interface-definitions/interfaces-wirelessmodem.xml.in b/interface-definitions/interfaces-wirelessmodem.xml.in
index 2ec9205e0..6bec34b56 100644
--- a/interface-definitions/interfaces-wirelessmodem.xml.in
+++ b/interface-definitions/interfaces-wirelessmodem.xml.in
@@ -31,7 +31,7 @@
<help>Distance backup default route</help>
<valueHelp>
<format>1-255</format>
- <description>Distance of the backup route (default: 10) </description>
+ <description>Distance of the backup route (default: 10)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-255"/>
diff --git a/interface-definitions/protocols-igmp.xml.in b/interface-definitions/protocols-igmp.xml.in
index a6a2f1420..a9b11e1a3 100644
--- a/interface-definitions/protocols-igmp.xml.in
+++ b/interface-definitions/protocols-igmp.xml.in
@@ -16,6 +16,33 @@
</completionHelp>
</properties>
<children>
+ <tagNode name="join">
+ <properties>
+ <help>IGMP join multicast group</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Multicast group address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="source">
+ <properties>
+ <help>Source address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Source address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
<leafNode name="version">
<properties>
<help>IGMP version</help>
diff --git a/interface-definitions/protocols-pim.xml.in b/interface-definitions/protocols-pim.xml.in
index 944224f7b..6152045a7 100644
--- a/interface-definitions/protocols-pim.xml.in
+++ b/interface-definitions/protocols-pim.xml.in
@@ -17,6 +17,18 @@
</completionHelp>
</properties>
<children>
+ <leafNode name="dr-priority">
+ <properties>
+ <help>Designated Router Election Priority</help>
+ <valueHelp>
+ <format>1-4294967295</format>
+ <description>Value of the new DR Priority</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967295"/>
+ </constraint>
+ </properties>
+ </leafNode>
<leafNode name="hello">
<properties>
<help>Hello Interval</help>
diff --git a/interface-definitions/vrrp.xml.in b/interface-definitions/vrrp.xml.in
index 89d22f79f..120c7d218 100644
--- a/interface-definitions/vrrp.xml.in
+++ b/interface-definitions/vrrp.xml.in
@@ -254,6 +254,45 @@
</completionHelp>
</properties>
</leafNode>
+ <node name="transition-script">
+ <properties>
+ <help>VRRP transition scripts</help>
+ </properties>
+ <children>
+ <leafNode name="master">
+ <properties>
+ <help>Script to run on VRRP state transition to master</help>
+ <constraint>
+ <validator name="script"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="backup">
+ <properties>
+ <help>Script to run on VRRP state transition to backup</help>
+ <constraint>
+ <validator name="script"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="fault">
+ <properties>
+ <help>Script to run on VRRP state transition to fault</help>
+ <constraint>
+ <validator name="script"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="stop">
+ <properties>
+ <help>Script to run on VRRP state transition to stop</help>
+ <constraint>
+ <validator name="script"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
</children>
</tagNode>
</children>
diff --git a/python/vyos/ifconfig/control.py b/python/vyos/ifconfig/control.py
index c1a073aef..c7a2fa2d6 100644
--- a/python/vyos/ifconfig/control.py
+++ b/python/vyos/ifconfig/control.py
@@ -15,8 +15,9 @@
import os
-from subprocess import Popen, PIPE, STDOUT
+from vyos.util import debug, debug_msg
+from vyos.util import popen, cmd
from vyos.ifconfig.register import Register
@@ -32,27 +33,18 @@ class Control(Register):
# to prevent this, debugging can be explicitely disabled
# if debug is not explicitely disabled the the config, enable it
- self.debug = kargs.get('debug', True)
+ self.debug = ''
+ if kargs.get('debug', True):
+ self.debug = debug('ifconfig')
- def _debug_msg(self, msg):
- if os.path.isfile('/tmp/vyos.ifconfig.debug') and self.debug:
- print('DEBUG/{:<6} {}'.format(self.config['ifname'], msg))
+ def _debug_msg (self, message):
+ return debug_msg(message, self.debug)
def _popen(self, command):
- p = Popen(command, stdout=PIPE, stderr=STDOUT, shell=True)
- tmp = p.communicate()[0].strip()
- self._debug_msg(f"cmd '{command}'")
- decoded = tmp.decode()
- if decoded:
- self._debug_msg(f"returned:\n{decoded}")
- return decoded, p.returncode
+ return popen(command, self.debug)
def _cmd(self, command):
- decoded, code = self._popen(command)
- if code != 0:
- # error code can be recovered with .errno
- raise OSError(code, f'{command}\nreturned: {decoded}')
- return decoded
+ return cmd(command, self.debug)
def _get_command(self, config, name):
"""
@@ -79,6 +71,10 @@ class Control(Register):
if convert:
value = convert(value)
+ possible = self._command_set[name].get('possible', None)
+ if possible and not possible(config['ifname'], value):
+ return False
+
config = {**config, **{'value': value}}
cmd = self._command_set[name]['shellcmd'].format(**config)
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index 50552dc71..c18d2e72f 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -18,7 +18,7 @@ import re
from vyos.ifconfig.interface import Interface
from vyos.ifconfig.vlan import VLAN
-
+from vyos.util import popen
from vyos.validate import *
@@ -43,27 +43,36 @@ class EthernetIf(Interface):
}
}
+ @staticmethod
+ def feature(ifname, option, value):
+ out, code = popen(f'/sbin/ethtool -K {ifname} {option} {value}','ifconfig')
+ return False
_command_set = {**Interface._command_set, **{
'gro': {
'validate': lambda v: assert_list(v, ['on', 'off']),
- 'shellcmd': '/sbin/ethtool -K {ifname} gro {value}',
+ 'possible': lambda i, v: EthernetIf.feature(i, 'gro', v),
+ # 'shellcmd': '/sbin/ethtool -K {ifname} gro {value}',
},
'gso': {
'validate': lambda v: assert_list(v, ['on', 'off']),
- 'shellcmd': '/sbin/ethtool -K {ifname} gso {value}',
+ 'possible': lambda i, v: EthernetIf.feature(i, 'gso', v),
+ # 'shellcmd': '/sbin/ethtool -K {ifname} gso {value}',
},
'sg': {
'validate': lambda v: assert_list(v, ['on', 'off']),
- 'shellcmd': '/sbin/ethtool -K {ifname} sg {value}',
+ 'possible': lambda i, v: EthernetIf.feature(i, 'sg', v),
+ # 'shellcmd': '/sbin/ethtool -K {ifname} sg {value}',
},
'tso': {
'validate': lambda v: assert_list(v, ['on', 'off']),
- 'shellcmd': '/sbin/ethtool -K {ifname} tso {value}',
+ 'possible': lambda i, v: EthernetIf.feature(i, 'tso', v),
+ # 'shellcmd': '/sbin/ethtool -K {ifname} tso {value}',
},
'ufo': {
'validate': lambda v: assert_list(v, ['on', 'off']),
- 'shellcmd': '/sbin/ethtool -K {ifname} ufo {value}',
+ 'possible': lambda i, v: EthernetIf.feature(i, 'ufo', v),
+ # 'shellcmd': '/sbin/ethtool -K {ifname} ufo {value}',
},
}}
@@ -245,8 +254,4 @@ class EthernetIf(Interface):
>>> i = EthernetIf('eth0')
>>> i.set_udp_offload('on')
"""
- if state not in ['on', 'off']:
- raise ValueError('state must be "on" or "off"')
-
- cmd = '/sbin/ethtool -K {} ufo {}'.format(self.config['ifname'], state)
- return self._cmd(cmd)
+ return self.set_interface('ufo', state)
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 4a34f96d6..96057a943 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -39,6 +39,8 @@ class Interface(DHCP):
required = []
default = {
'type': '',
+ 'debug': True,
+ 'create': True,
}
definition = {
'section': '',
@@ -174,15 +176,26 @@ class Interface(DHCP):
super().__init__(ifname, **kargs)
if not os.path.exists('/sys/class/net/{}'.format(self.config['ifname'])):
+ # Any instance of Interface, such as Interface('eth0')
+ # can be used safely to access the generic function in this class
+ # as 'type' is unset, the class can not be created
if not self.config['type']:
raise Exception('interface "{}" not found'.format(self.config['ifname']))
- for k in self.required:
- if k not in kargs:
- name = self.default['type']
- raise ConfigError(f'missing required option {k} for {name} {ifname} creation')
-
- self._create()
+ # Should an Instance of a child class (EthernetIf, DummyIf, ..)
+ # be required, then create should be set to False to not accidentally create it.
+ # In case a subclass does not define it, we use get to set the default to True
+ if self.config.get('create',True):
+ for k in self.required:
+ if k not in kargs:
+ name = self.default['type']
+ raise ConfigError(f'missing required option {k} for {name} {ifname} creation')
+
+ self._create()
+ # If we can not connect to the interface then let the caller know
+ # as the class could not be correctly initialised
+ else:
+ raise Exception('interface "{}" not found'.format(self.config['ifname']))
# list of assigned IP addresses
self._addr = []
diff --git a/python/vyos/ifconfig/l2tpv3.py b/python/vyos/ifconfig/l2tpv3.py
index f0d64a53d..34147eb38 100644
--- a/python/vyos/ifconfig/l2tpv3.py
+++ b/python/vyos/ifconfig/l2tpv3.py
@@ -80,16 +80,14 @@ class L2TPv3If(Interface):
# interface is always A/D down. It needs to be enabled explicitly
self.set_admin_state('down')
- if self._config['tunnel_id'] and self._config['session_id']:
- cmd = 'ip l2tp del session tunnel_id {} '.format(
- self._config['tunnel_id'])
- cmd += 'session_id {} '.format(self._config['session_id'])
- self._cmd(cmd)
+ if self.config['tunnel_id'] and self.config['session_id']:
+ cmd = 'ip l2tp del session tunnel_id {tunnel_id}'
+ cmd += ' session_id {session_id}'
+ self._cmd(cmd.format(**self.config))
- if self._config['tunnel_id']:
- cmd = 'ip l2tp del tunnel tunnel_id {} '.format(
- self._config['tunnel_id'])
- self._cmd(cmd)
+ if self.config['tunnel_id']:
+ cmd = 'ip l2tp del tunnel tunnel_id {tunnel_id}'
+ self._cmd(cmd.format(**self.config))
@staticmethod
def get_config():
diff --git a/python/vyos/util.py b/python/vyos/util.py
index 3970b8bf1..c8ab43c11 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -16,6 +16,52 @@
import os
import re
import sys
+from subprocess import Popen, PIPE, STDOUT
+
+
+# debugging
+
+
+def debug(flag):
+ return flag if os.path.isfile(f'/tmp/vyos.{flag}.debug') else ''
+
+
+def debug_msg(message, section=''):
+ if section:
+ print(f'DEBUG/{section:<6} {message}')
+
+
+# commands
+
+
+def popen(command, section=''):
+ p = Popen(command, stdout=PIPE, stderr=STDOUT, shell=True)
+ tmp = p.communicate()[0].strip()
+ debug_msg(f"cmd '{command}'", section)
+ decoded = tmp.decode()
+ if decoded:
+ debug_msg(f"returned:\n{decoded}", section)
+ return decoded, p.returncode
+
+
+def cmd(command, section=''):
+ decoded, code = popen(command, section)
+ if code != 0:
+ # error code can be recovered with .errno
+ raise OSError(code, f'{command}\nreturned: {decoded}')
+ return decoded
+
+
+# file manipulation
+
+
+
+def subprocess_cmd(command):
+ """ execute arbitrary command via Popen """
+ from subprocess import Popen, PIPE
+ p = Popen(command, stdout=PIPE, shell=True)
+ p.communicate()
+
def read_file(path):
""" Read a file to string """
@@ -35,11 +81,11 @@ def chown_file(path, user, group):
os.chown(path, uid, gid)
-def chmod_x_file(path):
+def chmod_x(path):
""" make file executable """
from stat import S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH
- if os.path.isfile(path):
+ if os.path.exists(path):
bitmask = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | \
S_IROTH | S_IXOTH
os.chmod(path, bitmask)
diff --git a/src/conf_mode/accel_l2tp.py b/src/conf_mode/accel_l2tp.py
index a7af9cc68..77e1ee874 100755
--- a/src/conf_mode/accel_l2tp.py
+++ b/src/conf_mode/accel_l2tp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# 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
@@ -13,19 +13,18 @@
#
# 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 sys
import os
import re
import subprocess
-import jinja2
import socket
import time
-import syslog as sl
+
+from jinja2 import FileSystemLoader, Environment
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
pidfile = r'/var/run/accel_l2tp.pid'
@@ -34,526 +33,371 @@ chap_secrets = l2tp_cnf_dir + '/chap-secrets'
l2tp_conf = l2tp_cnf_dir + '/l2tp.config'
# accel-pppd -d -c /etc/accel-ppp/l2tp/l2tp.config -p /var/run/accel_l2tp.pid
-### config path creation
+# config path creation
if not os.path.exists(l2tp_cnf_dir):
- os.makedirs(l2tp_cnf_dir)
- sl.syslog(sl.LOG_NOTICE, l2tp_cnf_dir + " created")
-
-l2tp_config = '''
-### generated by accel_l2tp.py ###
-[modules]
-log_syslog
-l2tp
-chap-secrets
-{% for proto in authentication['auth_proto']: %}
-{{proto}}
-{% endfor%}
-{% if authentication['mode'] == 'radius' %}
-radius
-{% endif -%}
-ippool
-shaper
-ipv6pool
-ipv6_nd
-ipv6_dhcp
-
-[core]
-thread-count={{thread_cnt}}
-
-[log]
-syslog=accel-l2tp,daemon
-copy=1
-level=5
-
-{% if dns %}
-[dns]
-{% if dns[0] %}
-dns1={{dns[0]}}
-{% endif %}
-{% if dns[1] %}
-dns2={{dns[1]}}
-{% endif %}
-{% endif -%}
-
-{% if dnsv6 %}
-[ipv6-dns]
-{% for srv in dnsv6: %}
-{{srv}}
-{% endfor %}
-{% endif %}
-
-{% if wins %}
-[wins]
-{% if wins[0] %}
-wins1={{wins[0]}}
-{% endif %}
-{% if wins[1] %}
-wins2={{wins[1]}}
-{% endif %}
-{% endif -%}
-
-[l2tp]
-verbose=1
-ifname=l2tp%d
-ppp-max-mtu={{mtu}}
-mppe={{authentication['mppe']}}
-{% if outside_addr %}
-bind={{outside_addr}}
-{% endif %}
-{% if lns_shared_secret %}
-secret={{lns_shared_secret}}
-{% endif %}
-
-[client-ip-range]
-0.0.0.0/0
-
-{% if (client_ip_pool) or (client_ip_subnets) %}
-[ip-pool]
-{% if client_ip_pool %}
-{{client_ip_pool}}
-{% endif -%}
-{% if client_ip_subnets %}
-{% for sn in client_ip_subnets %}
-{{sn}}
-{% endfor -%}
-{% endif %}
-{% endif %}
-{% if gateway_address %}
-gw-ip-address={{gateway_address}}
-{% endif %}
-
-{% if authentication['mode'] == 'local' %}
-[chap-secrets]
-chap-secrets=/etc/accel-ppp/l2tp/chap-secrets
-{% if gateway_address %}
-gw-ip-address={{gateway_address}}
-{% endif %}
-{% endif %}
-
-[ppp]
-verbose=1
-check-ip=1
-single-session=replace
-{% if idle_timeout %}
-lcp-echo-timeout={{idle_timeout}}
-{% endif %}
-{% if ppp_options['lcp-echo-interval'] %}
-lcp-echo-interval={{ppp_options['lcp-echo-interval']}}
-{% else %}
-lcp-echo-interval=30
-{% endif %}
-{% if ppp_options['lcp-echo-failure'] %}
-lcp-echo-failure={{ppp_options['lcp-echo-failure']}}
-{% else %}
-lcp-echo-failure=3
-{% endif %}
-{% if ccp_disable %}
-ccp=0
-{% endif %}
-{% if client_ipv6_pool %}
-ipv6=allow
-{% endif %}
-
-{% if authentication['mode'] == 'radius' %}
-[radius]
-{% for rsrv in authentication['radiussrv']: %}
-server={{rsrv}},{{authentication['radiussrv'][rsrv]['secret']}},\
-req-limit={{authentication['radiussrv'][rsrv]['req-limit']}},\
-fail-time={{authentication['radiussrv'][rsrv]['fail-time']}}
-{% endfor %}
-{% if authentication['radiusopt']['timeout'] %}
-timeout={{authentication['radiusopt']['timeout']}}
-{% endif %}
-{% if authentication['radiusopt']['acct-timeout'] %}
-acct-timeout={{authentication['radiusopt']['acct-timeout']}}
-{% endif %}
-{% if authentication['radiusopt']['max-try'] %}
-max-try={{authentication['radiusopt']['max-try']}}
-{% endif %}
-{% if authentication['radiusopt']['nas-id'] %}
-nas-identifier={{authentication['radiusopt']['nas-id']}}
-{% endif %}
-{% if authentication['radius_source_address'] %}
-nas-ip-address={{authentication['radius_source_address']}}
-{% endif -%}
-{% if authentication['radiusopt']['dae-srv'] %}
-dae-server={{authentication['radiusopt']['dae-srv']['ip-addr']}}:\
-{{authentication['radiusopt']['dae-srv']['port']}},\
-{{authentication['radiusopt']['dae-srv']['secret']}}
-{% endif -%}
-gw-ip-address={{gateway_address}}
-verbose=1
-{% endif -%}
-
-{% if client_ipv6_pool %}
-[ipv6-pool]
-{% for prfx in client_ipv6_pool.prefix: %}
-{{prfx}}
-{% endfor %}
-{% for prfx in client_ipv6_pool.delegate_prefix: %}
-delegate={{prfx}}
-{% endfor %}
-{% endif %}
-
-{% if client_ipv6_pool['delegate_prefix'] %}
-[ipv6-dhcp]
-verbose=1
-{% endif %}
-
-{% if authentication['radiusopt']['shaper'] %}
-[shaper]
-verbose=1
-attr={{authentication['radiusopt']['shaper']['attr']}}
-{% if authentication['radiusopt']['shaper']['vendor'] %}
-vendor={{authentication['radiusopt']['shaper']['vendor']}}
-{% endif -%}
-{% endif %}
-
-[cli]
-tcp=127.0.0.1:2004
-sessions-columns=ifname,username,calling-sid,ip,{{ip6_column}}{{ip6_dp_column}}rate-limit,type,comp,state,rx-bytes,tx-bytes,uptime
-
-'''
-
-### l2tp chap secrets
-chap_secrets_conf = '''
-# username server password acceptable local IP addresses shaper
-{% for user in authentication['local-users'] %}
-{% if authentication['local-users'][user]['state'] == 'enabled' %}
-{% if (authentication['local-users'][user]['upload']) and (authentication['local-users'][user]['download']) %}
-{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}\t\
-{{authentication['local-users'][user]['download']}}/{{authentication['local-users'][user]['upload']}}
-{% else %}
-{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}
-{% endif %}
-{% endif %}
-{% endfor %}
-'''
+ os.makedirs(l2tp_cnf_dir)
###
# inline helper functions
###
# depending on hw and threads, daemon needs a little to start
# if it takes longer than 100 * 0.5 secs, exception is being raised
-# not sure if that's the best way to check it, but it worked so far quite well
+# not sure if that's the best way to check it, but it worked so far quite well
###
+
+
def chk_con():
- cnt = 0
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- while True:
- try:
- s.connect(("127.0.0.1", 2004))
- break
- except ConnectionRefusedError:
- time.sleep(0.5)
- cnt +=1
- if cnt == 100:
- raise("failed to start l2tp server")
- break
-
-### chap_secrets file if auth mode local
-def write_chap_secrets(c):
- tmpl = jinja2.Template(chap_secrets_conf, trim_blocks=True)
- chap_secrets_txt = tmpl.render(c)
- old_umask = os.umask(0o077)
- open(chap_secrets,'w').write(chap_secrets_txt)
- os.umask(old_umask)
- sl.syslog(sl.LOG_NOTICE, chap_secrets + ' written')
+ cnt = 0
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ while True:
+ try:
+ s.connect(("127.0.0.1", 2004))
+ break
+ except ConnectionRefusedError:
+ time.sleep(0.5)
+ cnt += 1
+ if cnt == 100:
+ raise("failed to start l2tp server")
+ break
+
def accel_cmd(cmd=''):
- if not cmd:
- return None
- try:
- ret = subprocess.check_output(['/usr/bin/accel-cmd','-p','2004',cmd]).decode().strip()
- return ret
- except:
- return 1
-
-###
+ if not cmd:
+ return None
+ try:
+ ret = subprocess.check_output(
+ ['/usr/bin/accel-cmd', '-p', '2004', cmd]).decode().strip()
+ return ret
+ except:
+ return 1
+
+###
# inline helper functions end
###
+
def get_config():
- c = Config()
- if not c.exists('vpn l2tp remote-access '):
- return None
-
- c.set_level('vpn l2tp remote-access')
- config_data = {
- 'authentication' : {
- 'mode' : 'local',
- 'local-users' : {
+ c = Config()
+ if not c.exists('vpn l2tp remote-access '):
+ return None
+
+ c.set_level('vpn l2tp remote-access')
+ config_data = {
+ 'authentication': {
+ 'mode': 'local',
+ 'local-users': {
+ },
+ 'radiussrv': {},
+ 'radiusopt': {},
+ 'auth_proto': [],
+ 'mppe': 'prefer'
},
- 'radiussrv' : {},
- 'radiusopt' : {},
- 'auth_proto' : [],
- 'mppe' : 'prefer'
- },
- 'outside_addr' : '',
- 'gateway_address' : '10.255.255.0',
- 'dns' : [],
- 'dnsv6' : [],
- 'wins' : [],
- 'client_ip_pool' : None,
- 'client_ip_subnets' : [],
- 'client_ipv6_pool' : {},
- 'mtu' : '1436',
- 'ip6_column' : '',
- 'ip6_dp_column' : '',
- 'ppp_options' : {},
- }
-
- ### general options ###
-
- if c.exists('dns-servers server-1'):
- config_data['dns'].append( c.return_value('dns-servers server-1'))
- if c.exists('dns-servers server-2'):
- config_data['dns'].append( c.return_value('dns-servers server-2'))
- if c.exists('dnsv6-servers'):
- for dns6_server in c.return_values('dnsv6-servers'):
- config_data['dnsv6'].append(dns6_server)
- if c.exists('wins-servers server-1'):
- config_data['wins'].append( c.return_value('wins-servers server-1'))
- if c.exists('wins-servers server-2'):
- config_data['wins'].append( c.return_value('wins-servers server-2'))
- if c.exists('outside-address'):
- config_data['outside_addr'] = c.return_value('outside-address')
-
- ### auth local
- if c.exists('authentication mode local'):
- if c.exists('authentication local-users username'):
- for usr in c.list_nodes('authentication local-users username'):
- config_data['authentication']['local-users'].update(
- {
- usr : {
- 'passwd' : '',
- 'state' : 'enabled',
- 'ip' : '*',
- 'upload' : None,
- 'download' : None
- }
- }
- )
-
- if c.exists('authentication local-users username ' + usr + ' password'):
- config_data['authentication']['local-users'][usr]['passwd'] = c.return_value('authentication local-users username ' + usr + ' password')
- if c.exists('authentication local-users username ' + usr + ' disable'):
- config_data['authentication']['local-users'][usr]['state'] = 'disable'
- if c.exists('authentication local-users username ' + usr + ' static-ip'):
- config_data['authentication']['local-users'][usr]['ip'] = c.return_value('authentication local-users username ' + usr + ' static-ip')
- if c.exists('authentication local-users username ' + usr + ' rate-limit download'):
- config_data['authentication']['local-users'][usr]['download'] = c.return_value('authentication local-users username ' + usr + ' rate-limit download')
- if c.exists('authentication local-users username ' + usr + ' rate-limit upload'):
- config_data['authentication']['local-users'][usr]['upload'] = c.return_value('authentication local-users username ' + usr + ' rate-limit upload')
-
- ### authentication mode radius servers and settings
-
- if c.exists('authentication mode radius'):
- config_data['authentication']['mode'] = 'radius'
- rsrvs = c.list_nodes('authentication radius server')
- for rsrv in rsrvs:
- if c.return_value('authentication radius server ' + rsrv + ' fail-time') == None:
- ftime = '0'
- else:
- ftime = str(c.return_value('authentication radius server ' + rsrv + ' fail-time'))
- if c.return_value('authentication radius-server ' + rsrv + ' req-limit') == None:
- reql = '0'
- else:
- reql = str(c.return_value('authentication radius server ' + rsrv + ' req-limit'))
-
- config_data['authentication']['radiussrv'].update(
- {
- rsrv : {
- 'secret' : c.return_value('authentication radius server ' + rsrv + ' key'),
- 'fail-time' : ftime,
- 'req-limit' : reql
- }
- }
- )
- ### Source ip address feature
- if c.exists('authentication radius source-address'):
- config_data['authentication']['radius_source_address'] = c.return_value('authentication radius source-address')
-
- #### advanced radius-setting
- if c.exists('authentication radius acct-timeout'):
- config_data['authentication']['radiusopt']['acct-timeout'] = c.return_value('authentication radius acct-timeout')
- if c.exists('authentication radius max-try'):
- config_data['authentication']['radiusopt']['max-try'] = c.return_value('authentication radius max-try')
- if c.exists('authentication radius timeout'):
- config_data['authentication']['radiusopt']['timeout'] = c.return_value('authentication radius timeout')
- if c.exists('authentication radius nas-identifier'):
- config_data['authentication']['radiusopt']['nas-id'] = c.return_value('authentication radius nas-identifier')
- if c.exists('authentication radius dae-server'):
- # Set default dae-server port if not defined
- if c.exists('authentication radius dae-server port'):
- dae_server_port = c.return_value('authentication radius dae-server port')
- else:
- dae_server_port = "3799"
- config_data['authentication']['radiusopt'].update(
- {
- 'dae-srv' : {
- 'ip-addr' : c.return_value('authentication radius dae-server ip-address'),
- 'port' : dae_server_port,
- 'secret' : str(c.return_value('authentication radius dae-server secret'))
- }
- }
- )
- #### filter-id is the internal accel default if attribute is empty
- #### set here as default for visibility which may change in the future
- if c.exists('authentication radius rate-limit enable'):
- if not c.exists('authentication radius rate-limit attribute'):
- config_data['authentication']['radiusopt']['shaper'] = {
- 'attr' : 'Filter-Id'
- }
- else:
- config_data['authentication']['radiusopt']['shaper'] = {
- 'attr' : c.return_value('authentication radius rate-limit attribute')
- }
- if c.exists('authentication radius rate-limit vendor'):
- config_data['authentication']['radiusopt']['shaper']['vendor'] = c.return_value('authentication radius rate-limit vendor')
-
- if c.exists('client-ip-pool'):
- if c.exists('client-ip-pool start') and c.exists('client-ip-pool stop'):
- config_data['client_ip_pool'] = c.return_value('client-ip-pool start') + '-' + re.search('[0-9]+$', c.return_value('client-ip-pool stop')).group(0)
-
- if c.exists('client-ip-pool subnet'):
- config_data['client_ip_subnets'] = c.return_values('client-ip-pool subnet')
-
- if c.exists('client-ipv6-pool prefix'):
- config_data['client_ipv6_pool']['prefix'] = c.return_values('client-ipv6-pool prefix')
- config_data['ip6_column'] = 'ip6,'
- if c.exists('client-ipv6-pool delegate-prefix'):
- config_data['client_ipv6_pool']['delegate_prefix'] = c.return_values('client-ipv6-pool delegate-prefix')
- config_data['ip6_dp_column'] = 'ip6-dp,'
-
- if c.exists('mtu'):
- config_data['mtu'] = c.return_value('mtu')
-
- ### gateway address
- if c.exists('gateway-address'):
- config_data['gateway_address'] = c.return_value('gateway-address')
- else:
- ### calculate gw-ip-address
- if c.exists('client-ip-pool start'):
- ### use start ip as gw-ip-address
- config_data['gateway_address'] = c.return_value('client-ip-pool start')
- elif c.exists('client-ip-pool subnet'):
- ### use first ip address from first defined pool
- lst_ip = re.findall("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", c.return_values('client-ip-pool subnet')[0])
- config_data['gateway_address'] = lst_ip[0]
-
- if c.exists('authentication require'):
- auth_mods = {'pap' : 'pap','chap' : 'auth_chap_md5', 'mschap' : 'auth_mschap_v1', 'mschap-v2' : 'auth_mschap_v2'}
- for proto in c.return_values('authentication require'):
- config_data['authentication']['auth_proto'].append(auth_mods[proto])
- else:
- config_data['authentication']['auth_proto'] = ['auth_mschap_v2']
-
- if c.exists('authentication mppe'):
- config_data['authentication']['mppe'] = c.return_value('authentication mppe')
-
- if c.exists('idle'):
- config_data['idle_timeout'] = c.return_value('idle')
-
- ### LNS secret
- if c.exists('lns shared-secret'):
- config_data['lns_shared_secret'] = c.return_value('lns shared-secret')
-
- if c.exists('ccp-disable'):
- config_data['ccp_disable'] = True
-
- ### ppp_options
- ppp_options = {}
- if c.exists('ppp-options'):
- if c.exists('ppp-options lcp-echo-failure'):
- ppp_options['lcp-echo-failure'] = c.return_value('ppp-options lcp-echo-failure')
- if c.exists('ppp-options lcp-echo-interval'):
- ppp_options['lcp-echo-interval'] = c.return_value('ppp-options lcp-echo-interval')
-
- if len(ppp_options) !=0:
- config_data['ppp_options'] = ppp_options
-
- return config_data
+ 'outside_addr': '',
+ 'gateway_address': '10.255.255.0',
+ 'dns': [],
+ 'dnsv6': [],
+ 'wins': [],
+ 'client_ip_pool': None,
+ 'client_ip_subnets': [],
+ 'client_ipv6_pool': {},
+ 'mtu': '1436',
+ 'ip6_column': '',
+ 'ip6_dp_column': '',
+ 'ppp_options': {},
+ }
+
+ ### general options ###
+
+ if c.exists('dns-servers server-1'):
+ config_data['dns'].append(c.return_value('dns-servers server-1'))
+ if c.exists('dns-servers server-2'):
+ config_data['dns'].append(c.return_value('dns-servers server-2'))
+ if c.exists('dnsv6-servers'):
+ for dns6_server in c.return_values('dnsv6-servers'):
+ config_data['dnsv6'].append(dns6_server)
+ if c.exists('wins-servers server-1'):
+ config_data['wins'].append(c.return_value('wins-servers server-1'))
+ if c.exists('wins-servers server-2'):
+ config_data['wins'].append(c.return_value('wins-servers server-2'))
+ if c.exists('outside-address'):
+ config_data['outside_addr'] = c.return_value('outside-address')
+
+ # auth local
+ if c.exists('authentication mode local'):
+ if c.exists('authentication local-users username'):
+ for usr in c.list_nodes('authentication local-users username'):
+ config_data['authentication']['local-users'].update(
+ {
+ usr: {
+ 'passwd': '',
+ 'state': 'enabled',
+ 'ip': '*',
+ 'upload': None,
+ 'download': None
+ }
+ }
+ )
+
+ if c.exists('authentication local-users username ' + usr + ' password'):
+ config_data['authentication']['local-users'][usr]['passwd'] = c.return_value(
+ 'authentication local-users username ' + usr + ' password')
+ if c.exists('authentication local-users username ' + usr + ' disable'):
+ config_data['authentication']['local-users'][usr]['state'] = 'disable'
+ if c.exists('authentication local-users username ' + usr + ' static-ip'):
+ config_data['authentication']['local-users'][usr]['ip'] = c.return_value(
+ 'authentication local-users username ' + usr + ' static-ip')
+ if c.exists('authentication local-users username ' + usr + ' rate-limit download'):
+ config_data['authentication']['local-users'][usr]['download'] = c.return_value(
+ 'authentication local-users username ' + usr + ' rate-limit download')
+ if c.exists('authentication local-users username ' + usr + ' rate-limit upload'):
+ config_data['authentication']['local-users'][usr]['upload'] = c.return_value(
+ 'authentication local-users username ' + usr + ' rate-limit upload')
+
+ # authentication mode radius servers and settings
+
+ if c.exists('authentication mode radius'):
+ config_data['authentication']['mode'] = 'radius'
+ rsrvs = c.list_nodes('authentication radius server')
+ for rsrv in rsrvs:
+ if c.return_value('authentication radius server ' + rsrv + ' fail-time') == None:
+ ftime = '0'
+ else:
+ ftime = str(c.return_value(
+ 'authentication radius server ' + rsrv + ' fail-time'))
+ if c.return_value('authentication radius-server ' + rsrv + ' req-limit') == None:
+ reql = '0'
+ else:
+ reql = str(c.return_value(
+ 'authentication radius server ' + rsrv + ' req-limit'))
+
+ config_data['authentication']['radiussrv'].update(
+ {
+ rsrv: {
+ 'secret': c.return_value('authentication radius server ' + rsrv + ' key'),
+ 'fail-time': ftime,
+ 'req-limit': reql
+ }
+ }
+ )
+ # Source ip address feature
+ if c.exists('authentication radius source-address'):
+ config_data['authentication']['radius_source_address'] = c.return_value(
+ 'authentication radius source-address')
+
+ # advanced radius-setting
+ if c.exists('authentication radius acct-timeout'):
+ config_data['authentication']['radiusopt']['acct-timeout'] = c.return_value(
+ 'authentication radius acct-timeout')
+ if c.exists('authentication radius max-try'):
+ config_data['authentication']['radiusopt']['max-try'] = c.return_value(
+ 'authentication radius max-try')
+ if c.exists('authentication radius timeout'):
+ config_data['authentication']['radiusopt']['timeout'] = c.return_value(
+ 'authentication radius timeout')
+ if c.exists('authentication radius nas-identifier'):
+ config_data['authentication']['radiusopt']['nas-id'] = c.return_value(
+ 'authentication radius nas-identifier')
+ if c.exists('authentication radius dae-server'):
+ # Set default dae-server port if not defined
+ if c.exists('authentication radius dae-server port'):
+ dae_server_port = c.return_value(
+ 'authentication radius dae-server port')
+ else:
+ dae_server_port = "3799"
+ config_data['authentication']['radiusopt'].update(
+ {
+ 'dae-srv': {
+ 'ip-addr': c.return_value('authentication radius dae-server ip-address'),
+ 'port': dae_server_port,
+ 'secret': str(c.return_value('authentication radius dae-server secret'))
+ }
+ }
+ )
+ # filter-id is the internal accel default if attribute is empty
+ # set here as default for visibility which may change in the future
+ if c.exists('authentication radius rate-limit enable'):
+ if not c.exists('authentication radius rate-limit attribute'):
+ config_data['authentication']['radiusopt']['shaper'] = {
+ 'attr': 'Filter-Id'
+ }
+ else:
+ config_data['authentication']['radiusopt']['shaper'] = {
+ 'attr': c.return_value('authentication radius rate-limit attribute')
+ }
+ if c.exists('authentication radius rate-limit vendor'):
+ config_data['authentication']['radiusopt']['shaper']['vendor'] = c.return_value(
+ 'authentication radius rate-limit vendor')
+
+ if c.exists('client-ip-pool'):
+ if c.exists('client-ip-pool start') and c.exists('client-ip-pool stop'):
+ config_data['client_ip_pool'] = c.return_value(
+ 'client-ip-pool start') + '-' + re.search('[0-9]+$', c.return_value('client-ip-pool stop')).group(0)
+
+ if c.exists('client-ip-pool subnet'):
+ config_data['client_ip_subnets'] = c.return_values(
+ 'client-ip-pool subnet')
+
+ if c.exists('client-ipv6-pool prefix'):
+ config_data['client_ipv6_pool']['prefix'] = c.return_values(
+ 'client-ipv6-pool prefix')
+ config_data['ip6_column'] = 'ip6,'
+ if c.exists('client-ipv6-pool delegate-prefix'):
+ config_data['client_ipv6_pool']['delegate_prefix'] = c.return_values(
+ 'client-ipv6-pool delegate-prefix')
+ config_data['ip6_dp_column'] = 'ip6-dp,'
+
+ if c.exists('mtu'):
+ config_data['mtu'] = c.return_value('mtu')
+
+ # gateway address
+ if c.exists('gateway-address'):
+ config_data['gateway_address'] = c.return_value('gateway-address')
+ else:
+ # calculate gw-ip-address
+ if c.exists('client-ip-pool start'):
+ # use start ip as gw-ip-address
+ config_data['gateway_address'] = c.return_value(
+ 'client-ip-pool start')
+ elif c.exists('client-ip-pool subnet'):
+ # use first ip address from first defined pool
+ lst_ip = re.findall("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", c.return_values(
+ 'client-ip-pool subnet')[0])
+ config_data['gateway_address'] = lst_ip[0]
+
+ if c.exists('authentication require'):
+ auth_mods = {'pap': 'pap', 'chap': 'auth_chap_md5',
+ 'mschap': 'auth_mschap_v1', 'mschap-v2': 'auth_mschap_v2'}
+ for proto in c.return_values('authentication require'):
+ config_data['authentication']['auth_proto'].append(
+ auth_mods[proto])
+ else:
+ config_data['authentication']['auth_proto'] = ['auth_mschap_v2']
+
+ if c.exists('authentication mppe'):
+ config_data['authentication']['mppe'] = c.return_value(
+ 'authentication mppe')
+
+ if c.exists('idle'):
+ config_data['idle_timeout'] = c.return_value('idle')
+
+ # LNS secret
+ if c.exists('lns shared-secret'):
+ config_data['lns_shared_secret'] = c.return_value('lns shared-secret')
+
+ if c.exists('ccp-disable'):
+ config_data['ccp_disable'] = True
+
+ # ppp_options
+ ppp_options = {}
+ if c.exists('ppp-options'):
+ if c.exists('ppp-options lcp-echo-failure'):
+ ppp_options['lcp-echo-failure'] = c.return_value(
+ 'ppp-options lcp-echo-failure')
+ if c.exists('ppp-options lcp-echo-interval'):
+ ppp_options['lcp-echo-interval'] = c.return_value(
+ 'ppp-options lcp-echo-interval')
+
+ if len(ppp_options) != 0:
+ config_data['ppp_options'] = ppp_options
+
+ return config_data
+
def verify(c):
- if c == None:
- return None
-
- if c['authentication']['mode'] == 'local':
- if not c['authentication']['local-users']:
- raise ConfigError('l2tp-server authentication local-users required')
- for usr in c['authentication']['local-users']:
- if not c['authentication']['local-users'][usr]['passwd']:
- raise ConfigError('user ' + usr + ' requires a password')
-
- if c['authentication']['mode'] == 'radius':
- if len(c['authentication']['radiussrv']) == 0:
- raise ConfigError('radius server required')
- for rsrv in c['authentication']['radiussrv']:
- if c['authentication']['radiussrv'][rsrv]['secret'] == None:
- raise ConfigError('radius server ' + rsrv + ' needs a secret configured')
-
- ### check for the existence of a client ip pool
- if not c['client_ip_pool'] and not c['client_ip_subnets']:
- raise ConfigError("set vpn l2tp remote-access client-ip-pool requires subnet or start/stop IP pool")
-
- ## check ipv6
- if 'delegate_prefix' in c['client_ipv6_pool'] and not 'prefix' in c['client_ipv6_pool']:
- raise ConfigError("\"set vpn l2tp remote-access client-ipv6-pool prefix\" required for delegate-prefix ")
-
- if len(c['dnsv6']) > 3:
- raise ConfigError("Maximum allowed dnsv6-servers addresses is 3")
+ if c == None:
+ return None
+
+ if c['authentication']['mode'] == 'local':
+ if not c['authentication']['local-users']:
+ raise ConfigError(
+ 'l2tp-server authentication local-users required')
+ for usr in c['authentication']['local-users']:
+ if not c['authentication']['local-users'][usr]['passwd']:
+ raise ConfigError('user ' + usr + ' requires a password')
+
+ if c['authentication']['mode'] == 'radius':
+ if len(c['authentication']['radiussrv']) == 0:
+ raise ConfigError('radius server required')
+ for rsrv in c['authentication']['radiussrv']:
+ if c['authentication']['radiussrv'][rsrv]['secret'] == None:
+ raise ConfigError('radius server ' + rsrv +
+ ' needs a secret configured')
+
+ # check for the existence of a client ip pool
+ if not c['client_ip_pool'] and not c['client_ip_subnets']:
+ raise ConfigError(
+ "set vpn l2tp remote-access client-ip-pool requires subnet or start/stop IP pool")
+
+ # check ipv6
+ if 'delegate_prefix' in c['client_ipv6_pool'] and not 'prefix' in c['client_ipv6_pool']:
+ raise ConfigError(
+ "\"set vpn l2tp remote-access client-ipv6-pool prefix\" required for delegate-prefix ")
+
+ if len(c['dnsv6']) > 3:
+ raise ConfigError("Maximum allowed dnsv6-servers addresses is 3")
+
def generate(c):
- if c == None:
- return None
-
- ### accel-cmd reload doesn't work so any change results in a restart of the daemon
- try:
- if os.cpu_count() == 1:
- c['thread_cnt'] = 1
- else:
- c['thread_cnt'] = int(os.cpu_count()/2)
- except KeyError:
- if os.cpu_count() == 1:
- c['thread_cnt'] = 1
- else:
- c['thread_cnt'] = int(os.cpu_count()/2)
+ if c == None:
+ return None
- tmpl = jinja2.Template(l2tp_config, trim_blocks=True)
- config_text = tmpl.render(c)
- open(l2tp_conf,'w').write(config_text)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'l2tp')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader, trim_blocks=True)
- if c['authentication']['local-users']:
- write_chap_secrets(c)
+ # accel-cmd reload doesn't work so any change results in a restart of the daemon
+ try:
+ if os.cpu_count() == 1:
+ c['thread_cnt'] = 1
+ else:
+ c['thread_cnt'] = int(os.cpu_count()/2)
+ except KeyError:
+ if os.cpu_count() == 1:
+ c['thread_cnt'] = 1
+ else:
+ c['thread_cnt'] = int(os.cpu_count()/2)
+
+ tmpl = env.get_template('l2tp.config.tmpl')
+ config_text = tmpl.render(c)
+ open(l2tp_conf, 'w').write(config_text)
+
+ if c['authentication']['local-users']:
+ tmpl = env.get_template('chap-secrets.tmpl')
+ chap_secrets_txt = tmpl.render(c)
+ old_umask = os.umask(0o077)
+ open(chap_secrets, 'w').write(chap_secrets_txt)
+ os.umask(old_umask)
+
+ return c
- return c
def apply(c):
- if c == None:
- if os.path.exists(pidfile):
- accel_cmd('shutdown hard')
- if os.path.exists(pidfile):
- os.remove(pidfile)
- return None
-
- if not os.path.exists(pidfile):
- ret = subprocess.call(['/usr/sbin/accel-pppd','-c',l2tp_conf,'-p',pidfile,'-d'])
- chk_con()
- if ret !=0 and os.path.exists(pidfile):
- os.remove(pidfile)
- raise ConfigError('accel-pppd failed to start')
- else:
- ### if gw ip changes, only restart doesn't work
- accel_cmd('restart')
- sl.syslog(sl.LOG_NOTICE, "reloading config via daemon restart")
+ if c == None:
+ if os.path.exists(pidfile):
+ accel_cmd('shutdown hard')
+ if os.path.exists(pidfile):
+ os.remove(pidfile)
+ return None
+
+ if not os.path.exists(pidfile):
+ ret = subprocess.call(
+ ['/usr/sbin/accel-pppd', '-c', l2tp_conf, '-p', pidfile, '-d'])
+ chk_con()
+ if ret != 0 and os.path.exists(pidfile):
+ os.remove(pidfile)
+ raise ConfigError('accel-pppd failed to start')
+ else:
+ # if gw ip changes, only restart doesn't work
+ accel_cmd('restart')
+
if __name__ == '__main__':
- try:
- c = get_config()
- verify(c)
- generate(c)
- apply(c)
- except ConfigError as e:
- print(e)
- sys.exit(1)
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ sys.exit(1)
diff --git a/src/conf_mode/bcast_relay.py b/src/conf_mode/bcast_relay.py
index 8889e701c..96576ddd4 100755
--- a/src/conf_mode/bcast_relay.py
+++ b/src/conf_mode/bcast_relay.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2017 VyOS maintainers and contributors
+# Copyright (C) 2017-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
@@ -13,42 +13,34 @@
#
# 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 sys
import os
import fnmatch
-import jinja2
+
+from sys import exit
+from copy import deepcopy
+from jinja2 import FileSystemLoader, Environment
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
config_file = r'/etc/default/udp-broadcast-relay'
-config_tmpl = """
-### Autogenerated by bcast_relay.py ###
-
-# UDP broadcast relay configuration for instance {{ id }}
-{%- if description %}
-# Comment: {{ description }}
-{% endif %}
-DAEMON_ARGS="{% if address %}-s {{ address }} {% endif %}{{ id }} {{ port }} {{ interfaces | join(' ') }}"
-
-"""
-
default_config_data = {
'disabled': False,
'instances': []
}
def get_config():
- relay = default_config_data
+ relay = deepcopy(default_config_data)
conf = Config()
- if not conf.exists('service broadcast-relay'):
+ base = ['service', 'broadcast-relay']
+
+ if not conf.exists(base):
return None
else:
- conf.set_level('service broadcast-relay')
+ conf.set_level(base)
# Service can be disabled by user
if conf.exists('disable'):
@@ -58,7 +50,7 @@ def get_config():
# Parse configuration of each individual instance
if conf.exists('id'):
for id in conf.list_nodes('id'):
- conf.set_level('service broadcast-relay id {0}'.format(id))
+ conf.set_level(base + ['id', id])
config = {
'id': id,
'disabled': False,
@@ -69,24 +61,24 @@ def get_config():
}
# Check if individual broadcast relay service is disabled
- if conf.exists('disable'):
+ if conf.exists(['disable']):
config['disabled'] = True
# Source IP of forwarded packets, if empty original senders address is used
- if conf.exists('address'):
- config['address'] = conf.return_value('address')
+ if conf.exists(['address']):
+ config['address'] = conf.return_value(['address'])
# A description for each individual broadcast relay service
- if conf.exists('description'):
- config['description'] = conf.return_value('description')
+ if conf.exists(['description']):
+ config['description'] = conf.return_value(['description'])
# UDP port to listen on for broadcast frames
- if conf.exists('port'):
- config['port'] = conf.return_value('port')
+ if conf.exists(['port']):
+ config['port'] = conf.return_value(['port'])
# Network interfaces to listen on for broadcast frames to be relayed
- if conf.exists('interface'):
- config['interfaces'] = conf.return_values('interface')
+ if conf.exists(['interface']):
+ config['interfaces'] = conf.return_values(['interface'])
relay['instances'].append(config)
@@ -119,6 +111,11 @@ def generate(relay):
if relay is None:
return None
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'bcast-relay')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
config_dir = os.path.dirname(config_file)
config_filename = os.path.basename(config_file)
active_configs = []
@@ -148,7 +145,7 @@ def generate(relay):
# configuration filename contains instance id
file = config_file + str(r['id'])
- tmpl = jinja2.Template(config_tmpl)
+ tmpl = env.get_template('udp-broadcast-relay.tmpl')
config_text = tmpl.render(r)
with open(file, 'w') as f:
f.write(config_text)
@@ -179,4 +176,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/dhcp_relay.py b/src/conf_mode/dhcp_relay.py
index a1af2575f..6f8d66e7b 100755
--- a/src/conf_mode/dhcp_relay.py
+++ b/src/conf_mode/dhcp_relay.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,39 +13,18 @@
#
# 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 sys
import os
-import jinja2
+
+from jinja2 import FileSystemLoader, Environment
+from sys import exit
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
config_file = r'/etc/default/isc-dhcp-relay'
-# Please be careful if you edit the template.
-config_tmpl = """
-### Autogenerated by dhcp_relay.py ###
-
-# Defaults for isc-dhcp-relay initscript
-# sourced by /etc/init.d/isc-dhcp-relay
-
-#
-# This is a POSIX shell fragment
-#
-
-# What servers should the DHCP relay forward requests to?
-SERVERS="{{ server | join(' ') }}"
-
-# On what interfaces should the DHCP relay (dhrelay) serve DHCP requests?
-INTERFACES="{{ interface | join(' ') }}"
-
-# Additional options that are passed to the DHCP relay daemon?
-OPTIONS="-4 {{ options | join(' ') }}"
-"""
-
default_config_data = {
'interface': [],
'server': [],
@@ -57,23 +36,23 @@ default_config_data = {
def get_config():
relay = default_config_data
conf = Config()
- if not conf.exists('service dhcp-relay'):
+ if not conf.exists(['service', 'dhcp-relay']):
return None
else:
- conf.set_level('service dhcp-relay')
+ conf.set_level(['service', 'dhcp-relay'])
# Network interfaces to listen on
- if conf.exists('interface'):
- relay['interface'] = conf.return_values('interface')
+ if conf.exists(['interface']):
+ relay['interface'] = conf.return_values(['interface'])
# Servers equal to the address of the DHCP server(s)
- if conf.exists('server'):
- relay['server'] = conf.return_values('server')
+ if conf.exists(['server']):
+ relay['server'] = conf.return_values(['server'])
- conf.set_level('service dhcp-relay relay-options')
+ conf.set_level(['service', 'dhcp-relay', 'relay-options'])
- if conf.exists('hop-count'):
- count = '-c ' + conf.return_value('hop-count')
+ if conf.exists(['hop-count']):
+ count = '-c ' + conf.return_value(['hop-count'])
relay['options'].append(count)
# Specify the maximum packet size to send to a DHCPv4/BOOTP server.
@@ -81,8 +60,8 @@ def get_config():
# options while still fitting into the Ethernet MTU size.
#
# Available in DHCPv4 mode only:
- if conf.exists('max-size'):
- size = '-A ' + conf.return_value('max-size')
+ if conf.exists(['max-size']):
+ size = '-A ' + conf.return_value(['max-size'])
relay['options'].append(size)
# Control the handling of incoming DHCPv4 packets which already contain
@@ -94,8 +73,8 @@ def get_config():
# field; it may forward the packet unchanged; or, it may discard it.
#
# Available in DHCPv4 mode only:
- if conf.exists('relay-agents-packets'):
- pkt = '-a -m ' + conf.return_value('relay-agents-packets')
+ if conf.exists(['relay-agents-packets']):
+ pkt = '-a -m ' + conf.return_value(['relay-agents-packets'])
relay['options'].append(pkt)
return relay
@@ -119,7 +98,12 @@ def generate(relay):
if relay is None:
return None
- tmpl = jinja2.Template(config_tmpl)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'dhcp-relay')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
+ tmpl = env.get_template('config.tmpl')
config_text = tmpl.render(relay)
with open(config_file, 'w') as f:
f.write(config_text)
@@ -144,4 +128,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py
index 39cc72574..3d75414f5 100755
--- a/src/conf_mode/dhcp_server.py
+++ b/src/conf_mode/dhcp_server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2019 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -14,16 +14,17 @@
# 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 sys
import os
-import jinja2
-import socket
-import struct
-
-import vyos.validate
from ipaddress import ip_address, ip_network
+from jinja2 import FileSystemLoader, Environment
+from socket import inet_ntoa
+from struct import pack
+from sys import exit
+
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
+from vyos.validate import is_subnet_connected
from vyos import ConfigError
config_file = r'/etc/dhcp/dhcpd.conf'
@@ -31,215 +32,6 @@ lease_file = r'/config/dhcpd.leases'
pid_file = r'/var/run/dhcpd.pid'
daemon_config_file = r'/etc/default/isc-dhcpv4-server'
-# Please be careful if you edit the template.
-config_tmpl = """
-### Autogenerated by dhcp_server.py ###
-
-# For options please consult the following website:
-# https://www.isc.org/wp-content/uploads/2017/08/dhcp43options.html
-#
-# log-facility local7;
-
-{% if hostfile_update %}
-on release {
- set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name);
- set ClientIp = binary-to-ascii(10, 8, ".",leased-address);
- set ClientMac = binary-to-ascii(16, 8, ":",substring(hardware, 1, 6));
- set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!");
- execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", ClientName, ClientIp, ClientMac, ClientDomain);
-}
-
-on expiry {
- set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name);
- set ClientIp = binary-to-ascii(10, 8, ".",leased-address);
- set ClientMac = binary-to-ascii(16, 8, ":",substring(hardware, 1, 6));
- set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!");
- execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", ClientName, ClientIp, ClientMac, ClientDomain);
-}
-{% endif %}
-{%- if host_decl_name %}
-use-host-decl-names on;
-{%- endif %}
-ddns-update-style {% if ddns_enable -%} interim {%- else -%} none {%- endif %};
-{% if static_route -%}
-option rfc3442-static-route code 121 = array of integer 8;
-option windows-static-route code 249 = array of integer 8;
-{%- endif %}
-{% if wpad -%}
-option wpad-url code 252 = text;
-{% endif %}
-
-{%- if global_parameters %}
-# The following {{ global_parameters | length }} line(s) were added as global-parameters in the CLI and have not been validated
-{%- for param in global_parameters %}
-{{ param }}
-{%- endfor -%}
-{%- endif %}
-
-# Failover configuration
-{% for network in shared_network %}
-{%- if not network.disabled -%}
-{%- for subnet in network.subnet %}
-{%- if subnet.failover_name -%}
-failover peer "{{ subnet.failover_name }}" {
-{%- if subnet.failover_status == 'primary' %}
- primary;
- mclt 1800;
- split 128;
-{%- elif subnet.failover_status == 'secondary' %}
- secondary;
-{%- endif %}
- address {{ subnet.failover_local_addr }};
- port 520;
- peer address {{ subnet.failover_peer_addr }};
- peer port 520;
- max-response-delay 30;
- max-unacked-updates 10;
- load balance max seconds 3;
-}
-{% endif -%}
-{% endfor -%}
-{% endif -%}
-{% endfor %}
-
-# Shared network configration(s)
-{% for network in shared_network %}
-{%- if not network.disabled -%}
-shared-network {{ network.name }} {
- {%- if network.authoritative %}
- authoritative;
- {%- endif %}
- {%- if network.network_parameters %}
- # The following {{ network.network_parameters | length }} line(s) were added as shared-network-parameters in the CLI and have not been validated
- {%- for param in network.network_parameters %}
- {{ param }}
- {%- endfor %}
- {%- endif %}
- {%- for subnet in network.subnet %}
- subnet {{ subnet.address }} netmask {{ subnet.netmask }} {
- {%- if subnet.dns_server %}
- option domain-name-servers {{ subnet.dns_server | join(', ') }};
- {%- endif %}
- {%- if subnet.domain_search %}
- option domain-search {{ subnet.domain_search | join(', ') }};
- {%- endif %}
- {%- if subnet.ntp_server %}
- option ntp-servers {{ subnet.ntp_server | join(', ') }};
- {%- endif %}
- {%- if subnet.pop_server %}
- option pop-server {{ subnet.pop_server | join(', ') }};
- {%- endif %}
- {%- if subnet.smtp_server %}
- option smtp-server {{ subnet.smtp_server | join(', ') }};
- {%- endif %}
- {%- if subnet.time_server %}
- option time-servers {{ subnet.time_server | join(', ') }};
- {%- endif %}
- {%- if subnet.wins_server %}
- option netbios-name-servers {{ subnet.wins_server | join(', ') }};
- {%- endif %}
- {%- if subnet.static_route %}
- option rfc3442-static-route {{ subnet.static_route }}{% if subnet.rfc3442_default_router %}, {{ subnet.rfc3442_default_router }}{% endif %};
- option windows-static-route {{ subnet.static_route }};
- {%- endif %}
- {%- if subnet.ip_forwarding %}
- option ip-forwarding true;
- {%- endif -%}
- {%- if subnet.default_router %}
- option routers {{ subnet.default_router }};
- {%- endif -%}
- {%- if subnet.server_identifier %}
- option dhcp-server-identifier {{ subnet.server_identifier }};
- {%- endif -%}
- {%- if subnet.domain_name %}
- option domain-name "{{ subnet.domain_name }}";
- {%- endif -%}
- {%- if subnet.subnet_parameters %}
- # The following {{ subnet.subnet_parameters | length }} line(s) were added as subnet-parameters in the CLI and have not been validated
- {%- for param in subnet.subnet_parameters %}
- {{ param }}
- {%- endfor -%}
- {%- endif %}
- {%- if subnet.tftp_server %}
- option tftp-server-name "{{ subnet.tftp_server }}";
- {%- endif -%}
- {%- if subnet.bootfile_name %}
- option bootfile-name "{{ subnet.bootfile_name }}";
- filename "{{ subnet.bootfile_name }}";
- {%- endif -%}
- {%- if subnet.bootfile_server %}
- next-server {{ subnet.bootfile_server }};
- {%- endif -%}
- {%- if subnet.time_offset %}
- option time-offset {{ subnet.time_offset }};
- {%- endif -%}
- {%- if subnet.wpad_url %}
- option wpad-url "{{ subnet.wpad_url }}";
- {%- endif -%}
- {%- if subnet.client_prefix_length %}
- option subnet-mask {{ subnet.client_prefix_length }};
- {%- endif -%}
- {% if subnet.lease %}
- default-lease-time {{ subnet.lease }};
- max-lease-time {{ subnet.lease }};
- {%- endif -%}
- {%- for host in subnet.static_mapping %}
- {% if not host.disabled -%}
- host {% if host_decl_name -%} {{ host.name }} {%- else -%} {{ network.name }}_{{ host.name }} {%- endif %} {
- {%- if host.ip_address %}
- fixed-address {{ host.ip_address }};
- {%- endif %}
- hardware ethernet {{ host.mac_address }};
- {%- if host.static_parameters %}
- # The following {{ host.static_parameters | length }} line(s) were added as static-mapping-parameters in the CLI and have not been validated
- {%- for param in host.static_parameters %}
- {{ param }}
- {%- endfor -%}
- {%- endif %}
- }
- {%- endif %}
- {%- endfor %}
- {%- if subnet.failover_name %}
- pool {
- failover peer "{{ subnet.failover_name }}";
- deny dynamic bootp clients;
- {%- for range in subnet.range %}
- range {{ range.start }} {{ range.stop }};
- {%- endfor %}
- }
- {%- else %}
- {%- for range in subnet.range %}
- range {{ range.start }} {{ range.stop }};
- {%- endfor %}
- {%- endif %}
- }
- {%- endfor %}
- on commit {
- set shared-networkname = "{{ network.name }}";
- {% if hostfile_update -%}
- set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name);
- set ClientIp = binary-to-ascii(10, 8, ".", leased-address);
- set ClientMac = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));
- set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!");
- execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "commit", ClientName, ClientIp, ClientMac, ClientDomain);
- {%- endif %}
- }
-}
-{%- endif %}
-{% endfor %}
-"""
-
-daemon_tmpl = """
-### Autogenerated by dhcp_server.py ###
-
-# sourced by /etc/init.d/isc-dhcpv4-server
-
-DHCPD_CONF={{ config_file }}
-DHCPD_PID={{ pid_file }}
-OPTIONS="-4 -lf {{ lease_file }}"
-INTERFACES=""
-"""
-
default_config_data = {
'lease_file': lease_file,
'disabled': False,
@@ -459,7 +251,7 @@ def get_config():
if conf.exists('client-prefix-length'):
# snippet borrowed from https://stackoverflow.com/questions/33750233/convert-cidr-to-subnet-mask-in-python
host_bits = 32 - int(conf.return_value('client-prefix-length'))
- subnet['client_prefix_length'] = socket.inet_ntoa(struct.pack('!I', (1 << 32) - (1 << host_bits)))
+ subnet['client_prefix_length'] = inet_ntoa(pack('!I', (1 << 32) - (1 << host_bits)))
# Default router IP address on the client's subnet
if conf.exists('default-router'):
@@ -778,7 +570,7 @@ def verify(dhcp):
# There must be one subnet connected to a listen interface.
# This only counts if the network itself is not disabled!
if not network['disabled']:
- if vyos.validate.is_subnet_connected(subnet['network'], primary=True):
+ if is_subnet_connected(subnet['network'], primary=True):
listen_ok = True
# Subnets must be non overlapping
@@ -810,9 +602,13 @@ def generate(dhcp):
print('Warning: DHCP server will be deactivated because it is disabled')
return None
- tmpl = jinja2.Template(config_tmpl)
- config_text = tmpl.render(dhcp)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'dhcp-server')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+ tmpl = env.get_template('dhcpd.conf.tmpl')
+ config_text = tmpl.render(dhcp)
# Please see: https://phabricator.vyos.net/T1129 for quoting of the raw parameters
# we can pass to ISC DHCPd
config_text = config_text.replace("&quot;",'"')
@@ -820,7 +616,7 @@ def generate(dhcp):
with open(config_file, 'w') as f:
f.write(config_text)
- tmpl = jinja2.Template(daemon_tmpl)
+ tmpl = env.get_template('daemon.tmpl')
config_text = tmpl.render(dhcp)
with open(daemon_config_file, 'w') as f:
f.write(config_text)
@@ -852,4 +648,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/dhcpv6_relay.py b/src/conf_mode/dhcpv6_relay.py
index ccabc901d..d942daf37 100755
--- a/src/conf_mode/dhcpv6_relay.py
+++ b/src/conf_mode/dhcpv6_relay.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,27 +13,19 @@
#
# 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 sys
import os
-import jinja2
+
+from sys import exit
+from copy import deepcopy
+from jinja2 import FileSystemLoader, Environment
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
config_file = r'/etc/default/isc-dhcpv6-relay'
-# Please be careful if you edit the template.
-config_tmpl = """
-### Autogenerated by dhcpv6_relay.py ###
-
-# Defaults for isc-dhcpv6-relay initscript sourced by /etc/init.d/isc-dhcpv6-relay
-OPTIONS="-6 -l {{ listen_addr | join(' -l ') }} -u {{ upstream_addr | join(' -u ') }} {{ options | join(' ') }}"
-
-"""
-
default_config_data = {
'listen_addr': [],
'upstream_addr': [],
@@ -41,7 +33,7 @@ default_config_data = {
}
def get_config():
- relay = default_config_data
+ relay = deepcopy(default_config_data)
conf = Config()
if not conf.exists('service dhcpv6-relay'):
return None
@@ -92,7 +84,12 @@ def generate(relay):
if relay is None:
return None
- tmpl = jinja2.Template(config_tmpl)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'dhcpv6-relay')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
+ tmpl = env.get_template('config.tmpl')
config_text = tmpl.render(relay)
with open(config_file, 'w') as f:
f.write(config_text)
@@ -117,4 +114,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/dhcpv6_server.py b/src/conf_mode/dhcpv6_server.py
index 44a927789..10b40baa4 100755
--- a/src/conf_mode/dhcpv6_server.py
+++ b/src/conf_mode/dhcpv6_server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2019 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,18 +13,17 @@
#
# 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 sys
import os
import ipaddress
-import jinja2
-
-import vyos.validate
+from sys import exit
+from copy import deepcopy
+from jinja2 import FileSystemLoader, Environment
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
+from vyos.validate import is_subnet_connected
from vyos import ConfigError
config_file = r'/etc/dhcp/dhcpdv6.conf'
@@ -32,100 +31,6 @@ lease_file = r'/config/dhcpdv6.leases'
pid_file = r'/var/run/dhcpdv6.pid'
daemon_config_file = r'/etc/default/isc-dhcpv6-server'
-# Please be careful if you edit the template.
-config_tmpl = """
-### Autogenerated by dhcpv6_server.py ###
-
-# For options please consult the following website:
-# https://www.isc.org/wp-content/uploads/2017/08/dhcp43options.html
-
-log-facility local7;
-{%- if preference %}
-option dhcp6.preference {{ preference }};
-{%- endif %}
-
-# Shared network configration(s)
-{% for network in shared_network %}
-{%- if not network.disabled -%}
-shared-network {{ network.name }} {
- {%- for subnet in network.subnet %}
- subnet6 {{ subnet.network }} {
- {%- for range in subnet.range6_prefix %}
- range6 {{ range.prefix }}{{ " temporary" if range.temporary }};
- {%- endfor %}
- {%- for range in subnet.range6 %}
- range6 {{ range.start }} {{ range.stop }};
- {%- endfor %}
- {%- if subnet.domain_search %}
- option dhcp6.domain-search {{ subnet.domain_search | join(', ') }};
- {%- endif %}
- {%- if subnet.lease_def %}
- default-lease-time {{ subnet.lease_def }};
- {%- endif %}
- {%- if subnet.lease_max %}
- max-lease-time {{ subnet.lease_max }};
- {%- endif %}
- {%- if subnet.lease_min %}
- min-lease-time {{ subnet.lease_min }};
- {%- endif %}
- {%- if subnet.dns_server %}
- option dhcp6.name-servers {{ subnet.dns_server | join(', ') }};
- {%- endif %}
- {%- if subnet.nis_domain %}
- option dhcp6.nis-domain-name "{{ subnet.nis_domain }}";
- {%- endif %}
- {%- if subnet.nis_server %}
- option dhcp6.nis-servers {{ subnet.nis_server | join(', ') }};
- {%- endif %}
- {%- if subnet.nisp_domain %}
- option dhcp6.nisp-domain-name "{{ subnet.nisp_domain }}";
- {%- endif %}
- {%- if subnet.nisp_server %}
- option dhcp6.nisp-servers {{ subnet.nisp_server | join(', ') }};
- {%- endif %}
- {%- if subnet.sip_address %}
- option dhcp6.sip-servers-addresses {{ subnet.sip_address | join(', ') }};
- {%- endif %}
- {%- if subnet.sip_hostname %}
- option dhcp6.sip-servers-names {{ subnet.sip_hostname | join(', ') }};
- {%- endif %}
- {%- if subnet.sntp_server %}
- option dhcp6.sntp-servers {{ subnet.sntp_server | join(', ') }};
- {%- endif %}
- {%- for host in subnet.static_mapping %}
- {% if not host.disabled -%}
- host {{ network.name }}_{{ host.name }} {
- {%- if host.client_identifier %}
- host-identifier option dhcp6.client-id {{ host.client_identifier }};
- {%- endif %}
- {%- if host.ipv6_address %}
- fixed-address6 {{ host.ipv6_address }};
- {%- endif %}
- }
- {%- endif %}
- {%- endfor %}
- }
- {%- endfor %}
- on commit {
- set shared-networkname = "{{ network.name }}";
- }
-}
-{%- endif %}
-{% endfor %}
-
-"""
-
-daemon_tmpl = """
-### Autogenerated by dhcpv6_server.py ###
-
-# sourced by /etc/init.d/isc-dhcpv6-server
-
-DHCPD_CONF={{ config_file }}
-DHCPD_PID={{ pid_file }}
-OPTIONS="-6 -lf {{ lease_file }}"
-INTERFACES=""
-"""
-
default_config_data = {
'lease_file': lease_file,
'preference': '',
@@ -134,7 +39,7 @@ default_config_data = {
}
def get_config():
- dhcpv6 = default_config_data
+ dhcpv6 = deepcopy(default_config_data)
conf = Config()
if not conf.exists('service dhcpv6-server'):
return None
@@ -409,7 +314,7 @@ def verify(dhcpv6):
# There must be one subnet connected to a listen interface if network is not disabled.
if not network['disabled']:
- if vyos.validate.is_subnet_connected(subnet['network']):
+ if is_subnet_connected(subnet['network']):
listen_ok = True
# DHCPv6 subnet must not overlap. ISC DHCP also complains about overlapping
@@ -437,12 +342,17 @@ def generate(dhcpv6):
print('Warning: DHCPv6 server will be deactivated because it is disabled')
return None
- tmpl = jinja2.Template(config_tmpl)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'dhcpv6-server')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
+ tmpl = env.get_template('dhcpdv6.conf.tmpl')
config_text = tmpl.render(dhcpv6)
with open(config_file, 'w') as f:
f.write(config_text)
- tmpl = jinja2.Template(daemon_tmpl)
+ tmpl = env.get_template('daemon.tmpl')
config_text = tmpl.render(dhcpv6)
with open(daemon_config_file, 'w') as f:
f.write(config_text)
@@ -474,4 +384,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py
index 38f3cb4de..bbb69cdf7 100755
--- a/src/conf_mode/dns_forwarding.py
+++ b/src/conf_mode/dns_forwarding.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,76 +13,26 @@
#
# 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 sys
import os
-
import argparse
-import jinja2
-import netifaces
-import vyos.util
-import vyos.hostsd_client
+from sys import exit
+from copy import deepcopy
+from jinja2 import FileSystemLoader, Environment
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
+from vyos.hostsd_client import Client as hostsd_client
+from vyos.util import wait_for_commit_lock
from vyos import ConfigError
-
parser = argparse.ArgumentParser()
parser.add_argument("--dhclient", action="store_true",
help="Started from dhclient-script")
config_file = r'/etc/powerdns/recursor.conf'
-# XXX: pdns recursor doesn't like whitespace near entry separators,
-# especially in the semicolon-separated lists of name servers.
-# Please be careful if you edit the template.
-config_tmpl = """
-### Autogenerated by dns_forwarding.py ###
-
-# Non-configurable defaults
-daemon=yes
-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=::
-
-# cache-size
-max-cache-entries={{ cache_size }}
-
-# negative TTL for NXDOMAIN
-max-negative-ttl={{ negative_ttl }}
-
-# ignore-hosts-file
-export-etc-hosts={{ export_hosts_file }}
-
-# listen-on
-local-address={{ listen_on | join(',') }}
-
-# dnssec
-dnssec={{ dnssec }}
-
-# forward-zones / recursion
-#
-# statement is only inserted if either one forwarding domain or nameserver is configured
-# if nothing is given at all, powerdns will act as a real recursor and resolve all requests by its own
-#
-{% if name_servers or domains %}forward-zones-recurse=
-{%- for d in domains %}
-{{ d.name }}={{ d.servers | join(";") }}
-{{- ", " if not loop.last -}}
-{%- endfor -%}
-{%- if name_servers -%}
-{%- if domains -%}, {% endif -%}.={{ name_servers | join(';') }}
-{% endif %}
-{% endif %}
-
-"""
-
default_config_data = {
'allow_from': [],
'cache_size': 10000,
@@ -96,74 +46,74 @@ default_config_data = {
def get_config(arguments):
- dns = default_config_data
+ dns = deepcopy(default_config_data)
conf = Config()
+ base = ['service', 'dns', 'forwarding']
if arguments.dhclient:
conf.exists = conf.exists_effective
conf.return_value = conf.return_effective_value
conf.return_values = conf.return_effective_values
- if not conf.exists('service dns forwarding'):
+ if not conf.exists(base):
return None
- conf.set_level('service dns forwarding')
+ conf.set_level(base)
- if conf.exists('allow-from'):
- dns['allow_from'] = conf.return_values('allow-from')
+ if conf.exists(['allow-from']):
+ dns['allow_from'] = conf.return_values(['allow-from'])
- if conf.exists('cache-size'):
- cache_size = conf.return_value('cache-size')
+ if conf.exists(['cache-size']):
+ cache_size = conf.return_value(['cache-size'])
dns['cache_size'] = cache_size
if conf.exists('negative-ttl'):
- negative_ttl = conf.return_value('negative-ttl')
+ negative_ttl = conf.return_value(['negative-ttl'])
dns['negative_ttl'] = negative_ttl
- if conf.exists('domain'):
- for node in conf.list_nodes('domain'):
- servers = conf.return_values("domain {0} server".format(node))
+ if conf.exists(['domain']):
+ for node in conf.list_nodes(['domain']):
+ servers = conf.return_values(['domain', node, 'server'])
domain = {
"name": node,
"servers": bracketize_ipv6_addrs(servers)
}
dns['domains'].append(domain)
- if conf.exists('ignore-hosts-file'):
+ if conf.exists(['ignore-hosts-file']):
dns['export_hosts_file'] = "no"
- if conf.exists('name-server'):
- name_servers = conf.return_values('name-server')
+ if conf.exists(['name-server']):
+ name_servers = conf.return_values(['name-server'])
dns['name_servers'] = dns['name_servers'] + name_servers
- if conf.exists('system'):
- conf.set_level('system')
+ if conf.exists(['system']):
+ conf.set_level(['system'])
system_name_servers = []
- system_name_servers = conf.return_values('name-server')
+ system_name_servers = conf.return_values(['name-server'])
if not system_name_servers:
- print(
- "DNS forwarding warning: No name-servers set under 'system name-server'\n")
+ print("DNS forwarding warning: No name-servers set under 'system name-server'\n")
else:
dns['name_servers'] = dns['name_servers'] + system_name_servers
- conf.set_level('service dns forwarding')
+ conf.set_level(base)
dns['name_servers'] = bracketize_ipv6_addrs(dns['name_servers'])
- if conf.exists('listen-address'):
- dns['listen_on'] = conf.return_values('listen-address')
+ if conf.exists(['listen-address']):
+ dns['listen_on'] = conf.return_values(['listen-address'])
- if conf.exists('dnssec'):
- dns['dnssec'] = conf.return_value('dnssec')
+ if conf.exists(['dnssec']):
+ dns['dnssec'] = conf.return_value(['dnssec'])
# Add name servers received from DHCP
- if conf.exists('dhcp'):
+ if conf.exists(['dhcp']):
interfaces = []
- interfaces = conf.return_values('dhcp')
- hc = vyos.hostsd_client.Client()
+ interfaces = conf.return_values(['dhcp'])
+ hc = hostsd_client()
for interface in interfaces:
- dhcp_resolvers = hc.get_name_servers("dhcp-{0}".format(interface))
- dhcpv6_resolvers = hc.get_name_servers("dhcpv6-{0}".format(interface))
+ dhcp_resolvers = hc.get_name_servers(f'dhcp-{interface}')
+ dhcpv6_resolvers = hc.get_name_servers(f'dhcpv6-{interface}')
if dhcp_resolvers:
dns['name_servers'] = dns['name_servers'] + dhcp_resolvers
@@ -202,7 +152,12 @@ def generate(dns):
if dns is None:
return None
- tmpl = jinja2.Template(config_tmpl, trim_blocks=True)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'dns-forwarding')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader, trim_blocks=True)
+
+ tmpl = env.get_template('recursor.conf.tmpl')
config_text = tmpl.render(dns)
with open(config_file, 'w') as f:
f.write(config_text)
@@ -223,7 +178,7 @@ if __name__ == '__main__':
if args.dhclient:
# There's a big chance it was triggered by a commit still in progress
# so we need to wait until the new values are in the running config
- vyos.util.wait_for_commit_lock()
+ wait_for_commit_lock()
try:
c = get_config(args)
@@ -232,4 +187,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/dynamic_dns.py b/src/conf_mode/dynamic_dns.py
index faf0663ab..56ce4fedc 100755
--- a/src/conf_mode/dynamic_dns.py
+++ b/src/conf_mode/dynamic_dns.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2019 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -15,68 +15,20 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import sys
-import jinja2
+from sys import exit
+from copy import deepcopy
+from jinja2 import FileSystemLoader, Environment
from stat import S_IRUSR, S_IWUSR
+
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
config_file = r'/etc/ddclient/ddclient.conf'
cache_file = r'/var/cache/ddclient/ddclient.cache'
pid_file = r'/var/run/ddclient/ddclient.pid'
-config_tmpl = """
-### Autogenerated by dynamic_dns.py ###
-daemon=1m
-syslog=yes
-ssl=yes
-pid={{ pid_file }}
-cache={{ cache_file }}
-
-{% for interface in interfaces -%}
-
-#
-# ddclient configuration for interface "{{ interface.interface }}":
-#
-{% if interface.web_url -%}
-use=web, web='{{ interface.web_url}}' {%- if interface.web_skip %}, web-skip='{{ interface.web_skip }}'{% endif %}
-{% else -%}
-use=if, if={{ interface.interface }}
-{% endif -%}
-
-{% for rfc in interface.rfc2136 -%}
-{% for record in rfc.record %}
-# RFC2136 dynamic DNS configuration for {{ record }}.{{ rfc.zone }}
-server={{ rfc.server }}
-protocol=nsupdate
-password={{ rfc.keyfile }}
-ttl={{ rfc.ttl }}
-zone={{ rfc.zone }}
-{{ record }}
-{% endfor -%}
-{% endfor -%}
-
-{% for srv in interface.service %}
-{% for host in srv.host %}
-# DynDNS provider configuration for {{ host }}
-protocol={{ srv.protocol }},
-max-interval=28d,
-login={{ srv.login }},
-password='{{ srv.password }}',
-{% if srv.server -%}
-server={{ srv.server }},
-{% endif -%}
-{% if srv.zone -%}
-zone={{ srv.zone }},
-{% endif -%}
-{{ host }}
-{% endfor %}
-{% endfor %}
-
-{% endfor %}
-"""
-
# Mapping of service name to service protocol
default_service_protocol = {
'afraid': 'freedns',
@@ -100,7 +52,7 @@ default_config_data = {
}
def get_config():
- dyndns = default_config_data
+ dyndns = deepcopy(default_config_data)
conf = Config()
base_level = ['service', 'dns', 'dynamic']
@@ -200,17 +152,17 @@ def get_config():
# Set config back to appropriate level for these options
conf.set_level(base_level + ['interface', interface])
-
+
# Additional settings in CLI
if conf.exists(['use-web', 'skip']):
node['web_skip'] = conf.return_value(['use-web', 'skip'])
if conf.exists(['use-web', 'url']):
node['web_url'] = conf.return_value(['use-web', 'url'])
-
+
# set config level back to top level
conf.set_level(base_level)
-
+
dyndns['interfaces'].append(node)
return dyndns
@@ -272,6 +224,11 @@ def generate(dyndns):
return None
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'dynamic-dns')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
dirname = os.path.dirname(dyndns['pid_file'])
if not os.path.exists(dirname):
os.mkdir(dirname)
@@ -280,7 +237,7 @@ def generate(dyndns):
if not os.path.exists(dirname):
os.mkdir(dirname)
- tmpl = jinja2.Template(config_tmpl)
+ tmpl = env.get_template('ddclient.conf.tmpl')
config_text = tmpl.render(dyndns)
with open(config_file, 'w') as f:
f.write(config_text)
@@ -314,4 +271,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py
index 2e941de0a..b040c8b64 100755
--- a/src/conf_mode/flow_accounting_conf.py
+++ b/src/conf_mode/flow_accounting_conf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,17 +13,19 @@
#
# 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 sys
+import os
import re
-import ipaddress
import subprocess
+from ipaddress import ip_address
+from jinja2 import FileSystemLoader, Environment
+from sys import exit
+
+from vyos.ifconfig import Interface
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
-from vyos.ifconfig import Interface
-from jinja2 import Template
# default values
default_sflow_server_port = 6343
@@ -36,78 +38,6 @@ uacctd_conf_path = '/etc/pmacct/uacctd.conf'
iptables_nflog_table = 'raw'
iptables_nflog_chain = 'VYATTA_CT_PREROUTING_HOOK'
-# pmacct config template
-uacct_conf_jinja = '''# Genereated from VyOS configuration
-daemonize: true
-promisc: false
-pidfile: /var/run/uacctd.pid
-uacctd_group: 2
-uacctd_nl_size: 2097152
-snaplen: {{ snaplen }}
-aggregate: in_iface,src_mac,dst_mac,vlan,src_host,dst_host,src_port,dst_port,proto,tos,flows
-plugin_pipe_size: {{ templatecfg['plugin_pipe_size'] }}
-plugin_buffer_size: {{ templatecfg['plugin_buffer_size'] }}
-{%- if templatecfg['syslog-facility'] != none %}
-syslog: {{ templatecfg['syslog-facility'] }}
-{%- endif %}
-{%- if templatecfg['disable-imt'] == none %}
-imt_path: /tmp/uacctd.pipe
-imt_mem_pools_number: 169
-{%- endif %}
-plugins:
-{%- if templatecfg['netflow']['servers'] != none -%}
- {% for server in templatecfg['netflow']['servers'] %}
- {%- if loop.last -%}nfprobe[nf_{{ server['address'] }}]{%- else %}nfprobe[nf_{{ server['address'] }}],{%- endif %}
- {%- endfor -%}
- {% set plugins_presented = true %}
-{%- endif %}
-{%- if templatecfg['sflow']['servers'] != none -%}
- {% if plugins_presented -%}
- {%- for server in templatecfg['sflow']['servers'] -%}
- ,sfprobe[sf_{{ server['address'] }}]
- {%- endfor %}
- {%- else %}
- {%- for server in templatecfg['sflow']['servers'] %}
- {%- if loop.last -%}sfprobe[sf_{{ server['address'] }}]{%- else %}sfprobe[sf_{{ server['address'] }}],{%- endif %}
- {%- endfor %}
- {%- endif -%}
- {% set plugins_presented = true %}
-{%- endif %}
-{%- if templatecfg['disable-imt'] == none %}
- {%- if plugins_presented -%},memory{%- else %}memory{%- endif %}
-{%- endif %}
-{%- if templatecfg['netflow']['servers'] != none %}
-{%- for server in templatecfg['netflow']['servers'] %}
-nfprobe_receiver[nf_{{ server['address'] }}]: {{ server['address'] }}:{{ server['port'] }}
-nfprobe_version[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['version'] }}
-{%- if templatecfg['netflow']['engine-id'] != none %}
-nfprobe_engine[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['engine-id'] }}
-{%- endif %}
-{%- if templatecfg['netflow']['max-flows'] != none %}
-nfprobe_maxflows[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['max-flows'] }}
-{%- endif %}
-{%- if templatecfg['netflow']['sampling-rate'] != none %}
-sampling_rate[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['sampling-rate'] }}
-{%- endif %}
-{%- if templatecfg['netflow']['source-ip'] != none %}
-nfprobe_source_ip[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['source-ip'] }}
-{%- endif %}
-{%- if templatecfg['netflow']['timeout_string'] != '' %}
-nfprobe_timeouts[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['timeout_string'] }}
-{%- endif %}
-{%- endfor %}
-{%- endif %}
-{%- if templatecfg['sflow']['servers'] != none %}
-{%- for server in templatecfg['sflow']['servers'] %}
-sfprobe_receiver[sf_{{ server['address'] }}]: {{ server['address'] }}:{{ server['port'] }}
-sfprobe_agentip[sf_{{ server['address'] }}]: {{ templatecfg['sflow']['agent-address'] }}
-{%- if templatecfg['sflow']['sampling-rate'] != none %}
-sampling_rate[sf_{{ server['address'] }}]: {{ templatecfg['sflow']['sampling-rate'] }}
-{%- endif %}
-{%- endfor %}
-{% endif %}
-'''
-
# helper functions
# check if node exists and return True if this is true
def _node_exists(path):
@@ -154,7 +84,7 @@ def _iptables_get_nflog():
stdout, stderr = process.communicate()
if not process.returncode == 0:
print("Failed to get flows list: command \"{}\" returned exit code: {}\nError: {}".format(command, process.returncode, stderr))
- sys.exit(1)
+ exit(1)
iptables_out = stdout.splitlines()
# parse each line and add information to list
@@ -179,7 +109,7 @@ def _iptables_config(configured_ifaces):
# get currently configured interfaces with iptables rules
active_nflog_rules = _iptables_get_nflog()
-
+
# compare current active list with configured one and delete excessive interfaces, add missed
active_nflog_ifaces = []
for rule in active_nflog_rules:
@@ -314,15 +244,15 @@ def verify(config):
sflow_collector_ipver = None
for sflow_collector in config['sflow']['servers']:
if sflow_collector_ipver:
- if sflow_collector_ipver != ipaddress.ip_address(sflow_collector['address']).version:
+ if sflow_collector_ipver != ip_address(sflow_collector['address']).version:
raise ConfigError("All sFlow servers must use the same IP protocol")
else:
- sflow_collector_ipver = ipaddress.ip_address(sflow_collector['address']).version
+ sflow_collector_ipver = ip_address(sflow_collector['address']).version
# check agent-id for sFlow: we should avoid mixing IPv4 agent-id with IPv6 collectors and vice-versa
for sflow_collector in config['sflow']['servers']:
- if ipaddress.ip_address(sflow_collector['address']).version != ipaddress.ip_address(config['sflow']['agent-address']).version:
+ if ip_address(sflow_collector['address']).version != ip_address(config['sflow']['agent-address']).version:
raise ConfigError("Different IP address versions cannot be mixed in \"sflow agent-address\" and \"sflow server\". You need to set manually the same IP version for \"agent-address\" as for all sFlow servers")
# check if configured sFlow agent-id exist in the system
@@ -378,7 +308,7 @@ def generate(config):
# skip all checks if flow-accounting was removed
if not config['flow-accounting-configured']:
return True
-
+
# Calculate all necessary values
if config['buffer-size']:
# circular queue size
@@ -399,13 +329,16 @@ def generate(config):
timeout_string = "{}:{}={}".format(timeout_string, timeout_type, timeout_value)
config['netflow']['timeout_string'] = timeout_string
- # Generate daemon configs
- uacct_conf_template = Template(uacct_conf_jinja)
- uacct_conf_file_data = uacct_conf_template.render(templatecfg = config, snaplen = default_captured_packet_size)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'netflow')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
- # save generated config to uacctd.conf
+ # Generate daemon configs
+ tmpl = env.get_template('uacctd.conf.tmpl')
+ config_text = tmpl.render(templatecfg = config, snaplen = default_captured_packet_size)
with open(uacctd_conf_path, 'w') as file:
- file.write(uacct_conf_file_data)
+ file.write(config_text)
def apply(config):
@@ -436,4 +369,4 @@ if __name__ == '__main__':
apply(config)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py
index 889b62cf4..83a5f3602 100755
--- a/src/conf_mode/https.py
+++ b/src/conf_mode/https.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# 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
@@ -13,88 +13,22 @@
#
# 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 sys
import os
-from copy import deepcopy
-import jinja2
+from sys import exit
+from copy import deepcopy
+from jinja2 import FileSystemLoader, Environment
import vyos.defaults
import vyos.certbot_util
+
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
config_file = '/etc/nginx/sites-available/default'
-# Please be careful if you edit the template.
-config_tmpl = """
-
-### Autogenerated by https.py ###
-# Default server configuration
-#
-server {
- listen 80 default_server;
- listen [::]:80 default_server;
- server_name _;
- return 301 https://$server_name$request_uri;
-}
-
-{% for server in server_block_list %}
-server {
-
- # SSL configuration
- #
-{% if server.address == '*' %}
- listen {{ server.port }} ssl;
- listen [::]:{{ server.port }} ssl;
-{% else %}
- listen {{ server.address }}:{{ server.port }} ssl;
-{% endif %}
-
-{% for name in server.name %}
- server_name {{ name }};
-{% endfor %}
-
-{% if server.certbot %}
- ssl_certificate /etc/letsencrypt/live/{{ server.certbot_dir }}/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/{{ server.certbot_dir }}/privkey.pem;
- include /etc/letsencrypt/options-ssl-nginx.conf;
- ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
-{% elif server.vyos_cert %}
- include {{ server.vyos_cert.conf }};
-{% else %}
- #
- # Self signed certs generated by the ssl-cert package
- # Don't use them in a production server!
- #
- include snippets/snakeoil.conf;
-{% endif %}
-
- # proxy settings for HTTP API, if enabled; 503, if not
- location ~ /(retrieve|configure|config-file|image|generate|show) {
-{% if server.api %}
- proxy_pass http://localhost:{{ server.api.port }};
- proxy_buffering off;
-{% else %}
- return 503;
-{% endif %}
- }
-
- error_page 501 502 503 =200 @50*_json;
-
- location @50*_json {
- default_type application/json;
- return 200 '{"error": "Start service in configuration mode: set service https api"}';
- }
-
-}
-
-{% endfor %}
-"""
-
default_server_block = {
'id' : '',
'address' : '*',
@@ -193,10 +127,15 @@ def generate(https):
if https is None:
return None
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'https')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader, trim_blocks=True)
+
if 'server_block_list' not in https or not https['server_block_list']:
https['server_block_list'] = [default_server_block]
- tmpl = jinja2.Template(config_tmpl, trim_blocks=True)
+ tmpl = env.get_template('nginx.default.tmpl')
config_text = tmpl.render(https)
with open(config_file, 'w') as f:
f.write(config_text)
@@ -217,4 +156,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/igmp_proxy.py b/src/conf_mode/igmp_proxy.py
index cd0704124..aa46f2c4e 100755
--- a/src/conf_mode/igmp_proxy.py
+++ b/src/conf_mode/igmp_proxy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,60 +13,20 @@
#
# 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 sys
import os
-import jinja2
+
+from sys import exit
+from copy import deepcopy
+from jinja2 import FileSystemLoader, Environment
from netifaces import interfaces
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
config_file = r'/etc/igmpproxy.conf'
-# Please be careful if you edit the template.
-config_tmpl = """
-########################################################
-#
-# autogenerated by igmp_proxy.py
-#
-# The configuration file must define one upstream
-# interface, and one or more downstream interfaces.
-#
-# If multicast traffic originates outside the
-# upstream subnet, the "altnet" option can be
-# used in order to define legal multicast sources.
-# (Se example...)
-#
-# The "quickleave" should be used to avoid saturation
-# of the upstream link. The option should only
-# be used if it's absolutely nessecary to
-# accurately imitate just one Client.
-#
-########################################################
-
-{% if not disable_quickleave -%}
-quickleave
-{% endif -%}
-
-{% for interface in interfaces %}
-# Configuration for {{ interface.name }} ({{ interface.role }} interface)
-{% if interface.role == 'disabled' -%}
-phyint {{ interface.name }} disabled
-{%- else -%}
-phyint {{ interface.name }} {{ interface.role }} ratelimit 0 threshold {{ interface.threshold }}
-{%- endif -%}
-{%- for subnet in interface.alt_subnet %}
- altnet {{ subnet }}
-{%- endfor %}
-{%- for subnet in interface.whitelist %}
- whitelist {{ subnet }}
-{%- endfor %}
-{% endfor %}
-"""
-
default_config_data = {
'disable': False,
'disable_quickleave': False,
@@ -74,23 +34,24 @@ default_config_data = {
}
def get_config():
- igmp_proxy = default_config_data
+ igmp_proxy = deepcopy(default_config_data)
conf = Config()
- if not conf.exists('protocols igmp-proxy'):
+ base = ['protocols', 'igmp-proxy']
+ if not conf.exists(base):
return None
else:
- conf.set_level('protocols igmp-proxy')
+ conf.set_level(base)
# Network interfaces to listen on
- if conf.exists('disable'):
+ if conf.exists(['disable']):
igmp_proxy['disable'] = True
# Option to disable "quickleave"
- if conf.exists('disable-quickleave'):
+ if conf.exists(['disable-quickleave']):
igmp_proxy['disable_quickleave'] = True
- for intf in conf.list_nodes('interface'):
- conf.set_level('protocols igmp-proxy interface {0}'.format(intf))
+ for intf in conf.list_nodes(['interface']):
+ conf.set_level(base + ['interface', intf])
interface = {
'name': intf,
'alt_subnet': [],
@@ -99,17 +60,17 @@ def get_config():
'whitelist': []
}
- if conf.exists('alt-subnet'):
- interface['alt_subnet'] = conf.return_values('alt-subnet')
+ if conf.exists(['alt-subnet']):
+ interface['alt_subnet'] = conf.return_values(['alt-subnet'])
- if conf.exists('role'):
- interface['role'] = conf.return_value('role')
+ if conf.exists(['role']):
+ interface['role'] = conf.return_value(['role'])
- if conf.exists('threshold'):
- interface['threshold'] = conf.return_value('threshold')
+ if conf.exists(['threshold']):
+ interface['threshold'] = conf.return_value(['threshold'])
- if conf.exists('whitelist'):
- interface['whitelist'] = conf.return_values('whitelist')
+ if conf.exists(['whitelist']):
+ interface['whitelist'] = conf.return_values(['whitelist'])
# Append interface configuration to global configuration list
igmp_proxy['interfaces'].append(interface)
@@ -153,7 +114,12 @@ def generate(igmp_proxy):
print('Warning: IGMP Proxy will be deactivated because it is disabled')
return None
- tmpl = jinja2.Template(config_tmpl)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'igmp-proxy')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
+ tmpl = env.get_template('igmpproxy.conf.tmpl')
config_text = tmpl.render(igmp_proxy)
with open(config_file, 'w') as f:
f.write(config_text)
@@ -167,7 +133,7 @@ def apply(igmp_proxy):
if os.path.exists(config_file):
os.unlink(config_file)
else:
- os.system('sudo systemctl restart igmpproxy.service')
+ os.system('systemctl restart igmpproxy.service')
return None
@@ -179,4 +145,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index fb2d6e6d9..faaee9ac0 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# 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
@@ -17,7 +17,7 @@
import os
import re
-from jinja2 import Template
+from jinja2 import FileSystemLoader, Environment
from copy import deepcopy
from sys import exit
from stat import S_IRUSR,S_IRWXU,S_IRGRP,S_IXGRP,S_IROTH,S_IXOTH
@@ -29,257 +29,16 @@ from subprocess import Popen, PIPE
from time import sleep
from shutil import rmtree
-from vyos import ConfigError
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos.ifconfig import VTunIf
from vyos.util import process_running
from vyos.validate import is_addr_assigned
+from vyos import ConfigError
user = 'openvpn'
group = 'openvpn'
-# Please be careful if you edit the template.
-config_tmpl = """
-### Autogenerated by interfaces-openvpn.py ###
-#
-# See https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
-# for individual keyword definition
-
-{% if description %}
-# {{ description }}
-{% endif %}
-
-verb 3
-status /opt/vyatta/etc/openvpn/status/{{ intf }}.status 30
-writepid /var/run/openvpn/{{ intf }}.pid
-
-dev-type {{ type }}
-dev {{ intf }}
-user {{ uid }}
-group {{ gid }}
-persist-key
-iproute /usr/libexec/vyos/system/unpriv-ip
-
-proto {% if 'tcp-active' in protocol -%}tcp-client{% elif 'tcp-passive' in protocol -%}tcp-server{% else %}udp{% endif %}
-
-{%- if local_host %}
-local {{ local_host }}
-{% endif %}
-
-{%- if local_port %}
-lport {{ local_port }}
-{% endif %}
-
-{%- if remote_port %}
-rport {{ remote_port }}
-{% endif %}
-
-{%- if remote_host %}
-{% for remote in remote_host -%}
-remote {{ remote }}
-{% endfor -%}
-{% endif %}
-
-{%- if shared_secret_file %}
-secret {{ shared_secret_file }}
-{% endif %}
-
-{%- if persistent_tunnel %}
-persist-tun
-{% endif %}
-
-{%- if mode %}
-{%- if 'client' in mode %}
-#
-# OpenVPN Client mode
-#
-client
-nobind
-{%- elif 'server' in mode %}
-#
-# OpenVPN Server mode
-#
-mode server
-tls-server
-keepalive {{ ping_interval }} {{ ping_restart }}
-management /tmp/openvpn-mgmt-intf unix
-
-{%- if server_topology %}
-topology {% if 'point-to-point' in server_topology %}p2p{% else %}subnet{% endif %}
-{% endif %}
-
-{% for ns in server_dns_nameserver -%}
-push "dhcp-option DNS {{ ns }}"
-{% endfor -%}
-
-{% for route in server_push_route -%}
-push "route {{ route }}"
-{% endfor -%}
-
-{%- if server_domain %}
-push "dhcp-option DOMAIN {{ server_domain }}"
-{% endif %}
-
-{%- if server_max_conn %}
-max-clients {{ server_max_conn }}
-{% endif %}
-
-{%- if bridge_member %}
-server-bridge nogw
-{%- else %}
-server {{ server_subnet }}
-{% endif %}
-
-{%- if server_reject_unconfigured %}
-ccd-exclusive
-{% endif %}
-
-{%- else %}
-#
-# OpenVPN site-2-site mode
-#
-ping {{ ping_interval }}
-ping-restart {{ ping_restart }}
-
-{%- if local_address_subnet %}
-ifconfig {{ local_address }} {{ local_address_subnet }}
-{% elif remote_address %}
-ifconfig {{ local_address }} {{ remote_address }}
-{% endif %}
-
-{% endif %}
-{% endif %}
-
-{%- if tls_ca_cert %}
-ca {{ tls_ca_cert }}
-{% endif %}
-
-{%- if tls_cert %}
-cert {{ tls_cert }}
-{% endif %}
-
-{%- if tls_key %}
-key {{ tls_key }}
-{% endif %}
-
-{%- if tls_crypt %}
-tls-crypt {{ tls_crypt }}
-{% endif %}
-
-{%- if tls_crl %}
-crl-verify {{ tls_crl }}
-{% endif %}
-
-{%- if tls_version_min %}
-tls-version-min {{tls_version_min}}
-{% endif %}
-
-{%- if tls_dh %}
-dh {{ tls_dh }}
-{% endif %}
-
-{%- if tls_auth %}
-tls-auth {{tls_auth}}
-{% endif %}
-
-{%- if 'active' in tls_role %}
-tls-client
-{%- elif 'passive' in tls_role %}
-tls-server
-{% endif %}
-
-{%- if redirect_gateway %}
-push "redirect-gateway {{ redirect_gateway }}"
-{% endif %}
-
-{%- if compress_lzo %}
-compress lzo
-{% endif %}
-
-{%- if hash %}
-auth {{ hash }}
-{% endif %}
-
-{%- if encryption %}
-{%- if 'des' in encryption %}
-cipher des-cbc
-{%- elif '3des' in encryption %}
-cipher des-ede3-cbc
-{%- elif 'bf128' in encryption %}
-cipher bf-cbc
-keysize 128
-{%- elif 'bf256' in encryption %}
-cipher bf-cbc
-keysize 25
-{%- elif 'aes128gcm' in encryption %}
-cipher aes-128-gcm
-{%- elif 'aes128' in encryption %}
-cipher aes-128-cbc
-{%- elif 'aes192gcm' in encryption %}
-cipher aes-192-gcm
-{%- elif 'aes192' in encryption %}
-cipher aes-192-cbc
-{%- elif 'aes256gcm' in encryption %}
-cipher aes-256-gcm
-{%- elif 'aes256' in encryption %}
-cipher aes-256-cbc
-{% endif %}
-{% endif %}
-
-{%- if ncp_ciphers %}
-ncp-ciphers {{ncp_ciphers}}
-{% endif %}
-{%- if disable_ncp %}
-ncp-disable
-{% endif %}
-
-{%- if auth %}
-auth-user-pass /tmp/openvpn-{{ intf }}-pw
-auth-retry nointeract
-{% endif %}
-
-{%- if client %}
-client-config-dir /opt/vyatta/etc/openvpn/ccd/{{ intf }}
-{% endif %}
-
-# DEPRECATED This option will be removed in OpenVPN 2.5
-# Until OpenVPN v2.3 the format of the X.509 Subject fields was formatted like this:
-# /C=US/L=Somewhere/CN=John Doe/emailAddress=john@example.com In addition the old
-# behaviour was to remap any character other than alphanumeric, underscore ('_'),
-# dash ('-'), dot ('.'), and slash ('/') to underscore ('_'). The X.509 Subject
-# string as returned by the tls_id environmental variable, could additionally
-# contain colon (':') or equal ('='). When using the --compat-names option, this
-# old formatting and remapping will be re-enabled again. This is purely implemented
-# for compatibility reasons when using older plug-ins or scripts which does not
-# handle the new formatting or UTF-8 characters.
-#
-# See https://phabricator.vyos.net/T1512
-compat-names
-
-{% for option in options -%}
-{{ option }}
-{% endfor -%}
-"""
-
-client_tmpl = """
-### Autogenerated by interfaces-openvpn.py ###
-
-{% if ip -%}
-ifconfig-push {{ ip }} {{ remote_netmask }}
-{% endif -%}
-{% for route in push_route -%}
-push "route {{ route }}"
-{% endfor -%}
-
-{% for net in subnet -%}
-iroute {{ net }}
-{% endfor -%}
-
-{% if disable -%}
-disable
-{% endif -%}
-"""
-
default_config_data = {
'address': [],
'auth_user': '',
@@ -308,7 +67,7 @@ default_config_data = {
'ncp_ciphers': '',
'options': [],
'persistent_tunnel': False,
- 'protocol': '',
+ 'protocol': 'udp',
'redirect_gateway': '',
'remote_address': '',
'remote_host': [],
@@ -512,8 +271,7 @@ def get_config():
# OpenVPN operation mode
if conf.exists('mode'):
- mode = conf.return_value('mode')
- openvpn['mode'] = mode
+ openvpn['mode'] = conf.return_value('mode')
# Additional OpenVPN options
if conf.exists('openvpn-option'):
@@ -917,6 +675,11 @@ def generate(openvpn):
if openvpn['deleted'] or openvpn['disable']:
return None
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'openvpn')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
interface = openvpn['intf']
directory = os.path.dirname(get_config_name(interface))
@@ -957,19 +720,17 @@ def generate(openvpn):
# Generate client specific configuration
for client in openvpn['client']:
client_file = directory + '/ccd/' + interface + '/' + client['name']
- tmpl = Template(client_tmpl)
+ tmpl = env.get_template('client.conf.tmpl')
client_text = tmpl.render(client)
with open(client_file, 'w') as f:
f.write(client_text)
os.chown(client_file, uid, gid)
- tmpl = Template(config_tmpl)
+ tmpl = env.get_template('server.conf.tmpl')
config_text = tmpl.render(openvpn)
-
# we need to support quoting of raw parameters from OpenVPN CLI
# see https://phabricator.vyos.net/T1632
config_text = config_text.replace("&quot;",'"')
-
with open(get_config_name(interface), 'w') as f:
f.write(config_text)
os.chown(get_config_name(interface), uid, gid)
diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py
index 9c045534c..a396af4ea 100755
--- a/src/conf_mode/interfaces-pppoe.py
+++ b/src/conf_mode/interfaces-pppoe.py
@@ -18,153 +18,14 @@ import os
from sys import exit
from copy import deepcopy
-from jinja2 import Template
-from subprocess import Popen, PIPE
+from jinja2 import FileSystemLoader, Environment
+from netifaces import interfaces
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos.ifconfig import Interface
-from vyos.util import chown_file, chmod_x_file
+from vyos.util import chown_file, chmod_x, subprocess_cmd
from vyos import ConfigError
-from netifaces import interfaces
-
-# Please be careful if you edit the template.
-config_pppoe_tmpl = """### Autogenerated by interfaces-pppoe.py ###
-{% if description %}
-# {{ description }}
-{% endif %}
-
-# Require peer to provide the local IP address if it is not
-# specified explicitly in the config file.
-noipdefault
-
-# Don't show the password in logfiles:
-hide-password
-
-# Standard Link Control Protocol (LCP) parameters:
-lcp-echo-interval 20
-lcp-echo-failure 3
-
-# RFC 2516, paragraph 7 mandates that the following options MUST NOT be
-# requested and MUST be rejected if requested by the peer:
-# Address-and-Control-Field-Compression (ACFC)
-noaccomp
-
-# Asynchronous-Control-Character-Map (ACCM)
-default-asyncmap
-
-# Override any connect script that may have been set in /etc/ppp/options.
-connect /bin/true
-
-# Don't try to authenticate the remote node
-noauth
-
-# Don't try to proxy ARP for the remote endpoint. User can set proxy
-# arp entries up manually if they wish. More importantly, having
-# the "proxyarp" parameter set disables the "defaultroute" option.
-noproxyarp
-
-# Unlimited connection attempts
-maxfail 0
-
-plugin rp-pppoe.so
-{{ source_interface }}
-persist
-ifname {{ intf }}
-ipparam {{ intf }}
-debug
-logfile {{ logfile }}
-{% if 'auto' in default_route -%}
-defaultroute
-{% elif 'force' in default_route -%}
-defaultroute
-replacedefaultroute
-{% endif %}
-mtu {{ mtu }}
-mru {{ mtu }}
-user "{{ auth_username }}"
-password "{{ auth_password }}"
-{% if name_server -%}
-usepeerdns
-{% endif %}
-{% if ipv6_enable -%}
-+ipv6
-ipv6cp-use-ipaddr
-{% endif %}
-{% if service_name -%}
-rp_pppoe_service "{{ service_name }}"
-{% endif %}
-{% if on_demand %}
-demand
-{% endif %}
-
-"""
-
-# Please be careful if you edit the template.
-# There must be no blank line at the top pf the script file
-config_pppoe_ipv6_up_tmpl = """#!/bin/sh
-
-# As PPPoE is an "on demand" interface we need to re-configure it when it
-# becomes up
-
-if [ "$6" != "{{ intf }}" ]; then
- exit
-fi
-
-{% if ipv6_autoconf -%}
-# add some info to syslog
-DIALER_PID=$(cat /var/run/{{ intf }}.pid)
-logger -t pppd[$DIALER_PID] "executing $0"
-logger -t pppd[$DIALER_PID] "configuring interface {{ intf }} via $2"
-
-# Configure interface-specific Host/Router behaviour.
-# Note: It is recommended to have the same setting on all interfaces; mixed
-# router/host scenarios are rather uncommon. Possible values are:
-#
-# 0 Forwarding disabled
-# 1 Forwarding enabled
-#
-echo 1 > /proc/sys/net/ipv6/conf/{{ intf }}/forwarding
-
-# Accept Router Advertisements; autoconfigure using them.
-#
-# It also determines whether or not to transmit Router
-# Solicitations. If and only if the functional setting is to
-# accept Router Advertisements, Router Solicitations will be
-# transmitted. Possible values are:
-#
-# 0 Do not accept Router Advertisements.
-# 1 Accept Router Advertisements if forwarding is disabled.
-# 2 Overrule forwarding behaviour. Accept Router Advertisements
-# even if forwarding is enabled.
-#
-echo 2 > /proc/sys/net/ipv6/conf/{{ intf }}/accept_ra
-
-# Autoconfigure addresses using Prefix Information in Router Advertisements.
-echo 1 > /proc/sys/net/ipv6/conf/{{ intf }}/autoconfigure
-{% endif %}
-"""
-
-config_pppoe_ip_up_tmpl = """#!/bin/sh
-
-# As PPPoE is an "on demand" interface we need to re-configure it when it
-# becomes up
-
-if [ "$6" != "{{ intf }}" ]; then
- exit
-fi
-
-# add some info to syslog
-DIALER_PID=$(cat /var/run/{{ intf }}.pid)
-logger -t pppd[$DIALER_PID] "executing $0"
-
-echo "{{ description }}" > /sys/class/net/{{ intf }}/ifalias
-
-{% if vrf -%}
-logger -t pppd[$DIALER_PID] "configuring dialer interface $6 for VRF {{ vrf }}"
-ip link set dev {{ intf }} master {{ vrf }}
-{% endif %}
-
-"""
default_config_data = {
'access_concentrator': '',
@@ -189,10 +50,6 @@ default_config_data = {
'vrf': ''
}
-def subprocess_cmd(command):
- p = Popen(command, stdout=PIPE, shell=True)
- p.communicate()
-
def get_config():
pppoe = deepcopy(default_config_data)
conf = Config()
@@ -301,12 +158,22 @@ def verify(pppoe):
return None
def generate(pppoe):
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir["data"], "templates", "pppoe")
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
+ # set up configuration file path variables where our templates will be
+ # rendered into
intf = pppoe['intf']
- config_file_pppoe = f'/etc/ppp/peers/{intf}'
- ip_up_script_file = f'/etc/ppp/ip-up.d/9990-vyos-vrf-{intf}'
- ipv6_if_up_script_file = f'/etc/ppp/ipv6-up.d/9990-vyos-autoconf-{intf}'
+ config_pppoe = f'/etc/ppp/peers/{intf}'
+ script_pppoe_pre_up = f'/etc/ppp/ip-pre-up.d/1000-vyos-pppoe-{intf}'
+ script_pppoe_ip_up = f'/etc/ppp/ip-up.d/1000-vyos-pppoe-{intf}'
+ script_pppoe_ip_down = f'/etc/ppp/ip-down.d/1000-vyos-pppoe-{intf}'
+ script_pppoe_ipv6_up = f'/etc/ppp/ipv6-up.d/1000-vyos-pppoe-{intf}'
- config_files = [config_file_pppoe, ip_up_script_file, ipv6_if_up_script_file]
+ config_files = [config_pppoe, script_pppoe_pre_up, script_pppoe_ip_up,
+ script_pppoe_ip_down, script_pppoe_ipv6_up]
# Ensure directories for config files exist - otherwise create them on demand
for file in config_files:
@@ -326,24 +193,40 @@ def generate(pppoe):
else:
# Create PPP configuration files
- tmpl = Template(config_pppoe_tmpl)
+ tmpl = env.get_template('peer.tmpl')
+ config_text = tmpl.render(pppoe)
+ with open(config_pppoe, 'w') as f:
+ f.write(config_text)
+
+ # Create script for ip-pre-up.d
+ tmpl = env.get_template('ip-pre-up.script.tmpl')
+ config_text = tmpl.render(pppoe)
+ with open(script_pppoe_pre_up, 'w') as f:
+ f.write(config_text)
+
+ # Create script for ip-up.d
+ tmpl = env.get_template('ip-up.script.tmpl')
config_text = tmpl.render(pppoe)
- with open(config_file_pppoe, 'w') as f:
+ with open(script_pppoe_ip_up, 'w') as f:
f.write(config_text)
- tmpl = Template(config_pppoe_ip_up_tmpl)
+ # Create script for ip-down.d
+ tmpl = env.get_template('ip-down.script.tmpl')
config_text = tmpl.render(pppoe)
- with open(ip_up_script_file, 'w') as f:
+ with open(script_pppoe_ip_down, 'w') as f:
f.write(config_text)
- tmpl = Template(config_pppoe_ipv6_up_tmpl)
+ # Create script for ipv6-up.d
+ tmpl = env.get_template('ipv6-up.script.tmpl')
config_text = tmpl.render(pppoe)
- with open(ipv6_if_up_script_file, 'w') as f:
+ with open(script_pppoe_ipv6_up, 'w') as f:
f.write(config_text)
# make generated script file executable
- chmod_x_file(ip_up_script_file)
- chmod_x_file(ipv6_if_up_script_file)
+ chmod_x(script_pppoe_pre_up)
+ chmod_x(script_pppoe_ip_up)
+ chmod_x(script_pppoe_ip_down)
+ chmod_x(script_pppoe_ipv6_up)
return None
diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py
index 3d2638c6f..1f9636729 100755
--- a/src/conf_mode/interfaces-vxlan.py
+++ b/src/conf_mode/interfaces-vxlan.py
@@ -160,9 +160,6 @@ def verify(vxlan):
if not vxlan['link'] in interfaces():
raise ConfigError('VXLAN source interface does not exist')
- if not (vxlan['group'] or vxlan['remote']):
- raise ConfigError('Group or remote must be configured')
-
if not vxlan['vni']:
raise ConfigError('Must configure VNI for VXLAN')
diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py
index d8c327e19..5c0c07dc4 100755
--- a/src/conf_mode/interfaces-wireguard.py
+++ b/src/conf_mode/interfaces-wireguard.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,13 +13,12 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#
import sys
import os
import re
import subprocess
+
from copy import deepcopy
from netifaces import interfaces
@@ -30,10 +29,9 @@ from vyos.ifconfig import WireGuardIf
kdir = r'/config/auth/wireguard'
-
def _check_kmod():
if not os.path.exists('/sys/module/wireguard'):
- if os.system('sudo modprobe wireguard') != 0:
+ if os.system('modprobe wireguard') != 0:
raise ConfigError("modprobe wireguard failed")
@@ -135,7 +133,8 @@ def get_config():
{
p: {
'allowed-ips': [],
- 'endpoint': '',
+ 'address': '',
+ 'port': '',
'pubkey': ''
}
}
@@ -144,10 +143,14 @@ def get_config():
if c.exists(['peer', p, 'allowed-ips']):
wg['peer'][p]['allowed-ips'] = c.return_values(
['peer', p, 'allowed-ips'])
- # peer endpoint
- if c.exists(['peer', p, 'endpoint']):
- wg['peer'][p]['endpoint'] = c.return_value(
- ['peer', p, 'endpoint'])
+ # peer address
+ if c.exists(['peer', p, 'address']):
+ wg['peer'][p]['address'] = c.return_value(
+ ['peer', p, 'address'])
+ # peer port
+ if c.exists(['peer', p, 'port']):
+ wg['peer'][p]['port'] = c.return_value(
+ ['peer', p, 'port'])
# persistent-keepalive
if c.exists(['peer', p, 'persistent-keepalive']):
wg['peer'][p]['persistent-keepalive'] = c.return_value(
@@ -251,8 +254,8 @@ def apply(c):
if c['fwmark']:
intfc.config['fwmark'] = c['fwmark']
# endpoint
- if c['peer'][p]['endpoint']:
- intfc.config['endpoint'] = c['peer'][p]['endpoint']
+ if c['peer'][p]['address'] and c['peer'][p]['port']:
+ intfc.config['endpoint'] = "{}:{}".format(c['peer'][p]['address'], c['peer'][p]['port'])
# persistent-keepalive
if 'persistent-keepalive' in c['peer'][p]:
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index b6e62b0aa..da8470f7e 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -15,763 +15,27 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-
-from jinja2 import Template
-from copy import deepcopy
from sys import exit
-from stat import S_IRWXU,S_IRGRP,S_IXGRP,S_IROTH,S_IXOTH
-from pwd import getpwnam
-from grp import getgrnam
from re import findall
+from copy import deepcopy
+from jinja2 import FileSystemLoader, Environment
+
from subprocess import Popen, PIPE
from netifaces import interfaces
-from netaddr import *
+from netaddr import EUI, mac_unix_expanded
-from vyos import ConfigError
-from vyos.configdict import list_diff, vlan_to_dict
from vyos.config import Config
+from vyos.configdict import list_diff, vlan_to_dict
+from vyos.defaults import directories as vyos_data_dir
from vyos.ifconfig import WiFiIf
from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config
-from vyos.util import process_running
+from vyos.util import process_running, chmod_x, chown_file
+from vyos import ConfigError
user = 'root'
group = 'vyattacfg'
-# Please be careful if you edit the template.
-config_hostapd_tmpl = """
-### Autogenerated by interfaces-wireless.py ###
-{% if description %}
-# Description: {{ description }}
-# User-friendly description of device; up to 32 octets encoded in UTF-8
-device_name={{ description | truncate(32, True) }}
-{% endif %}
-
-# AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for
-# management frames with the Host AP driver); wlan0 with many nl80211 drivers
-# Note: This attribute can be overridden by the values supplied with the '-i'
-# command line parameter.
-interface={{ intf }}
-
-# Driver interface type (hostap/wired/none/nl80211/bsd);
-# default: hostap). nl80211 is used with all Linux mac80211 drivers.
-# Use driver=none if building hostapd as a standalone RADIUS server that does
-# not control any wireless/wired driver.
-driver=nl80211
-
-# Levels (minimum value for logged events):
-# 0 = verbose debugging
-# 1 = debugging
-# 2 = informational messages
-# 3 = notification
-# 4 = warning
-logger_syslog=-1
-logger_syslog_level=0
-logger_stdout=-1
-logger_stdout_level=0
-
-{%- if country_code %}
-
-# Country code (ISO/IEC 3166-1). Used to set regulatory domain.
-# Set as needed to indicate country in which device is operating.
-# This can limit available channels and transmit power.
-country_code={{ country_code }}
-
-# Enable IEEE 802.11d. This advertises the country_code and the set of allowed
-# channels and transmit power levels based on the regulatory limits. The
-# country_code setting must be configured with the correct country for
-# IEEE 802.11d functions.
-ieee80211d=1
-{% endif %}
-
-{%- if ssid %}
-
-# SSID to be used in IEEE 802.11 management frames
-ssid={{ ssid }}
-{% endif %}
-
-{%- if channel %}
-
-# Channel number (IEEE 802.11)
-# (default: 0, i.e., not set)
-# Please note that some drivers do not use this value from hostapd and the
-# channel will need to be configured separately with iwconfig.
-#
-# If CONFIG_ACS build option is enabled, the channel can be selected
-# automatically at run time by setting channel=acs_survey or channel=0, both of
-# which will enable the ACS survey based algorithm.
-channel={{ channel }}
-{% endif %}
-
-{%- if mode %}
-
-# Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz),
-# g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used
-# with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this
-# needs to be set to hw_mode=a. For IEEE 802.11ax (HE) on 6 GHz this needs
-# to be set to hw_mode=a. When using ACS (see channel parameter), a
-# special value "any" can be used to indicate that any support band can be used.
-# This special case is currently supported only with drivers with which
-# offloaded ACS is used.
-{% if 'n' in mode -%}
-hw_mode=g
-ieee80211n=1
-{% elif 'ac' in mode -%}
-hw_mode=a
-ieee80211h=1
-ieee80211ac=1
-{% else -%}
-hw_mode={{ mode }}
-{% endif %}
-{% endif %}
-
-# ieee80211w: Whether management frame protection (MFP) is enabled
-# 0 = disabled (default)
-# 1 = optional
-# 2 = required
-{% if 'disabled' in mgmt_frame_protection -%}
-ieee80211w=0
-{% elif 'optional' in mgmt_frame_protection -%}
-ieee80211w=1
-{% elif 'required' in mgmt_frame_protection -%}
-ieee80211w=2
-{% endif %}
-
-# ht_capab: HT capabilities (list of flags)
-# LDPC coding capability: [LDPC] = supported
-# Supported channel width set: [HT40-] = both 20 MHz and 40 MHz with secondary
-# channel below the primary channel; [HT40+] = both 20 MHz and 40 MHz
-# with secondary channel above the primary channel
-# (20 MHz only if neither is set)
-# Note: There are limits on which channels can be used with HT40- and
-# HT40+. Following table shows the channels that may be available for
-# HT40- and HT40+ use per IEEE 802.11n Annex J:
-# freq HT40- HT40+
-# 2.4 GHz 5-13 1-7 (1-9 in Europe/Japan)
-# 5 GHz 40,48,56,64 36,44,52,60
-# (depending on the location, not all of these channels may be available
-# for use)
-# Please note that 40 MHz channels may switch their primary and secondary
-# channels if needed or creation of 40 MHz channel maybe rejected based
-# on overlapping BSSes. These changes are done automatically when hostapd
-# is setting up the 40 MHz channel.
-# Spatial Multiplexing (SM) Power Save: [SMPS-STATIC] or [SMPS-DYNAMIC]
-# (SMPS disabled if neither is set)
-# HT-greenfield: [GF] (disabled if not set)
-# Short GI for 20 MHz: [SHORT-GI-20] (disabled if not set)
-# Short GI for 40 MHz: [SHORT-GI-40] (disabled if not set)
-# Tx STBC: [TX-STBC] (disabled if not set)
-# Rx STBC: [RX-STBC1] (one spatial stream), [RX-STBC12] (one or two spatial
-# streams), or [RX-STBC123] (one, two, or three spatial streams); Rx STBC
-# disabled if none of these set
-# HT-delayed Block Ack: [DELAYED-BA] (disabled if not set)
-# Maximum A-MSDU length: [MAX-AMSDU-7935] for 7935 octets (3839 octets if not
-# set)
-# DSSS/CCK Mode in 40 MHz: [DSSS_CCK-40] = allowed (not allowed if not set)
-# 40 MHz intolerant [40-INTOLERANT] (not advertised if not set)
-# L-SIG TXOP protection support: [LSIG-TXOP-PROT] (disabled if not set)
-{% if cap_ht %}
-ht_capab=
-{%- endif -%}
-
-{%- if cap_ht_40mhz_incapable -%}
-[40-INTOLERANT]
-{%- endif -%}
-
-{%- if cap_ht_delayed_block_ack -%}
-[DELAYED-BA]
-{%- endif -%}
-
-{%- if cap_ht_dsss_cck_40 -%}
-[DSSS_CCK-40]
-{%- endif -%}
-
-{%- if cap_ht_greenfield -%}
-[GF]
-{%- endif -%}
-
-{%- if cap_ht_ldpc -%}
-[LDPC]
-{%- endif -%}
-
-{%- if cap_ht_lsig_protection -%}
-[LSIG-TXOP-PROT]
-{%- endif -%}
-
-{%- if cap_ht_max_amsdu -%}
-[MAX-AMSDU-{{ cap_ht_max_amsdu }}]
-{%- endif -%}
-
-{%- if cap_ht_smps -%}
-[SMPS-{{ cap_ht_smps | upper }}]
-{%- endif -%}
-
-{%- if cap_ht_chan_set_width -%}
-{%- for csw in cap_ht_chan_set_width -%}
-[{{ csw | upper }}]
-{%- endfor -%}
-{%- endif -%}
-
-{%- if cap_ht_short_gi -%}
-{%- for gi in cap_ht_short_gi -%}
-[SHORT-GI-{{ gi }}]
-{%- endfor -%}
-{%- endif -%}
-
-{%- if cap_ht_stbc_tx -%}
-[TX-STBC]
-{%- endif -%}
-{%- if cap_ht_stbc_rx -%}
-[RX-STBC{{ cap_ht_stbc_rx }}]
-{%- endif %}
-
-# Required for full HT and VHT functionality
-wme_enabled=1
-
-{% if cap_ht_powersave -%}
-# WMM-PS Unscheduled Automatic Power Save Delivery [U-APSD]
-# Enable this flag if U-APSD supported outside hostapd (eg., Firmware/driver)
-uapsd_advertisement_enabled=1
-{%- endif %}
-
-{% if cap_req_ht -%}
-# Require stations to support HT PHY (reject association if they do not)
-require_ht=1
-{% endif %}
-
-# vht_capab: VHT capabilities (list of flags)
-#
-# vht_max_mpdu_len: [MAX-MPDU-7991] [MAX-MPDU-11454]
-# Indicates maximum MPDU length
-# 0 = 3895 octets (default)
-# 1 = 7991 octets
-# 2 = 11454 octets
-# 3 = reserved
-#
-# supported_chan_width: [VHT160] [VHT160-80PLUS80]
-# Indicates supported Channel widths
-# 0 = 160 MHz & 80+80 channel widths are not supported (default)
-# 1 = 160 MHz channel width is supported
-# 2 = 160 MHz & 80+80 channel widths are supported
-# 3 = reserved
-#
-# Rx LDPC coding capability: [RXLDPC]
-# Indicates support for receiving LDPC coded pkts
-# 0 = Not supported (default)
-# 1 = Supported
-#
-# Short GI for 80 MHz: [SHORT-GI-80]
-# Indicates short GI support for reception of packets transmitted with TXVECTOR
-# params format equal to VHT and CBW = 80Mhz
-# 0 = Not supported (default)
-# 1 = Supported
-#
-# Short GI for 160 MHz: [SHORT-GI-160]
-# Indicates short GI support for reception of packets transmitted with TXVECTOR
-# params format equal to VHT and CBW = 160Mhz
-# 0 = Not supported (default)
-# 1 = Supported
-#
-# Tx STBC: [TX-STBC-2BY1]
-# Indicates support for the transmission of at least 2x1 STBC
-# 0 = Not supported (default)
-# 1 = Supported
-#
-# Rx STBC: [RX-STBC-1] [RX-STBC-12] [RX-STBC-123] [RX-STBC-1234]
-# Indicates support for the reception of PPDUs using STBC
-# 0 = Not supported (default)
-# 1 = support of one spatial stream
-# 2 = support of one and two spatial streams
-# 3 = support of one, two and three spatial streams
-# 4 = support of one, two, three and four spatial streams
-# 5,6,7 = reserved
-#
-# SU Beamformer Capable: [SU-BEAMFORMER]
-# Indicates support for operation as a single user beamformer
-# 0 = Not supported (default)
-# 1 = Supported
-#
-# SU Beamformee Capable: [SU-BEAMFORMEE]
-# Indicates support for operation as a single user beamformee
-# 0 = Not supported (default)
-# 1 = Supported
-#
-# Compressed Steering Number of Beamformer Antennas Supported:
-# [BF-ANTENNA-2] [BF-ANTENNA-3] [BF-ANTENNA-4]
-# Beamformee's capability indicating the maximum number of beamformer
-# antennas the beamformee can support when sending compressed beamforming
-# feedback
-# If SU beamformer capable, set to maximum value minus 1
-# else reserved (default)
-#
-# Number of Sounding Dimensions:
-# [SOUNDING-DIMENSION-2] [SOUNDING-DIMENSION-3] [SOUNDING-DIMENSION-4]
-# Beamformer's capability indicating the maximum value of the NUM_STS parameter
-# in the TXVECTOR of a VHT NDP
-# If SU beamformer capable, set to maximum value minus 1
-# else reserved (default)
-#
-# MU Beamformer Capable: [MU-BEAMFORMER]
-# Indicates support for operation as an MU beamformer
-# 0 = Not supported or sent by Non-AP STA (default)
-# 1 = Supported
-#
-# VHT TXOP PS: [VHT-TXOP-PS]
-# Indicates whether or not the AP supports VHT TXOP Power Save Mode
-# or whether or not the STA is in VHT TXOP Power Save mode
-# 0 = VHT AP doesn't support VHT TXOP PS mode (OR) VHT STA not in VHT TXOP PS
-# mode
-# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT STA is in VHT TXOP power save
-# mode
-#
-# +HTC-VHT Capable: [HTC-VHT]
-# Indicates whether or not the STA supports receiving a VHT variant HT Control
-# field.
-# 0 = Not supported (default)
-# 1 = supported
-#
-# Maximum A-MPDU Length Exponent: [MAX-A-MPDU-LEN-EXP0]..[MAX-A-MPDU-LEN-EXP7]
-# Indicates the maximum length of A-MPDU pre-EOF padding that the STA can recv
-# This field is an integer in the range of 0 to 7.
-# The length defined by this field is equal to
-# 2 pow(13 + Maximum A-MPDU Length Exponent) -1 octets
-#
-# VHT Link Adaptation Capable: [VHT-LINK-ADAPT2] [VHT-LINK-ADAPT3]
-# Indicates whether or not the STA supports link adaptation using VHT variant
-# HT Control field
-# If +HTC-VHTcapable is 1
-# 0 = (no feedback) if the STA does not provide VHT MFB (default)
-# 1 = reserved
-# 2 = (Unsolicited) if the STA provides only unsolicited VHT MFB
-# 3 = (Both) if the STA can provide VHT MFB in response to VHT MRQ and if the
-# STA provides unsolicited VHT MFB
-# Reserved if +HTC-VHTcapable is 0
-#
-# Rx Antenna Pattern Consistency: [RX-ANTENNA-PATTERN]
-# Indicates the possibility of Rx antenna pattern change
-# 0 = Rx antenna pattern might change during the lifetime of an association
-# 1 = Rx antenna pattern does not change during the lifetime of an association
-#
-# Tx Antenna Pattern Consistency: [TX-ANTENNA-PATTERN]
-# Indicates the possibility of Tx antenna pattern change
-# 0 = Tx antenna pattern might change during the lifetime of an association
-# 1 = Tx antenna pattern does not change during the lifetime of an association
-{% if cap_vht %}
-vht_capab=
-{%- endif -%}
-
-{%- if cap_vht_max_mpdu -%}
-[MAX-MPDU-{{ cap_vht_max_mpdu }}]
-{%- endif -%}
-
-{%- if cap_vht_max_mpdu_exp -%}
-[MAX-A-MPDU-LEN-EXP{{ cap_vht_max_mpdu_exp }}]
-{%- endif -%}
-
-{%- if cap_vht_chan_set_width -%}
-[MAX-A-MPDU-LEN-EXP{{ cap_vht_max_mpdu_exp }}]
-{%- endif -%}
-
-{%- if cap_vht_chan_set_width -%}
-{%- if '2' in cap_vht_chan_set_width -%}
-[VHT160]
-{%- elif '3' in cap_vht_chan_set_width -%}
-[VHT160-80PLUS80]
-{%- endif -%}
-{%- endif -%}
-
-{%- if cap_vht_stbc_tx -%}
-[TX-STBC-2BY1]
-{%- endif -%}
-
-{%- if cap_vht_stbc_rx -%}
-[RX-STBC-{{ cap_vht_stbc_rx }}]
-{%- endif -%}
-
-{%- if cap_vht_link_adaptation -%}
-{%- if 'unsolicited' in cap_vht_link_adaptation -%}
-[VHT-LINK-ADAPT2]
-{%- elif 'both' in cap_vht_link_adaptation -%}
-[VHT-LINK-ADAPT3]
-{%- endif -%}
-{%- endif -%}
-
-{%- if cap_vht_short_gi -%}
-{%- for gi in cap_vht_short_gi -%}
-[SHORT-GI-{{ gi }}]
-{%- endfor -%}
-{%- endif -%}
-
-{%- if cap_vht_ldpc -%}
-[RXLDPC]
-{%- endif -%}
-
-{%- if cap_vht_tx_powersave -%}
-[VHT-TXOP-PS]
-{%- endif -%}
-
-{%- if cap_vht_vht_cf -%}
-[HTC-VHT]
-{%- endif -%}
-
-{%- if cap_vht_beamform -%}
-{%- for beamform in cap_vht_beamform -%}
-{%- if 'single-user-beamformer' in beamform -%}
-[SU-BEAMFORMER]
-{%- elif 'single-user-beamformee' in beamform -%}
-[SU-BEAMFORMEE]
-{%- elif 'multi-user-beamformer' in beamform -%}
-[MU-BEAMFORMER]
-{%- elif 'multi-user-beamformee' in beamform -%}
-[MU-BEAMFORMEE]
-{%- endif -%}
-{%- endfor -%}
-{%- endif -%}
-
-{%- if cap_vht_antenna_fixed -%}
-[RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN]
-{%- endif -%}
-
-{%- if cap_vht_antenna_cnt -%}
-{%- for beamform in cap_vht_beamform -%}
-{%- if 'single-user-beamformer' in beamform -%}
-[BF-ANTENNA-{{ cap_vht_antenna_cnt|int -1 }}][SOUNDING-DIMENSION-{{ cap_vht_antenna_cnt|int -1}}]
-{%- else -%}
-[BF-ANTENNA-{{ cap_vht_antenna_cnt }}][SOUNDING-DIMENSION-{{ cap_vht_antenna_cnt }}]
-{%- endif -%}
-{%- endfor -%}
-{%- endif %}
-
-# ieee80211n: Whether IEEE 802.11n (HT) is enabled
-# 0 = disabled (default)
-# 1 = enabled
-# Note: You will also need to enable WMM for full HT functionality.
-# Note: hw_mode=g (2.4 GHz) and hw_mode=a (5 GHz) is used to specify the band.
-{% if cap_req_vht -%}
-ieee80211n=0
-# Require stations to support VHT PHY (reject association if they do not)
-require_vht=1
-{% endif %}
-
-{% if cap_vht_center_freq_1 -%}
-# center freq = 5 GHz + (5 * index)
-# So index 42 gives center freq 5.210 GHz
-# which is channel 42 in 5G band
-vht_oper_centr_freq_seg0_idx={{ cap_vht_center_freq_1 }}
-{% endif %}
-
-{% if cap_vht_center_freq_2 -%}
-# center freq = 5 GHz + (5 * index)
-# So index 159 gives center freq 5.795 GHz
-# which is channel 159 in 5G band
-vht_oper_centr_freq_seg1_idx={{ cap_vht_center_freq_2 }}
-{% endif %}
-
-{% if disable_broadcast_ssid -%}
-# Send empty SSID in beacons and ignore probe request frames that do not
-# specify full SSID, i.e., require stations to know SSID.
-# default: disabled (0)
-# 1 = send empty (length=0) SSID in beacon and ignore probe request for
-# broadcast SSID
-# 2 = clear SSID (ASCII 0), but keep the original length (this may be required
-# with some clients that do not support empty SSID) and ignore probe
-# requests for broadcast SSID
-ignore_broadcast_ssid=1
-{% endif %}
-
-# Station MAC address -based authentication
-# Please note that this kind of access control requires a driver that uses
-# hostapd to take care of management frame processing and as such, this can be
-# used with driver=hostap or driver=nl80211, but not with driver=atheros.
-# 0 = accept unless in deny list
-# 1 = deny unless in accept list
-# 2 = use external RADIUS server (accept/deny lists are searched first)
-macaddr_acl=0
-
-{% if max_stations -%}
-# Maximum number of stations allowed in station table. New stations will be
-# rejected after the station table is full. IEEE 802.11 has a limit of 2007
-# different association IDs, so this number should not be larger than that.
-# (default: 2007)
-max_num_sta={{ max_stations }}
-{% endif %}
-
-{% if isolate_stations -%}
-# Client isolation can be used to prevent low-level bridging of frames between
-# associated stations in the BSS. By default, this bridging is allowed.
-ap_isolate=1
-{% endif %}
-
-{% if reduce_transmit_power -%}
-# Add Power Constraint element to Beacon and Probe Response frames
-# This config option adds Power Constraint element when applicable and Country
-# element is added. Power Constraint element is required by Transmit Power
-# Control. This can be used only with ieee80211d=1.
-# Valid values are 0..255.
-local_pwr_constraint={{ reduce_transmit_power }}
-{% endif %}
-
-{% if expunge_failing_stations -%}
-# Disassociate stations based on excessive transmission failures or other
-# indications of connection loss. This depends on the driver capabilities and
-# may not be available with all drivers.
-disassoc_low_ack=1
-{% endif %}
-
-{% if sec_wep -%}
-# IEEE 802.11 specifies two authentication algorithms. hostapd can be
-# configured to allow both of these or only one. Open system authentication
-# should be used with IEEE 802.1X.
-# Bit fields of allowed authentication algorithms:
-# bit 0 = Open System Authentication
-# bit 1 = Shared Key Authentication (requires WEP)
-auth_algs=2
-
-# WEP rekeying (disabled if key lengths are not set or are set to 0)
-# Key lengths for default/broadcast and individual/unicast keys:
-# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits)
-# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits)
-wep_key_len_broadcast=5
-wep_key_len_unicast=5
-
-# Static WEP key configuration
-#
-# The key number to use when transmitting.
-# It must be between 0 and 3, and the corresponding key must be set.
-# default: not set
-wep_default_key=0
-
-# The WEP keys to use.
-# A key may be a quoted string or unquoted hexadecimal digits.
-# The key length should be 5, 13, or 16 characters, or 10, 26, or 32
-# digits, depending on whether 40-bit (64-bit), 104-bit (128-bit), or
-# 128-bit (152-bit) WEP is used.
-# Only the default key must be supplied; the others are optional.
-{% if sec_wep_key -%}
-{% for key in sec_wep_key -%}
-wep_key{{ loop.index -1 }}={{ key}}
-{% endfor %}
-{%- endif %}
-
-{% elif sec_wpa -%}
-##### WPA/IEEE 802.11i configuration ##########################################
-
-# Enable WPA. Setting this variable configures the AP to require WPA (either
-# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
-# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
-# Instead of wpa_psk / wpa_passphrase, wpa_psk_radius might suffice.
-# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
-# RADIUS authentication server must be configured, and WPA-EAP must be included
-# in wpa_key_mgmt.
-# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0)
-# and/or WPA2 (full IEEE 802.11i/RSN):
-# bit0 = WPA
-# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled)
-{% if 'both' in sec_wpa_mode -%}
-wpa=3
-{%- elif 'wpa2' in sec_wpa_mode -%}
-wpa=2
-{%- elif 'wpa' in sec_wpa_mode -%}
-wpa=1
-{%- endif %}
-
-{% if sec_wpa_cipher -%}
-# Set of accepted cipher suites (encryption algorithms) for pairwise keys
-# (unicast packets). This is a space separated list of algorithms:
-# CCMP = AES in Counter mode with CBC-MAC (CCMP-128)
-# TKIP = Temporal Key Integrity Protocol
-# CCMP-256 = AES in Counter mode with CBC-MAC with 256-bit key
-# GCMP = Galois/counter mode protocol (GCMP-128)
-# GCMP-256 = Galois/counter mode protocol with 256-bit key
-# Group cipher suite (encryption algorithm for broadcast and multicast frames)
-# is automatically selected based on this configuration. If only CCMP is
-# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise,
-# TKIP will be used as the group cipher. The optional group_cipher parameter can
-# be used to override this automatic selection.
-{% if 'wpa2' in sec_wpa_mode -%}
-# Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value)
-rsn_pairwise={{ sec_wpa_cipher | join(" ") }}
-{% else -%}
-# Pairwise cipher for WPA (v1) (default: TKIP)
-wpa_pairwise={{ sec_wpa_cipher | join(" ") }}
-{%- endif -%}
-{% endif %}
-
-{% if sec_wpa_passphrase -%}
-# IEEE 802.11 specifies two authentication algorithms. hostapd can be
-# configured to allow both of these or only one. Open system authentication
-# should be used with IEEE 802.1X.
-# Bit fields of allowed authentication algorithms:
-# bit 0 = Open System Authentication
-# bit 1 = Shared Key Authentication (requires WEP)
-auth_algs=1
-
-# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
-# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
-# (8..63 characters) that will be converted to PSK. This conversion uses SSID
-# so the PSK changes when ASCII passphrase is used and the SSID is changed.
-wpa_passphrase={{ sec_wpa_passphrase }}
-
-# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
-# entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
-# added to enable SHA256-based stronger algorithms.
-# WPA-PSK = WPA-Personal / WPA2-Personal
-# WPA-PSK-SHA256 = WPA2-Personal using SHA256
-wpa_key_mgmt=WPA-PSK
-
-{% elif sec_wpa_radius -%}
-##### IEEE 802.1X-2004 related configuration ##################################
-# Require IEEE 802.1X authorization
-ieee8021x=1
-
-# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
-# entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
-# added to enable SHA256-based stronger algorithms.
-# WPA-EAP = WPA-Enterprise / WPA2-Enterprise
-# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256
-wpa_key_mgmt=WPA-EAP
-
-{% if sec_wpa_radius_source -%}
-# RADIUS client forced local IP address for the access point
-# Normally the local IP address is determined automatically based on configured
-# IP addresses, but this field can be used to force a specific address to be
-# used, e.g., when the device has multiple IP addresses.
-radius_client_addr={{ sec_wpa_radius_source }}
-
-# The own IP address of the access point (used as NAS-IP-Address)
-own_ip_addr={{ sec_wpa_radius_source }}
-{% else %}
-# The own IP address of the access point (used as NAS-IP-Address)
-own_ip_addr=127.0.0.1
-{% endif %}
-
-{% for radius in sec_wpa_radius -%}
-{%- if not radius.disabled -%}
-# RADIUS authentication server
-auth_server_addr={{ radius.server }}
-auth_server_port={{ radius.port }}
-auth_server_shared_secret={{ radius.key }}
-{% if radius.acc_port -%}
-# RADIUS accounting server
-acct_server_addr={{ radius.server }}
-acct_server_port={{ radius.acc_port }}
-acct_server_shared_secret={{ radius.key }}
-{% endif %}
-{% endif %}
-{% endfor %}
-
-{% endif %}
-
-{% else %}
-# Open system
-auth_algs=1
-{% endif %}
-
-# TX queue parameters (EDCF / bursting)
-# tx_queue_<queue name>_<param>
-# queues: data0, data1, data2, data3
-# (data0 is the highest priority queue)
-# parameters:
-# aifs: AIFS (default 2)
-# cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191,
-# 16383, 32767)
-# cwmax: cwMax (same values as cwMin, cwMax >= cwMin)
-# burst: maximum length (in milliseconds with precision of up to 0.1 ms) for
-# bursting
-#
-# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
-# These parameters are used by the access point when transmitting frames
-# to the clients.
-#
-# Low priority / AC_BK = background
-tx_queue_data3_aifs=7
-tx_queue_data3_cwmin=15
-tx_queue_data3_cwmax=1023
-tx_queue_data3_burst=0
-# Note: for IEEE 802.11b mode: cWmin=31 cWmax=1023 burst=0
-#
-# Normal priority / AC_BE = best effort
-tx_queue_data2_aifs=3
-tx_queue_data2_cwmin=15
-tx_queue_data2_cwmax=63
-tx_queue_data2_burst=0
-# Note: for IEEE 802.11b mode: cWmin=31 cWmax=127 burst=0
-#
-# High priority / AC_VI = video
-tx_queue_data1_aifs=1
-tx_queue_data1_cwmin=7
-tx_queue_data1_cwmax=15
-tx_queue_data1_burst=3.0
-# Note: for IEEE 802.11b mode: cWmin=15 cWmax=31 burst=6.0
-#
-# Highest priority / AC_VO = voice
-tx_queue_data0_aifs=1
-tx_queue_data0_cwmin=3
-tx_queue_data0_cwmax=7
-tx_queue_data0_burst=1.5
-
-# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
-# for 802.11a or 802.11g networks
-# These parameters are sent to WMM clients when they associate.
-# The parameters will be used by WMM clients for frames transmitted to the
-# access point.
-#
-# note - txop_limit is in units of 32microseconds
-# note - acm is admission control mandatory flag. 0 = admission control not
-# required, 1 = mandatory
-# note - Here cwMin and cmMax are in exponent form. The actual cw value used
-# will be (2^n)-1 where n is the value given here. The allowed range for these
-# wmm_ac_??_{cwmin,cwmax} is 0..15 with cwmax >= cwmin.
-#
-wmm_enabled=1
-
-# Low priority / AC_BK = background
-wmm_ac_bk_cwmin=4
-wmm_ac_bk_cwmax=10
-wmm_ac_bk_aifs=7
-wmm_ac_bk_txop_limit=0
-wmm_ac_bk_acm=0
-# Note: for IEEE 802.11b mode: cWmin=5 cWmax=10
-#
-# Normal priority / AC_BE = best effort
-wmm_ac_be_aifs=3
-wmm_ac_be_cwmin=4
-wmm_ac_be_cwmax=10
-wmm_ac_be_txop_limit=0
-wmm_ac_be_acm=0
-# Note: for IEEE 802.11b mode: cWmin=5 cWmax=7
-#
-# High priority / AC_VI = video
-wmm_ac_vi_aifs=2
-wmm_ac_vi_cwmin=3
-wmm_ac_vi_cwmax=4
-wmm_ac_vi_txop_limit=94
-wmm_ac_vi_acm=0
-# Note: for IEEE 802.11b mode: cWmin=4 cWmax=5 txop_limit=188
-#
-# Highest priority / AC_VO = voice
-wmm_ac_vo_aifs=2
-wmm_ac_vo_cwmin=2
-wmm_ac_vo_cwmax=3
-wmm_ac_vo_txop_limit=47
-wmm_ac_vo_acm=0
-
-"""
-
-# Please be careful if you edit the template.
-config_wpa_suppl_tmpl = """
-# WPA supplicant config
-network={
- ssid="{{ ssid }}"
-{%- if sec_wpa_passphrase %}
- psk="{{ sec_wpa_passphrase }}"
-{% else %}
- key_mgmt=NONE
-{% endif %}
-}
-
-"""
-
default_config_data = {
'address': [],
'address_remove': [],
@@ -799,7 +63,7 @@ default_config_data = {
'cap_vht_center_freq_2' : '',
'cap_vht_chan_set_width' : '',
'cap_vht_ldpc' : False,
- 'cap_vht_link_adaptation' : False,
+ 'cap_vht_link_adaptation' : '',
'cap_vht_max_mpdu_exp' : '',
'cap_vht_max_mpdu' : '',
'cap_vht_short_gi' : [],
@@ -857,11 +121,8 @@ def get_conf_file(conf_type, intf):
# create directory on demand
if not os.path.exists(cfg_dir):
os.mkdir(cfg_dir)
- # fix permissions - corresponds to mode 755
- os.chmod(cfg_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
- uid = getpwnam(user).pw_uid
- gid = getgrnam(group).gr_gid
- os.chown(cfg_dir, uid, gid)
+ chmod_x(cfg_dir)
+ chown_file(cfg_dir, user, group)
cfg_file = cfg_dir + r'/{}.cfg'.format(intf)
return cfg_file
@@ -872,11 +133,8 @@ def get_pid(conf_type, intf):
# create directory on demand
if not os.path.exists(cfg_dir):
os.mkdir(cfg_dir)
- # fix permissions - corresponds to mode 755
- os.chmod(cfg_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
- uid = getpwnam(user).pw_uid
- gid = getgrnam(group).gr_gid
- os.chown(cfg_dir, uid, gid)
+ chmod_x(cfg_dir)
+ chown_file(cfg_dir, user, group)
cfg_file = cfg_dir + r'/{}.pid'.format(intf)
return cfg_file
@@ -888,11 +146,8 @@ def get_wpa_suppl_config_name(intf):
# create directory on demand
if not os.path.exists(cfg_dir):
os.mkdir(cfg_dir)
- # fix permissions - corresponds to mode 755
- os.chmod(cfg_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
- uid = getpwnam(user).pw_uid
- gid = getgrnam(group).gr_gid
- os.chown(cfg_dir, uid, gid)
+ chmod_x(cfg_dir)
+ chown_file(cfg_dir, user, group)
cfg_file = cfg_dir + r'/{}.cfg'.format(intf)
return cfg_file
@@ -1043,7 +298,7 @@ def get_config():
# VHT link adaptation capabilities
if conf.exists('capabilities vht link-adaptation'):
wifi['cap_vht'] = True
- wifi['cap_vht_link_adaptation'] = True
+ wifi['cap_vht_link_adaptation'] = conf.return_value('capabilities vht link-adaptation')
# Set the maximum length of A-MPDU pre-EOF padding that the station can receive
if conf.exists('capabilities vht max-mpdu-exp'):
@@ -1329,6 +584,10 @@ def verify(wifi):
if wifi['cap_vht_beamform'] and wifi['cap_vht_antenna_cnt'] == 1:
raise ConfigError('Cannot use beam forming with just one antenna!')
+ if wifi['cap_vht_beamform'] == 'single-user-beamformer' and wifi['cap_vht_antenna_cnt'] < 3:
+ # Nasty Gotcha: see https://w1.fi/cgit/hostap/plain/hostapd/hostapd.conf lines 692-705
+ raise ConfigError('Single-user beam former requires at least 3 antennas!')
+
if wifi['sec_wep'] and (len(wifi['sec_wep_key']) == 0):
raise ConfigError('Missing WEP keys')
@@ -1364,6 +623,11 @@ def verify(wifi):
return None
def generate(wifi):
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir["data"], "templates", "wifi")
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
# always stop hostapd service first before reconfiguring it
pidfile = get_pid('hostapd', wifi['intf'])
if process_running(pidfile):
@@ -1418,13 +682,13 @@ def generate(wifi):
# render appropriate new config files depending on access-point or station mode
if wifi['op_mode'] == 'ap':
- tmpl = Template(config_hostapd_tmpl)
+ tmpl = env.get_template('hostapd.conf.tmpl')
config_text = tmpl.render(wifi)
with open(get_conf_file('hostapd', wifi['intf']), 'w') as f:
f.write(config_text)
elif wifi['op_mode'] == 'station':
- tmpl = Template(config_wpa_suppl_tmpl)
+ tmpl = env.get_template('wpa_supplicant.conf.tmpl')
config_text = tmpl.render(wifi)
with open(get_conf_file('wpa_supplicant', wifi['intf']), 'w') as f:
f.write(config_text)
diff --git a/src/conf_mode/interfaces-wirelessmodem.py b/src/conf_mode/interfaces-wirelessmodem.py
index 7aee5c9bd..da33d54e4 100755
--- a/src/conf_mode/interfaces-wirelessmodem.py
+++ b/src/conf_mode/interfaces-wirelessmodem.py
@@ -18,84 +18,14 @@ import os
from sys import exit
from copy import deepcopy
-from jinja2 import Template
-from subprocess import Popen, PIPE
+from jinja2 import FileSystemLoader, Environment
from netifaces import interfaces
from vyos.config import Config
-from vyos.util import chown_file, chmod_x_file
+from vyos.defaults import directories as vyos_data_dir
+from vyos.util import chown_file, chmod_x, subprocess_cmd
from vyos import ConfigError
-# Please be careful if you edit the template.
-config_wwan_tmpl = """### Autogenerated by interfaces-wirelessmodem.py ###
-{% if description %}
-# {{ description }}
-{% endif %}
-ifname {{ intf }}
-ipparam "{{ intf }} {{ metric }}"
-linkname {{ intf }}
-{% if name_server -%}
-usepeerdns
-{%- endif %}
-# physical device
-/dev/{{ device }}
-lcp-echo-failure 0
-115200
-debug
-logfile {{ logfile }}
-nodefaultroute
-ipcp-max-failure 4
-ipcp-accept-local
-ipcp-accept-remote
-noauth
-crtscts
-lock
-persist
-{% if on_demand -%}
-demand
-{%- endif %}
-
-connect '/usr/sbin/chat -v -t6 -f {{ chat_script }}'
-
-"""
-
-# Please be careful if you edit the template.
-chat_wwan_tmpl = """
-ABORT 'NO DIAL TONE' ABORT 'NO ANSWER' ABORT 'NO CARRIER' ABORT DELAYED
-'' AT
-OK ATZ
-OK 'AT+CGDCONT=1,"IP","{{ apn }}"'
-OK ATD*99#
-CONNECT ''
-
-"""
-
-config_wwan_ip_up_tmpl = """#!/bin/sh
-# As WWAN is an "on demand" interface we need to re-configure it when it
-# becomes 'up'
-
-ipparam=$6
-
-# device name and metric are received using ipparam
-device=`echo "$ipparam"|awk '{ print $1 }'`
-
-if [ "$device" != "{{ intf }}" ]; then
- exit
-fi
-
-# add some info to syslog
-DIALER_PID=$(cat /var/run/{{ intf }}.pid)
-logger -t pppd[$DIALER_PID] "executing $0"
-
-echo "{{ description }}" > /sys/class/net/{{ intf }}/ifalias
-
-{% if vrf -%}
-logger -t pppd[$DIALER_PID] "configuring interface {{ intf }} for VRF {{ vrf }}"
-ip link set dev {{ intf }} master {{ vrf }}
-{% endif %}
-
-"""
-
default_config_data = {
'address': [],
'apn': '',
@@ -114,10 +44,6 @@ default_config_data = {
'vrf': ''
}
-def subprocess_cmd(command):
- p = Popen(command, stdout=PIPE, shell=True)
- p.communicate()
-
def check_kmod():
modules = ['option', 'usb_wwan', 'usbserial']
for module in modules:
@@ -206,12 +132,22 @@ def verify(wwan):
return None
def generate(wwan):
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'wwan')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
+ # set up configuration file path variables where our templates will be
+ # rendered into
intf = wwan['intf']
- config_file_wwan = f'/etc/ppp/peers/{intf}'
- config_file_wwan_chat = wwan['chat_script']
- ip_up_script_file = f'/etc/ppp/ip-up.d/9991-vyos-vrf-{intf}'
+ config_wwan = f'/etc/ppp/peers/{intf}'
+ config_wwan_chat = wwan['chat_script']
+ script_wwan_pre_up = f'/etc/ppp/ip-pre-up.d/1010-vyos-wwan-{intf}'
+ script_wwan_ip_up = f'/etc/ppp/ip-up.d/1010-vyos-wwan-{intf}'
+ script_wwan_ip_down = f'/etc/ppp/ip-down.d/1010-vyos-wwan-{intf}'
- config_files = [config_file_wwan, config_file_wwan_chat, ip_up_script_file]
+ config_files = [config_wwan, config_wwan_chat, script_wwan_pre_up,
+ script_wwan_ip_up, script_wwan_ip_down]
# Ensure directories for config files exist - otherwise create them on demand
for file in config_files:
@@ -231,25 +167,39 @@ def generate(wwan):
else:
# Create PPP configuration files
- tmpl = Template(config_wwan_tmpl)
+ tmpl = env.get_template('peer.tmpl')
config_text = tmpl.render(wwan)
- with open(config_file_wwan, 'w') as f:
+ with open(config_wwan, 'w') as f:
f.write(config_text)
# Create PPP chat script
- tmpl = Template(chat_wwan_tmpl)
+ tmpl = env.get_template('chat.tmpl')
+ config_text = tmpl.render(wwan)
+ with open(config_wwan_chat, 'w') as f:
+ f.write(config_text)
+
+ # Create script for ip-pre-up.d
+ tmpl = env.get_template('ip-pre-up.script.tmpl')
+ config_text = tmpl.render(wwan)
+ with open(script_wwan_pre_up, 'w') as f:
+ f.write(config_text)
+
+ # Create script for ip-up.d
+ tmpl = env.get_template('ip-up.script.tmpl')
config_text = tmpl.render(wwan)
- with open(wwan['chat_script'], 'w') as f:
+ with open(script_wwan_ip_up, 'w') as f:
f.write(config_text)
- # Create ip-pre-up script
- tmpl = Template(config_wwan_ip_up_tmpl)
+ # Create script for ip-down.d
+ tmpl = env.get_template('ip-down.script.tmpl')
config_text = tmpl.render(wwan)
- with open(ip_up_script_file, 'w') as f:
+ with open(script_wwan_ip_down, 'w') as f:
f.write(config_text)
# make generated script file executable
- chmod_x_file(ip_up_script_file)
+ chmod_x(script_wwan_pre_up)
+ chmod_x(script_wwan_ip_up)
+ chmod_x(script_wwan_ip_down)
return None
diff --git a/src/conf_mode/ipsec-settings.py b/src/conf_mode/ipsec-settings.py
index e80c6caf0..90b6b0d57 100755
--- a/src/conf_mode/ipsec-settings.py
+++ b/src/conf_mode/ipsec-settings.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,22 +13,18 @@
#
# 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 sys
import re
import os
-import jinja2
-import syslog as sl
-import time
-import vyos.config
-import vyos.defaults
+from time import sleep
+from jinja2 import FileSystemLoader, Environment
+from sys import exit
+from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
-
ra_conn_name = "remote-access"
charon_conf_file = "/etc/strongswan.d/charon.conf"
ipsec_secrets_flie = "/etc/ipsec.secrets"
@@ -42,55 +38,8 @@ delim_ipsec_l2tp_begin = "### VyOS L2TP VPN Begin ###"
delim_ipsec_l2tp_end = "### VyOS L2TP VPN End ###"
charon_pidfile = "/var/run/charon.pid"
-l2pt_ipsec_conf = '''
-{{delim_ipsec_l2tp_begin}}
-include {{ipsec_ra_conn_file}}
-{{delim_ipsec_l2tp_end}}
-'''
-
-l2pt_ipsec_secrets_conf = '''
-{{delim_ipsec_l2tp_begin}}
-{% if ipsec_l2tp_auth_mode == 'pre-shared-secret' %}
-{{outside_addr}} %any : PSK "{{ipsec_l2tp_secret}}"
-{% elif ipsec_l2tp_auth_mode == 'x509' %}
-: RSA {{server_key_file_copied}}
-{% endif%}
-{{delim_ipsec_l2tp_end}}
-'''
-
-l2tp_ipsec_ra_conn_conf = '''
-{{delim_ipsec_l2tp_begin}}
-conn {{ra_conn_name}}
- type=transport
- left={{outside_addr}}
- leftsubnet=%dynamic[/1701]
- rightsubnet=%dynamic
- mark_in=%unique
- auto=add
- ike=aes256-sha1-modp1024,3des-sha1-modp1024,3des-sha1-modp1024!
- dpddelay=15
- dpdtimeout=45
- dpdaction=clear
- esp=aes256-sha1,3des-sha1!
- rekey=no
-{% if ipsec_l2tp_auth_mode == 'pre-shared-secret' %}
- authby=secret
- leftauth=psk
- rightauth=psk
-{% elif ipsec_l2tp_auth_mode == 'x509' %}
- authby=rsasig
- leftrsasigkey=%cert
- rightrsasigkey=%cert
- rightca=%same
- leftcert={{server_cert_file_copied}}
-{% endif %}
- ikelifetime={{ipsec_l2tp_ike_lifetime}}
- keylife={{ipsec_l2tp_lifetime}}
-{{delim_ipsec_l2tp_end}}
-'''
-
def get_config():
- config = vyos.config.Config()
+ config = Config()
data = {"install_routes": "yes"}
if config.exists("vpn ipsec options disable-route-autoinstall"):
@@ -146,44 +95,12 @@ def get_config():
return data
-### ipsec secret l2tp
-def write_ipsec_secrets(c):
- tmpl = jinja2.Template(l2pt_ipsec_secrets_conf, trim_blocks=True)
- l2pt_ipsec_secrets_txt = tmpl.render(c)
- old_umask = os.umask(0o077)
- open(ipsec_secrets_flie,'w').write(l2pt_ipsec_secrets_txt)
- os.umask(old_umask)
- sl.syslog(sl.LOG_NOTICE, ipsec_secrets_flie + ' written')
-
-### ipsec remote access connection config
-def write_ipsec_ra_conn(c):
- tmpl = jinja2.Template(l2tp_ipsec_ra_conn_conf, trim_blocks=True)
- ipsec_ra_conn_txt = tmpl.render(c)
- old_umask = os.umask(0o077)
-
- # Create tunnels directory if does not exist
- if not os.path.exists(ipsec_ra_conn_dir):
- os.makedirs(ipsec_ra_conn_dir)
- sl.syslog(sl.LOG_NOTICE, ipsec_ra_conn_dir + " created")
-
- open(ipsec_ra_conn_file,'w').write(ipsec_ra_conn_txt)
- os.umask(old_umask)
- sl.syslog(sl.LOG_NOTICE, ipsec_ra_conn_file + ' written')
### Remove config from file by delimiter
def remove_confs(delim_begin, delim_end, conf_file):
os.system("sed -i '/"+delim_begin+"/,/"+delim_end+"/d' "+conf_file)
-### Append "include /path/to/ra_conn" to ipsec conf file
-def append_ipsec_conf(c):
- tmpl = jinja2.Template(l2pt_ipsec_conf, trim_blocks=True)
- l2pt_ipsec_conf_txt = tmpl.render(c)
- old_umask = os.umask(0o077)
- open(ipsec_conf_flie,'a').write(l2pt_ipsec_conf_txt)
- os.umask(old_umask)
- sl.syslog(sl.LOG_NOTICE, ipsec_conf_flie + ' written')
-
### Checking certificate storage and notice if certificate not in /config directory
def check_cert_file_store(cert_name, file_path, dts_path):
if not re.search('^\/config\/.+', file_path):
@@ -197,8 +114,6 @@ def check_cert_file_store(cert_name, file_path, dts_path):
ret = os.system('cp -f '+file_path+' '+dts_path)
if ret:
raise ConfigError("L2TP VPN configuration error: Cannot copy "+file_path)
- else:
- sl.syslog(sl.LOG_NOTICE, file_path + ' copied to '+dts_path)
def verify(data):
# l2tp ipsec check
@@ -231,22 +146,45 @@ def verify(data):
raise ConfigError("L2TP VPN configuration error: \"vpn ipsec ipsec-interfaces\" must be specified.")
def generate(data):
- tmpl_path = os.path.join(vyos.defaults.directories["data"], "templates", "ipsec")
- fs_loader = jinja2.FileSystemLoader(tmpl_path)
- env = jinja2.Environment(loader=fs_loader)
-
-
- charon_conf_tmpl = env.get_template("charon.tmpl")
- charon_conf = charon_conf_tmpl.render(data)
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'ipsec')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader, trim_blocks=True)
+ tmpl = env.get_template('charon.tmpl')
+ config_text = tmpl.render(data)
with open(charon_conf_file, 'w') as f:
- f.write(charon_conf)
+ f.write(config_text)
if data["ipsec_l2tp"]:
remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_flie)
- write_ipsec_secrets(data)
- write_ipsec_ra_conn(data)
- append_ipsec_conf(data)
+
+ tmpl = env.get_template('ipsec.secrets.tmpl')
+ l2pt_ipsec_secrets_txt = tmpl.render(c)
+ old_umask = os.umask(0o077)
+ with open(ipsec_secrets_flie,'w') as f:
+ f.write(l2pt_ipsec_secrets_txt)
+ os.umask(old_umask)
+
+ tmpl = env.get_template('remote-access.tmpl')
+ ipsec_ra_conn_txt = tmpl.render(c)
+ old_umask = os.umask(0o077)
+
+ # Create tunnels directory if does not exist
+ if not os.path.exists(ipsec_ra_conn_dir):
+ os.makedirs(ipsec_ra_conn_dir)
+
+ with open(ipsec_ra_conn_file,'w') as f:
+ f.write(ipsec_ra_conn_txt)
+ os.umask(old_umask)
+
+
+ tmpl = env.get_template('ipsec.conf.tmpl')
+ l2pt_ipsec_conf_txt = tmpl.render(c)
+ old_umask = os.umask(0o077)
+ with open(ipsec_conf_flie,'a') as f:
+ f.write(l2pt_ipsec_conf_txt)
+ os.umask(old_umask)
+
else:
if os.path.exists(ipsec_ra_conn_file):
remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_ra_conn_file)
@@ -254,15 +192,15 @@ def generate(data):
remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_flie)
def restart_ipsec():
- os.system("ipsec restart >&/dev/null")
+ os.system('ipsec restart >&/dev/null')
# counter for apply swanctl config
counter = 10
while counter <= 10:
if os.path.exists(charon_pidfile):
- os.system("swanctl -q >&/dev/null")
+ os.system('swanctl -q >&/dev/null')
break
counter -=1
- time.sleep(1)
+ sleep(1)
if counter == 0:
raise ConfigError('VPN configuration error: IPSec is not running.')
@@ -278,4 +216,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py
index b72916ab8..4e3dfc0b6 100755
--- a/src/conf_mode/lldp.py
+++ b/src/conf_mode/lldp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2017-2019 VyOS maintainers and contributors
+# Copyright (C) 2017-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
@@ -14,47 +14,22 @@
# 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 sys
import os
-import jinja2
+import re
from copy import deepcopy
+from jinja2 import FileSystemLoader, Environment
+from sys import exit
+
from vyos.config import Config
from vyos.validate import is_addr_assigned,is_loopback_addr
+from vyos.defaults import directories as vyos_data_dir
+from vyos.version import get_version_data
from vyos import ConfigError
-# Please be careful if you edit the template.
config_file = "/etc/default/lldpd"
-lldp_tmpl = """
-### Autogenerated by lldp.py ###
-DAEMON_ARGS="-M 4{% if options.snmp %} -x{% endif %}{% if options.cdp %} -c{% endif %}{% if options.edp %} -e{% endif %}{% if options.fdp %} -f{% endif %}{% if options.sonmp %} -s{% endif %}"
-
-"""
-
vyos_config_file = "/etc/lldpd.d/01-vyos.conf"
-vyos_tmpl = """
-### Autogenerated by lldp.py ###
-
-configure system platform VyOS
-configure system description "VyOS {{ options.description }}"
-{% if options.listen_on -%}
-configure system interface pattern "{{ ( options.listen_on | select('equalto','all') | map('replace','all','*') | list + options.listen_on | select('equalto','!all') | map('replace','!all','!*') | list + options.listen_on | reject('equalto','all') | reject('equalto','!all') | list ) | unique | join(",") }}"
-{%- endif %}
-{% if options.mgmt_addr -%}
-configure system ip management pattern {{ options.mgmt_addr | join(",") }}
-{%- endif %}
-{%- for loc in location -%}
-{%- if loc.elin %}
-configure ports {{ loc.name }} med location elin "{{ loc.elin }}"
-{%- endif %}
-{%- if loc.coordinate_based %}
-configure ports {{ loc.name }} med location coordinate {% if loc.coordinate_based.latitude %}latitude {{ loc.coordinate_based.latitude }}{% endif %} {% if loc.coordinate_based.longitude %}longitude {{ loc.coordinate_based.longitude }}{% endif %} {% if loc.coordinate_based.altitude %}altitude {{ loc.coordinate_based.altitude }} m{% endif %} {% if loc.coordinate_based.datum %}datum {{ loc.coordinate_based.datum }}{% endif %}
-{%- endif %}
-
-
-{% endfor %}
-"""
+base = ['service', 'lldp']
default_config_data = {
"options": '',
@@ -64,7 +39,7 @@ default_config_data = {
def get_options(config):
options = {}
- config.set_level('service lldp')
+ config.set_level(base)
options['listen_vlan'] = config.exists('listen-vlan')
options['mgmt_addr'] = []
@@ -84,30 +59,31 @@ def get_options(config):
if snmp:
config.set_level('')
options["sys_snmp"] = config.exists('service snmp')
- config.set_level('service lldp')
+ config.set_level(base)
- config.set_level('service lldp legacy-protocols')
+ config.set_level(base + ['legacy-protocols'])
options['cdp'] = config.exists('cdp')
options['edp'] = config.exists('edp')
options['fdp'] = config.exists('fdp')
options['sonmp'] = config.exists('sonmp')
# start with an unknown version information
- options['description'] = 'unknown'
+ version_data = get_version_data()
+ options['description'] = version_data['version']
options['listen_on'] = []
return options
def get_interface_list(config):
- config.set_level('service lldp')
- intfs_names = config.list_nodes('interface')
+ config.set_level(base)
+ intfs_names = config.list_nodes(['interface'])
if len(intfs_names) < 0:
return 0
interface_list = []
for name in intfs_names:
- config.set_level('service lldp interface {0}'.format(name))
- disable = config.exists('disable')
+ config.set_level(base + ['interface', name])
+ disable = config.exists(['disable'])
intf = {
'name': name,
'disable': disable
@@ -117,10 +93,10 @@ def get_interface_list(config):
def get_location_intf(config, name):
- path = 'service lldp interface {0}'.format(name)
+ path = base + ['interface', name]
config.set_level(path)
- config.set_level('{} location'.format(path))
+ config.set_level(path + ['location'])
elin = ''
coordinate_based = {}
@@ -128,18 +104,18 @@ def get_location_intf(config, name):
elin = config.return_value('elin')
if config.exists('coordinate-based'):
- config.set_level('{} location coordinate-based'.format(path))
+ config.set_level(path + ['location', 'coordinate-based'])
- coordinate_based['latitude'] = config.return_value('latitude')
- coordinate_based['longitude'] = config.return_value('longitude')
+ coordinate_based['latitude'] = config.return_value(['latitude'])
+ coordinate_based['longitude'] = config.return_value(['longitude'])
coordinate_based['altitude'] = '0'
- if config.exists('altitude'):
- coordinate_based['altitude'] = config.return_value('altitude')
+ if config.exists(['altitude']):
+ coordinate_based['altitude'] = config.return_value(['altitude'])
coordinate_based['datum'] = 'WGS84'
- if config.exists('datum'):
- coordinate_based['datum'] = config.return_value('datum')
+ if config.exists(['datum']):
+ coordinate_based['datum'] = config.return_value(['datum'])
intf = {
'name': name,
@@ -151,8 +127,8 @@ def get_location_intf(config, name):
def get_location(config):
- config.set_level('service lldp')
- intfs_names = config.list_nodes('interface')
+ config.set_level(base)
+ intfs_names = config.list_nodes(['interface'])
if len(intfs_names) < 0:
return 0
@@ -170,7 +146,7 @@ def get_location(config):
def get_config():
lldp = deepcopy(default_config_data)
conf = Config()
- if not conf.exists('service lldp'):
+ if not conf.exists(base):
return None
else:
lldp['options'] = get_options(conf)
@@ -232,10 +208,10 @@ def generate(lldp):
if lldp is None:
return
- with open('/opt/vyatta/etc/version', 'r') as f:
- tmp = f.read()
- lldp['options']['description'] = tmp.split()[1]
-
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'lldp')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
# generate listen on interfaces
for intf in lldp['interface_list']:
@@ -248,13 +224,13 @@ def generate(lldp):
lldp['options']['listen_on'].append(tmp)
# generate /etc/default/lldpd
- tmpl = jinja2.Template(lldp_tmpl)
+ tmpl = env.get_template('lldpd.tmpl')
config_text = tmpl.render(lldp)
with open(config_file, 'w') as f:
f.write(config_text)
# generate /etc/lldpd.d/01-vyos.conf
- tmpl = jinja2.Template(vyos_tmpl)
+ tmpl = env.get_template('vyos.conf.tmpl')
config_text = tmpl.render(lldp)
with open(vyos_config_file, 'w') as f:
f.write(config_text)
@@ -278,5 +254,5 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/mdns_repeater.py b/src/conf_mode/mdns_repeater.py
index cef735c0d..f738cc6a6 100755
--- a/src/conf_mode/mdns_repeater.py
+++ b/src/conf_mode/mdns_repeater.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2017 VyOS maintainers and contributors
+# Copyright (C) 2017-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
@@ -13,45 +13,42 @@
#
# 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 sys
import os
-import jinja2
-import netifaces
+
+from sys import exit
+from copy import deepcopy
+from jinja2 import FileSystemLoader, Environment
+from netifaces import ifaddresses, AF_INET
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
config_file = r'/etc/default/mdns-repeater'
-config_tmpl = """
-### Autogenerated by mdns_repeater.py ###
-DAEMON_ARGS="{{ interfaces | join(' ') }}"
-"""
-
default_config_data = {
'disabled': False,
'interfaces': []
}
def get_config():
- mdns = default_config_data
+ mdns = deepcopy(default_config_data)
conf = Config()
- if not conf.exists('service mdns repeater'):
+ base = ['service', 'mdns', 'repeater']
+ if not conf.exists(base):
return None
else:
- conf.set_level('service mdns repeater')
+ conf.set_level(base)
# Service can be disabled by user
- if conf.exists('disable'):
+ if conf.exists(['disable']):
mdns['disabled'] = True
return mdns
# Interface to repeat mDNS advertisements
- if conf.exists('interface'):
- mdns['interfaces'] = conf.return_values('interface')
+ if conf.exists(['interface']):
+ mdns['interfaces'] = conf.return_values(['interface'])
return mdns
@@ -69,8 +66,8 @@ def verify(mdns):
# For mdns-repeater to work it is essential that the interfaces has
# an IPv4 address assigned
for interface in mdns['interfaces']:
- if netifaces.AF_INET in netifaces.ifaddresses(interface).keys():
- if len(netifaces.ifaddresses(interface)[netifaces.AF_INET]) < 1:
+ if AF_INET in ifaddresses(interface).keys():
+ if len(ifaddresses(interface)[AF_INET]) < 1:
raise ConfigError('mDNS repeater requires an IPv6 address configured on interface %s!'.format(interface))
return None
@@ -83,7 +80,12 @@ def generate(mdns):
print('Warning: mDNS repeater will be deactivated because it is disabled')
return None
- tmpl = jinja2.Template(config_tmpl)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'mdns-repeater')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
+ tmpl = env.get_template('mdns-repeater.tmpl')
config_text = tmpl.render(mdns)
with open(config_file, 'w') as f:
f.write(config_text)
@@ -108,4 +110,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/ntp.py b/src/conf_mode/ntp.py
index c3e8d51b3..0f635556b 100755
--- a/src/conf_mode/ntp.py
+++ b/src/conf_mode/ntp.py
@@ -14,61 +14,19 @@
# 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 sys
import os
-import jinja2
-import ipaddress
from copy import deepcopy
+from ipaddress import ip_network
+from jinja2 import FileSystemLoader, Environment
+from sys import exit
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
config_file = r'/etc/ntp.conf'
-# Please be careful if you edit the template.
-config_tmpl = """
-### Autogenerated by ntp.py ###
-
-#
-# Non-configurable defaults
-#
-driftfile /var/lib/ntp/ntp.drift
-# By default, only allow ntpd to query time sources, ignore any incoming requests
-restrict default noquery nopeer notrap nomodify
-# Local users have unrestricted access, allowing reconfiguration via ntpdc
-restrict 127.0.0.1
-restrict -6 ::1
-
-#
-# Configurable section
-#
-
-{% if servers -%}
-{% for s in servers -%}
-# Server configuration for: {{ s.name }}
-server {{ s.name }} iburst {{ s.options | join(" ") }}
-{% endfor -%}
-{% endif %}
-
-{% if allowed_networks -%}
-{% for n in allowed_networks -%}
-# Client configuration for network: {{ n.network }}
-restrict {{ n.address }} mask {{ n.netmask }} nomodify notrap nopeer
-
-{% endfor -%}
-{% endif %}
-
-{% if listen_address -%}
-# NTP should listen on configured addresses only
-interface ignore wildcard
-{% for a in listen_address -%}
-interface listen {{ a }}
-{% endfor -%}
-{% endif %}
-
-"""
-
default_config_data = {
'servers': [],
'allowed_networks': [],
@@ -86,7 +44,7 @@ def get_config():
if conf.exists('allow-clients address'):
networks = conf.return_values('allow-clients address')
for n in networks:
- addr = ipaddress.ip_network(n)
+ addr = ip_network(n)
net = {
"network" : n,
"address" : addr.network_address,
@@ -128,7 +86,7 @@ def verify(ntp):
for n in ntp['allowed_networks']:
try:
- addr = ipaddress.ip_network( n['network'] )
+ addr = ip_network( n['network'] )
break
except ValueError:
raise ConfigError("{0} does not appear to be a valid IPv4 or IPv6 network, check host bits!".format(n['network']))
@@ -140,7 +98,12 @@ def generate(ntp):
if ntp is None:
return None
- tmpl = jinja2.Template(config_tmpl)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'ntp')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
+ tmpl = env.get_template('ntp.conf.tmpl')
config_text = tmpl.render(ntp)
with open(config_file, 'w') as f:
f.write(config_text)
@@ -165,4 +128,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py
index 58f5b5a0e..9940c80c5 100755
--- a/src/conf_mode/protocols_bfd.py
+++ b/src/conf_mode/protocols_bfd.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# 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
@@ -13,39 +13,20 @@
#
# 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 sys
-import jinja2
-import copy
import os
-import vyos.validate
-from vyos import ConfigError
+from sys import exit
+from copy import deepcopy
+from jinja2 import FileSystemLoader, Environment
+
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
+from vyos.validate import is_ipv6_link_local, is_ipv6
+from vyos import ConfigError
config_file = r'/tmp/bfd.frr'
-# Please be careful if you edit the template.
-config_tmpl = """
-!
-bfd
-{% for peer in old_peers -%}
- no peer {{ peer.remote }}{% if peer.multihop %} multihop{% endif %}{% if peer.src_addr %} local-address {{ peer.src_addr }}{% endif %}{% if peer.src_if %} interface {{ peer.src_if }}{% endif %}
-{% endfor -%}
-!
-{% for peer in new_peers -%}
- peer {{ peer.remote }}{% if peer.multihop %} multihop{% endif %}{% if peer.src_addr %} local-address {{ peer.src_addr }}{% endif %}{% if peer.src_if %} interface {{ peer.src_if }}{% endif %}
- detect-multiplier {{ peer.multiplier }}
- receive-interval {{ peer.rx_interval }}
- transmit-interval {{ peer.tx_interval }}
- {% if peer.echo_mode %}echo-mode{% endif %}
- {% if peer.echo_interval != '' %}echo-interval {{ peer.echo_interval }}{% endif %}
- {% if not peer.shutdown %}no {% endif %}shutdown
-{% endfor -%}
-!
-"""
-
default_config_data = {
'new_peers': [],
'old_peers' : []
@@ -132,7 +113,7 @@ def get_bfd_peer_config(peer, conf_mode="proposed"):
return bfd_peer
def get_config():
- bfd = copy.deepcopy(default_config_data)
+ bfd = deepcopy(default_config_data)
conf = Config()
if not (conf.exists('protocols bfd') or conf.exists_effective('protocols bfd')):
return None
@@ -164,12 +145,12 @@ def verify(bfd):
for peer in bfd['new_peers']:
# IPv6 link local peers require an explicit local address/interface
- if vyos.validate.is_ipv6_link_local(peer['remote']):
+ if is_ipv6_link_local(peer['remote']):
if not (peer['src_if'] and peer['src_addr']):
raise ConfigError('BFD IPv6 link-local peers require explicit local address and interface setting')
# IPv6 peers require an explicit local address
- if vyos.validate.is_ipv6(peer['remote']):
+ if is_ipv6(peer['remote']):
if not peer['src_addr']:
raise ConfigError('BFD IPv6 peers require explicit local address setting')
@@ -208,18 +189,23 @@ def generate(bfd):
if bfd is None:
return None
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'frr-bfd')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
+ tmpl = env.get_template('bfd.frr.tmpl')
+ config_text = tmpl.render(bfd)
+ with open(config_file, 'w') as f:
+ f.write(config_text)
+
return None
def apply(bfd):
if bfd is None:
return None
- tmpl = jinja2.Template(config_tmpl)
- config_text = tmpl.render(bfd)
- with open(config_file, 'w') as f:
- f.write(config_text)
-
- os.system("sudo vtysh -d bfdd -f " + config_file)
+ os.system(f'vtysh -d bfdd -f {config_file}')
if os.path.exists(config_file):
os.remove(config_file)
@@ -233,4 +219,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/protocols_igmp.py b/src/conf_mode/protocols_igmp.py
index 8d25fe9d5..0148b5dac 100755
--- a/src/conf_mode/protocols_igmp.py
+++ b/src/conf_mode/protocols_igmp.py
@@ -13,46 +13,19 @@
#
# 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 sys
-import jinja2
-import copy
import os
-import vyos.validate
+
+from ipaddress import IPv4Address
+from jinja2 import FileSystemLoader, Environment
+from sys import exit
from vyos import ConfigError
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
config_file = r'/tmp/igmp.frr'
-# Please be careful if you edit the template.
-config_tmpl = """
-!
-{% for iface in old_ifaces -%}
-interface {{ iface }}
-no ip igmp
-!
-{% endfor -%}
-{% for iface in ifaces -%}
-interface {{ iface }}
-{% if ifaces[iface].version -%}
-ip igmp version {{ ifaces[iface].version }}
-{% else -%}
-{# IGMP default version 3 #}
-ip igmp
-{% endif -%}
-{% if ifaces[iface].query_interval -%}
-ip igmp query-interval {{ ifaces[iface].query_interval }}
-{% endif -%}
-{% if ifaces[iface].query_max_resp_time -%}
-ip igmp query-max-response-time {{ ifaces[iface].query_max_resp_time }}
-{% endif -%}
-!
-{% endfor -%}
-!
-"""
-
def get_config():
conf = Config()
igmp_conf = {
@@ -74,18 +47,24 @@ def get_config():
iface : {
'version' : conf.return_effective_value('interface {0} version'.format(iface)),
'query_interval' : conf.return_effective_value('interface {0} query-interval'.format(iface)),
- 'query_max_resp_time' : conf.return_effective_value('interface {0} query-max-response-time'.format(iface))
+ 'query_max_resp_time' : conf.return_effective_value('interface {0} query-max-response-time'.format(iface)),
+ 'gr_join' : {}
}
})
+ for gr_join in conf.list_effective_nodes('interface {0} join'.format(iface)):
+ igmp_conf['old_ifaces'][iface]['gr_join'][gr_join] = conf.return_effective_values('interface {0} join {1} source'.format(iface, gr_join))
for iface in conf.list_nodes('interface'):
igmp_conf['ifaces'].update({
iface : {
'version' : conf.return_value('interface {0} version'.format(iface)),
'query_interval' : conf.return_value('interface {0} query-interval'.format(iface)),
- 'query_max_resp_time' : conf.return_value('interface {0} query-max-response-time'.format(iface))
+ 'query_max_resp_time' : conf.return_value('interface {0} query-max-response-time'.format(iface)),
+ 'gr_join' : {}
}
})
+ for gr_join in conf.list_nodes('interface {0} join'.format(iface)):
+ igmp_conf['ifaces'][iface]['gr_join'][gr_join] = conf.return_values('interface {0} join {1} source'.format(iface, gr_join))
return igmp_conf
@@ -97,12 +76,22 @@ def verify(igmp):
# Check interfaces
if not igmp['ifaces']:
raise ConfigError(f"IGMP require defined interfaces!")
+ # Check, is this multicast group
+ for intfc in igmp['ifaces']:
+ for gr_addr in igmp['ifaces'][intfc]['gr_join']:
+ if IPv4Address(gr_addr) < IPv4Address('224.0.0.0'):
+ raise ConfigError(gr_addr + " not a multicast group")
def generate(igmp):
if igmp is None:
return None
- tmpl = jinja2.Template(config_tmpl)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'igmp')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
+ tmpl = env.get_template('igmp.frr.tmpl')
config_text = tmpl.render(igmp)
with open(config_file, 'w') as f:
f.write(config_text)
@@ -127,4 +116,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py
index 5f2497660..514fe5efb 100755
--- a/src/conf_mode/protocols_mpls.py
+++ b/src/conf_mode/protocols_mpls.py
@@ -13,81 +13,17 @@
#
# 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 sys
-import jinja2
-import copy
import os
-import vyos.validate
-from vyos import ConfigError
+from jinja2 import FileSystemLoader, Environment
+
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
+from vyos import ConfigError
config_file = r'/tmp/ldpd.frr'
-# Please be careful if you edit the template.
-config_tmpl = """
-!
-{% if mpls_ldp -%}
-mpls ldp
-{% if old_router_id -%}
-no router-id {{ old_router_id }}
-{% endif -%}
-{% if router_id -%}
-router-id {{ router_id }}
-{% endif -%}
-{% for neighbor_id in old_ldp.neighbors -%}
-no neighbor {{neighbor_id}} password {{old_ldp.neighbors[neighbor_id].password}}
-{% endfor -%}
-{% for neighbor_id in ldp.neighbors -%}
-neighbor {{neighbor_id}} password {{ldp.neighbors[neighbor_id].password}}
-{% endfor -%}
-address-family ipv4
-label local allocate host-routes
-{% if old_ldp.d_transp_ipv4 -%}
-no discovery transport-address {{ old_ldp.d_transp_ipv4 }}
-{% endif -%}
-{% if ldp.d_transp_ipv4 -%}
-discovery transport-address {{ ldp.d_transp_ipv4 }}
-{% endif -%}
-{% for interface in old_ldp.interfaces -%}
-no interface {{interface}}
-{% endfor -%}
-{% for interface in ldp.interfaces -%}
-interface {{interface}}
-{% endfor -%}
-!
-!
-exit-address-family
-!
-{% if ldp.d_transp_ipv6 -%}
-address-family ipv6
-label local allocate host-routes
-{% if old_ldp.d_transp_ipv6 -%}
-no discovery transport-address {{ old_ldp.d_transp_ipv6 }}
-{% endif -%}
-{% if ldp.d_transp_ipv6 -%}
-discovery transport-address {{ ldp.d_transp_ipv6 }}
-{% endif -%}
-{% for interface in old_ldp.interfaces -%}
-no interface {{interface}}
-{% endfor -%}
-{% for interface in ldp.interfaces -%}
-interface {{interface}}
-{% endfor -%}
-!
-exit-address-family
-{% else -%}
-no address-family ipv6
-{% endif -%}
-!
-{% else -%}
-no mpls ldp
-{% endif -%}
-!
-"""
-
def sysctl(name, value):
os.system('sysctl -wq {}={}'.format(name, value))
@@ -159,12 +95,10 @@ def get_config():
}
})
- # print(mpls_conf)
- # sys.exit(1)
return mpls_conf
def operate_mpls_on_intfc(interfaces, action):
- rp_filter = 0
+ rp_filter = 0
if action == 1:
rp_filter = 2
for iface in interfaces:
@@ -193,7 +127,12 @@ def generate(mpls):
if mpls is None:
return None
- tmpl = jinja2.Template(config_tmpl)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'mpls')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
+ tmpl = env.get_template('ldpd.frr.tmpl')
config_text = tmpl.render(mpls)
with open(config_file, 'w') as f:
f.write(config_text)
@@ -234,4 +173,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/protocols_pim.py b/src/conf_mode/protocols_pim.py
index dc65a59a6..7b360d62c 100755
--- a/src/conf_mode/protocols_pim.py
+++ b/src/conf_mode/protocols_pim.py
@@ -13,55 +13,19 @@
#
# 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 sys
-import jinja2
-import copy
import os
-import vyos.validate
+
from ipaddress import IPv4Address
+from jinja2 import FileSystemLoader, Environment
+from sys import exit
-from vyos import ConfigError
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
+from vyos import ConfigError
config_file = r'/tmp/pimd.frr'
-# Please be careful if you edit the template.
-config_tmpl = """
-!
-{% for rp_addr in old_pim.rp -%}
-{% for group in old_pim.rp[rp_addr] -%}
-no ip pim rp {{ rp_addr }} {{ group }}
-{% endfor -%}
-{% endfor -%}
-{% if old_pim.rp_keep_alive -%}
-no ip pim rp keep-alive-timer {{ old_pim.rp_keep_alive }}
-{% endif -%}
-{% for iface in old_pim.ifaces -%}
-interface {{ iface }}
-no ip pim
-!
-{% endfor -%}
-{% for iface in pim.ifaces -%}
-interface {{ iface }}
-{% if pim.ifaces[iface].hello -%}
-ip pim hello {{ pim.ifaces[iface].hello }}
-{% endif -%}
-ip pim
-!
-{% endfor -%}
-{% for rp_addr in pim.rp -%}
-{% for group in pim.rp[rp_addr] -%}
-ip pim rp {{ rp_addr }} {{ group }}
-{% endfor -%}
-{% endfor -%}
-{% if pim.rp_keep_alive -%}
-ip pim rp keep-alive-timer {{ pim.rp_keep_alive }}
-{% endif -%}
-!
-"""
-
def get_config():
conf = Config()
pim_conf = {
@@ -87,14 +51,16 @@ def get_config():
for iface in conf.list_effective_nodes('interface'):
pim_conf['old_pim']['ifaces'].update({
iface : {
- 'hello' : conf.return_effective_value('interface {0} hello'.format(iface))
+ 'hello' : conf.return_effective_value('interface {0} hello'.format(iface)),
+ 'dr_prio' : conf.return_effective_value('interface {0} dr-priority'.format(iface))
}
})
for iface in conf.list_nodes('interface'):
pim_conf['pim']['ifaces'].update({
iface : {
- 'hello' : conf.return_value('interface {0} hello'.format(iface))
+ 'hello' : conf.return_value('interface {0} hello'.format(iface)),
+ 'dr_prio' : conf.return_value('interface {0} dr-priority'.format(iface)),
}
})
@@ -147,7 +113,12 @@ def generate(pim):
if pim is None:
return None
- tmpl = jinja2.Template(config_tmpl)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'pim')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
+ tmpl = env.get_template('pimd.frr.tmpl')
config_text = tmpl.render(pim)
with open(config_file, 'w') as f:
f.write(config_text)
@@ -159,7 +130,7 @@ def apply(pim):
return None
if os.path.exists(config_file):
- os.system("sudo vtysh -d pimd -f " + config_file)
+ os.system("vtysh -d pimd -f " + config_file)
os.remove(config_file)
return None
@@ -172,4 +143,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/salt-minion.py b/src/conf_mode/salt-minion.py
index 303ddae48..bc1767454 100755
--- a/src/conf_mode/salt-minion.py
+++ b/src/conf_mode/salt-minion.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,102 +13,35 @@
#
# 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 sys
import os
-import pwd
-import socket
-import urllib3
-import jinja2
+from copy import deepcopy
+from jinja2 import FileSystemLoader, Environment
+from pwd import getpwnam
+from socket import gethostname
+from sys import exit
+from urllib3 import PoolManager
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
config_file = r'/etc/salt/minion'
-# Please be careful if you edit the template.
-config_tmpl = """
-### Autogenerated by salt-minion.py ###
-
-##### Primary configuration settings #####
-##########################################
-
-# The hash_type is the hash to use when discovering the hash of a file on
-# the master server. The default is sha256, but md5, sha1, sha224, sha384 and
-# sha512 are also supported.
-#
-# WARNING: While md5 and sha1 are also supported, do not use them due to the
-# high chance of possible collisions and thus security breach.
-#
-# Prior to changing this value, the master should be stopped and all Salt
-# caches should be cleared.
-hash_type: {{ hash_type }}
-
-##### Logging settings #####
-##########################################
-# The location of the minion log file
-# The minion log can be sent to a regular file, local path name, or network
-# location. Remote logging works best when configured to use rsyslogd(8) (e.g.:
-# ``file:///dev/log``), with rsyslogd(8) configured for network logging. The URI
-# format is: <file|udp|tcp>://<host|socketpath>:<port-if-required>/<log-facility>
-#log_file: /var/log/salt/minion
-#log_file: file:///dev/log
-#log_file: udp://loghost:10514
-#
-log_file: {{ log_file }}
-
-# The level of messages to send to the console.
-# One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'.
-#
-# The following log levels are considered INSECURE and may log sensitive data:
-# ['garbage', 'trace', 'debug']
-#
-# Default: 'warning'
-log_level: {{ log_level }}
-
-# Set the location of the salt master server, if the master server cannot be
-# resolved, then the minion will fail to start.
-master:
-{% for host in master -%}
-- {{ host }}
-{% endfor %}
-
-# The user to run salt
-user: {{ user }}
-
-# The directory to store the pki information in
-pki_dir: /config/salt/pki/minion
-
-# Explicitly declare the id for this minion to use, if left commented the id
-# will be the hostname as returned by the python call: socket.getfqdn()
-# Since salt uses detached ids it is possible to run multiple minions on the
-# same machine but with different ids, this can be useful for salt compute
-# clusters.
-id: {{ salt_id }}
-
-
-# The number of minutes between mine updates.
-mine_interval: {{ mine_interval }}
-
-verify_master_pubkey_sign: {{ verify_master_pubkey_sign }}
-"""
-
default_config_data = {
'hash_type': 'sha256',
'log_file': '/var/log/salt/minion',
'log_level': 'warning',
'master' : 'salt',
'user': 'minion',
- 'salt_id': socket.gethostname(),
+ 'salt_id': gethostname(),
'mine_interval': '60',
'verify_master_pubkey_sign': 'false'
}
def get_config():
- salt = default_config_data
+ salt = deepcopy(default_config_data)
conf = Config()
if not conf.exists('service salt-minion'):
return None
@@ -145,25 +78,31 @@ def get_config():
return salt
def generate(salt):
- paths = ['/etc/salt/','/var/run/salt','/opt/vyatta/etc/config/salt/']
+ paths = ['/etc/salt/','/var/run/salt','/opt/vyatta/etc/config/salt/']
directory = '/opt/vyatta/etc/config/salt/pki/minion'
- uid = pwd.getpwnam(salt['user']).pw_uid
- http = urllib3.PoolManager()
+ uid = getpwnam(salt['user']).pw_uid
+ http = PoolManager()
if salt is None:
return None
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'salt-minion')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
if not os.path.exists(directory):
os.makedirs(directory)
- tmpl = jinja2.Template(config_tmpl)
+ tmpl = env.get_template('minion.tmpl')
config_text = tmpl.render(salt)
with open(config_file, 'w') as f:
f.write(config_text)
- path = "/etc/salt/"
+
+ path = "/etc/salt/"
for path in paths:
- for root, dirs, files in os.walk(path):
- for usgr in dirs:
+ for root, dirs, files in os.walk(path):
+ for usgr in dirs:
os.chown(os.path.join(root, usgr), uid, 100)
for usgr in files:
os.chown(os.path.join(root, usgr), uid, 100)
@@ -171,14 +110,14 @@ def generate(salt):
if not os.path.exists('/opt/vyatta/etc/config/salt/pki/minion/master_sign.pub'):
if not salt['master-key'] is None:
r = http.request('GET', salt['master-key'], preload_content=False)
-
+
with open('/opt/vyatta/etc/config/salt/pki/minion/master_sign.pub', 'wb') as out:
while True:
data = r.read(1024)
if not data:
break
out.write(data)
-
+
r.release_conn()
return None
@@ -200,4 +139,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/service-ipoe.py b/src/conf_mode/service-ipoe.py
index bd7a898d0..dd9616a62 100755
--- a/src/conf_mode/service-ipoe.py
+++ b/src/conf_mode/service-ipoe.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,19 +13,18 @@
#
# 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 sys
import os
import re
-import time
-import socket
import subprocess
-import jinja2
-import syslog as sl
+
+from jinja2 import FileSystemLoader, Environment
+from socket import socket, AF_INET, SOCK_STREAM
+from sys import exit
+from time import sleep
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
ipoe_cnf_dir = r'/etc/accel-ppp/ipoe'
@@ -37,157 +36,8 @@ cmd_port = r'2002'
chap_secrets = ipoe_cnf_dir + '/chap-secrets'
## accel-pppd -d -c /etc/accel-ppp/pppoe/pppoe.config -p /var/run/accel_pppoe.pid
-ipoe_config = '''
-### generated by ipoe.py ###
-[modules]
-log_syslog
-ipoe
-shaper
-ipv6pool
-ipv6_nd
-ipv6_dhcp
-{% if auth['mech'] == 'radius' %}
-radius
-{% endif -%}
-ippool
-{% if auth['mech'] == 'local' %}
-chap-secrets
-{% endif %}
-
-[core]
-thread-count={{thread_cnt}}
-
-[log]
-syslog=accel-ipoe,daemon
-copy=1
-level=5
-
-[ipoe]
-verbose=1
-{% for intfc in interfaces %}
-{% if interfaces[intfc]['vlan_mon'] %}
-interface=re:{{intfc}}\.\d+,\
-{% else %}
-interface={{intfc}},\
-{% endif %}
-shared={{interfaces[intfc]['shared']}},\
-mode={{interfaces[intfc]['mode']}},\
-ifcfg={{interfaces[intfc]['ifcfg']}},\
-range={{interfaces[intfc]['range']}},\
-start={{interfaces[intfc]['sess_start']}},\
-ipv6=1
-{% endfor %}
-{% if auth['mech'] == 'noauth' %}
-noauth=1
-{% endif %}
-{% if auth['mech'] == 'local' %}
-username=ifname
-password=csid
-{% endif %}
-
-{%- for intfc in interfaces %}
-{% if (interfaces[intfc]['shared'] == '0') and (interfaces[intfc]['vlan_mon']) %}
-vlan-mon={{intfc}},{{interfaces[intfc]['vlan_mon']|join(',')}}
-{% endif %}
-{% endfor %}
-
-{% if (dns['server1']) or (dns['server2']) %}
-[dns]
-{% if dns['server1'] %}
-dns1={{dns['server1']}}
-{% endif -%}
-{% if dns['server2'] %}
-dns2={{dns['server2']}}
-{% endif -%}
-{% endif -%}
-
-{% if (dnsv6['server1']) or (dnsv6['server2']) or (dnsv6['server3']) %}
-[dnsv6]
-dns={{dnsv6['server1']}}
-dns={{dnsv6['server2']}}
-dns={{dnsv6['server3']}}
-{% endif %}
-
-[ipv6-nd]
-verbose=1
-
-[ipv6-dhcp]
-verbose=1
-
-{% if ipv6['prfx'] %}
-[ipv6-pool]
-{% for prfx in ipv6['prfx'] %}
-{{prfx}}
-{% endfor %}
-{% for pd in ipv6['pd'] %}
-delegate={{pd}}
-{% endfor %}
-{% endif %}
-
-{% if auth['mech'] == 'local' %}
-[chap-secrets]
-chap-secrets=/etc/accel-ppp/ipoe/chap-secrets
-{% endif %}
-
-{% if auth['mech'] == 'radius' %}
-[radius]
-verbose=1
-{% for srv in auth['radius'] %}
-server={{srv}},{{auth['radius'][srv]['secret']}},\
-req-limit={{auth['radius'][srv]['req-limit']}},\
-fail-time={{auth['radius'][srv]['fail-time']}}
-{% endfor %}
-{% if auth['radsettings']['dae-server']['ip-address'] %}
-dae-server={{auth['radsettings']['dae-server']['ip-address']}}:\
-{{auth['radsettings']['dae-server']['port']}},\
-{{auth['radsettings']['dae-server']['secret']}}
-{% endif -%}
-{% if auth['radsettings']['acct-timeout'] %}
-acct-timeout={{auth['radsettings']['acct-timeout']}}
-{% endif -%}
-{% if auth['radsettings']['max-try'] %}
-max-try={{auth['radsettings']['max-try']}}
-{% endif -%}
-{% if auth['radsettings']['timeout'] %}
-timeout={{auth['radsettings']['timeout']}}
-{% endif -%}
-{% if auth['radsettings']['nas-ip-address'] %}
-nas-ip-address={{auth['radsettings']['nas-ip-address']}}
-{% endif -%}
-{% if auth['radsettings']['nas-identifier'] %}
-nas-identifier={{auth['radsettings']['nas-identifier']}}
-{% endif -%}
-{% endif %}
-
-[cli]
-tcp=127.0.0.1:2002
-'''
-
-# chap secrets
-chap_secrets_conf = '''
-# username server password acceptable local IP addresses shaper
-{% for aifc in auth['auth_if'] %}
-{% for mac in auth['auth_if'][aifc] %}
-{% if (auth['auth_if'][aifc][mac]['up']) and (auth['auth_if'][aifc][mac]['down']) %}
-{% if auth['auth_if'][aifc][mac]['vlan'] %}
-{{aifc}}.{{auth['auth_if'][aifc][mac]['vlan']}}\t*\t{{mac.lower()}}\t*\t{{auth['auth_if'][aifc][mac]['down']}}/{{auth['auth_if'][aifc][mac]['up']}}
-{% else %}
-{{aifc}}\t*\t{{mac.lower()}}\t*\t{{auth['auth_if'][aifc][mac]['down']}}/{{auth['auth_if'][aifc][mac]['up']}}
-{% endif %}
-{% else %}
-{% if auth['auth_if'][aifc][mac]['vlan'] %}
-{{aifc}}.{{auth['auth_if'][aifc][mac]['vlan']}}\t*\t{{mac.lower()}}\t*
-{% else %}
-{{aifc}}\t*\t{{mac.lower()}}\t*
-{% endif %}
-{% endif %}
-{% endfor %}
-{% endfor %}
-'''
-
if not os.path.exists(ipoe_cnf_dir):
os.makedirs(ipoe_cnf_dir)
- sl.syslog(sl.LOG_NOTICE, ipoe_cnf_dir + " created")
def _get_cpu():
@@ -201,13 +51,13 @@ def _get_cpu():
def _chk_con():
cnt = 0
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s = socket(AF_INET, SOCK_STREAM)
while True:
try:
s.connect(("127.0.0.1", int(cmd_port)))
break
except ConnectionRefusedError:
- time.sleep(0.5)
+ sleep(0.5)
cnt += 1
if cnt == 100:
raise("failed to start pppoe server")
@@ -224,18 +74,6 @@ def _accel_cmd(cmd=''):
except:
return 1
-# chap_secrets file if auth mode local
-
-
-def _gen_chap_secrets(c):
-
- tmpl = jinja2.Template(chap_secrets_conf, trim_blocks=True)
- chap_secrets_txt = tmpl.render(c)
- old_umask = os.umask(0o077)
- open(chap_secrets, 'w').write(chap_secrets_txt)
- os.umask(old_umask)
- sl.syslog(sl.LOG_NOTICE, chap_secrets + ' written')
-
##### Inline functions end ####
@@ -388,14 +226,25 @@ def generate(c):
if c == None or not c:
return None
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'ipoe-server')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader, trim_blocks=True)
+
c['thread_cnt'] = _get_cpu()
if c['auth']['mech'] == 'local':
- _gen_chap_secrets(c)
-
- tmpl = jinja2.Template(ipoe_config, trim_blocks=True)
+ tmpl = env.get_template('chap-secrets.tmpl')
+ chap_secrets_txt = tmpl.render(c)
+ old_umask = os.umask(0o077)
+ with open(chap_secrets, 'w') as f:
+ f.write(chap_secrets_txt)
+ os.umask(old_umask)
+
+ tmpl = env.get_template('ipoe.config.tmpl')
config_text = tmpl.render(c)
- open(ipoe_cnf, 'w').write(config_text)
+ with open(ipoe_cnf, 'w') as f:
+ f.write(config_text)
return c
@@ -465,7 +314,6 @@ def apply(c):
raise ConfigError('accel-pppd failed to start')
else:
_accel_cmd('restart')
- sl.syslog(sl.LOG_NOTICE, "reloading config via daemon restart")
if __name__ == '__main__':
@@ -476,4 +324,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/service-pppoe.py b/src/conf_mode/service-pppoe.py
index 22250d18b..afcc5ba99 100755
--- a/src/conf_mode/service-pppoe.py
+++ b/src/conf_mode/service-pppoe.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,19 +13,18 @@
#
# 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 sys
import os
import re
import subprocess
-import jinja2
-import socket
-import time
-import syslog as sl
+
+from jinja2 import FileSystemLoader, Environment
+from socket import socket, AF_INET, SOCK_STREAM
+from sys import exit
+from time import sleep
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
pidfile = r'/var/run/accel_pppoe.pid'
@@ -38,282 +37,26 @@ pppoe_conf = pppoe_cnf_dir + '/pppoe.config'
# config path creation
if not os.path.exists(pppoe_cnf_dir):
os.makedirs(pppoe_cnf_dir)
- sl.syslog(sl.LOG_NOTICE, pppoe_cnf_dir + " created")
-
-pppoe_config = '''
-### generated by accel_pppoe.py ###
-[modules]
-log_syslog
-pppoe
-{% if authentication['mode'] == 'radius' %}
-radius
-{% endif %}
-ippool
-{% if ppp_options['ipv6'] != 'deny' %}
-ipv6pool
-ipv6_nd
-ipv6_dhcp
-{% endif %}
-chap-secrets
-auth_pap
-auth_chap_md5
-auth_mschap_v1
-auth_mschap_v2
-#pppd_compat
-shaper
-{% if snmp == 'enable' or snmp == 'enable-ma' %}
-net-snmp
-{% endif %}
-{% if limits %}
-connlimit
-{% endif %}
-
-[core]
-thread-count={{thread_cnt}}
-
-[log]
-syslog=accel-pppoe,daemon
-copy=1
-level=5
-
-{% if snmp == 'enable-ma' %}
-[snmp]
-master=1
-{% endif -%}
-
-[client-ip-range]
-disable
-
-{% if ppp_gw %}
-[ip-pool]
-gw-ip-address={{ppp_gw}}
-{% if client_ip_pool %}
-{{client_ip_pool}}
-{% endif -%}
-
-{% if client_ip_subnets %}
-{% for sn in client_ip_subnets %}
-{{sn}}
-{% endfor %}
-{% endif %}
-{% endif -%}
-
-{% if client_ipv6_pool %}
-[ipv6-pool]
-{% for prfx in client_ipv6_pool['prefix']: %}
-{{prfx}}
-{% endfor %}
-{% for prfx in client_ipv6_pool['delegate-prefix']: %}
-delegate={{prfx}}
-{% endfor %}
-{% endif %}
-
-{% if dns %}
-[dns]
-{% if dns[0] %}
-dns1={{dns[0]}}
-{% endif -%}
-{% if dns[1] %}
-dns2={{dns[1]}}
-{% endif -%}
-{% endif %}
-
-{% if dnsv6 %}
-[ipv6-dns]
-{% for srv in dnsv6: %}
-{{srv}}
-{% endfor %}
-{% endif %}
-
-{% if wins %}
-[wins]
-{% if wins[0] %}
-wins1={{wins[0]}}
-{% endif %}
-{% if wins[1] %}
-wins2={{wins[1]}}
-{% endif -%}
-{% endif -%}
-
-{% if authentication['mode'] == 'local' %}
-[chap-secrets]
-chap-secrets=/etc/accel-ppp/pppoe/chap-secrets
-{% endif -%}
-
-{% if authentication['mode'] == 'radius' %}
-[radius]
-{% for rsrv in authentication['radiussrv']: %}
-server={{rsrv}},{{authentication['radiussrv'][rsrv]['secret']}},\
-req-limit={{authentication['radiussrv'][rsrv]['req-limit']}},\
-fail-time={{authentication['radiussrv'][rsrv]['fail-time']}}
-{% endfor %}
-{% if authentication['radiusopt']['timeout'] %}
-timeout={{authentication['radiusopt']['timeout']}}
-{% endif %}
-{% if authentication['radiusopt']['acct-timeout'] %}
-acct-timeout={{authentication['radiusopt']['acct-timeout']}}
-{% endif %}
-{% if authentication['radiusopt']['max-try'] %}
-max-try={{authentication['radiusopt']['max-try']}}
-{% endif %}
-{% if authentication['radiusopt']['nas-id'] %}
-nas-identifier={{authentication['radiusopt']['nas-id']}}
-{% endif %}
-{% if authentication['radiusopt']['nas-ip'] %}
-nas-ip-address={{authentication['radiusopt']['nas-ip']}}
-{% endif -%}
-{% if authentication['radiusopt']['dae-srv'] %}
-dae-server={{authentication['radiusopt']['dae-srv']['ip-addr']}}:\
-{{authentication['radiusopt']['dae-srv']['port']}},\
-{{authentication['radiusopt']['dae-srv']['secret']}}
-{% endif -%}
-gw-ip-address={{ppp_gw}}
-verbose=1
-
-{% if authentication['radiusopt']['shaper'] %}
-[shaper]
-verbose=1
-attr={{authentication['radiusopt']['shaper']['attr']}}
-{% if authentication['radiusopt']['shaper']['vendor'] %}
-vendor={{authentication['radiusopt']['shaper']['vendor']}}
-{% endif -%}
-{% endif -%}
-{% endif %}
-
-[ppp]
-verbose=1
-check-ip=1
-{% if not sesscrtl == 'disable' %}
-single-session={{sesscrtl}}
-{% endif -%}
-{% if ppp_options['ccp'] %}
-ccp=1
-{% endif %}
-{% if ppp_options['min-mtu'] %}
-min-mtu={{ppp_options['min-mtu']}}
-{% else %}
-min-mtu={{mtu}}
-{% endif %}
-{% if ppp_options['mru'] %}
-mru={{ppp_options['mru']}}
-{% endif %}
-{% if ppp_options['mppe'] %}
-mppe={{ppp_options['mppe']}}
-{% else %}
-mppe=prefer
-{% endif %}
-{% if ppp_options['lcp-echo-interval'] %}
-lcp-echo-interval={{ppp_options['lcp-echo-interval']}}
-{% else %}
-lcp-echo-interval=30
-{% endif %}
-{% if ppp_options['lcp-echo-timeout'] %}
-lcp-echo-timeout={{ppp_options['lcp-echo-timeout']}}
-{% endif %}
-{% if ppp_options['lcp-echo-failure'] %}
-lcp-echo-failure={{ppp_options['lcp-echo-failure']}}
-{% else %}
-lcp-echo-failure=3
-{% endif %}
-{% if ppp_options['ipv4'] %}
-ipv4={{ppp_options['ipv4']}}
-{% endif %}
-{% if client_ipv6_pool %}
-ipv6=allow
-{% endif %}
-
-{% if ppp_options['ipv6'] %}
-ipv6={{ppp_options['ipv6']}}
-{% if ppp_options['ipv6-intf-id'] %}
-ipv6-intf-id={{ppp_options['ipv6-intf-id']}}
-{% endif %}
-{% if ppp_options['ipv6-peer-intf-id'] %}
-ipv6-peer-intf-id={{ppp_options['ipv6-peer-intf-id']}}
-{% endif %}
-{% if ppp_options['ipv6-accept-peer-intf-id'] %}
-ipv6-accept-peer-intf-id={{ppp_options['ipv6-accept-peer-intf-id']}}
-{% endif %}
-{% endif %}
-mtu={{mtu}}
-
-[pppoe]
-verbose=1
-{% if concentrator %}
-ac-name={{concentrator}}
-{% endif %}
-{% if interface %}
-{% for int in interface %}
-interface={{int}}
-{% if interface[int]['vlans'] %}
-vlan-mon={{int}},{{interface[int]['vlans']|join(',')}}
-interface=re:{{int}}\.\d+
-{% endif %}
-{% endfor -%}
-{% endif -%}
-
-{% if svc_name %}
-service-name={{svc_name|join(',')}}
-{% endif -%}
-
-{% if pado_delay %}
-pado-delay={{pado_delay}}
-{% endif %}
-
-{% if limits %}
-[connlimit]
-limit={{limits['conn-limit']}}
-burst={{limits['burst']}}
-timeout={{limits['timeout']}}
-{% endif %}
-
-[cli]
-tcp=127.0.0.1:2001
-'''
-
-# pppoe chap secrets
-chap_secrets_conf = '''
-# username server password acceptable local IP addresses shaper
-{% for user in authentication['local-users'] %}
-{% if authentication['local-users'][user]['state'] == 'enabled' %}
-{% if (authentication['local-users'][user]['upload']) and (authentication['local-users'][user]['download']) %}
-{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}\t\
-{{authentication['local-users'][user]['download']}}/{{authentication['local-users'][user]['upload']}}
-{% else %}
-{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}
-{% endif %}
-{% endif %}
-{% endfor %}
-'''
+
#
# depending on hw and threads, daemon needs a little to start
# if it takes longer than 100 * 0.5 secs, exception is being raised
# not sure if that's the best way to check it, but it worked so far quite well
#
-
-
def _chk_con():
cnt = 0
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s = socket(AF_INET, SOCK_STREAM)
while True:
try:
s.connect(("127.0.0.1", 2001))
break
except ConnectionRefusedError:
- time.sleep(0.5)
+ sleep(0.5)
cnt += 1
if cnt == 100:
raise("failed to start pppoe server")
-def _write_chap_secrets(c):
- tmpl = jinja2.Template(chap_secrets_conf, trim_blocks=True)
- chap_secrets_txt = tmpl.render(c)
- old_umask = os.umask(0o077)
- open(chap_secrets, 'w').write(chap_secrets_txt)
- os.umask(old_umask)
- sl.syslog(sl.LOG_NOTICE, chap_secrets + ' written')
-
-
def _accel_cmd(cmd=''):
if not cmd:
return None
@@ -640,6 +383,11 @@ def generate(c):
if c == None:
return None
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'pppoe-server')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader, trim_blocks=True)
+
# accel-cmd reload doesn't work so any change results in a restart of the
# daemon
try:
@@ -653,12 +401,18 @@ def generate(c):
else:
c['thread_cnt'] = int(os.cpu_count() / 2)
- tmpl = jinja2.Template(pppoe_config, trim_blocks=True)
+ tmpl = env.get_template('pppoe.config.tmpl')
config_text = tmpl.render(c)
- open(pppoe_conf, 'w').write(config_text)
+ with open(pppoe_conf, 'w') as f:
+ f.write(config_text)
if c['authentication']['local-users']:
- _write_chap_secrets(c)
+ tmpl = env.get_template('chap-secrets.tmpl')
+ chap_secrets_txt = tmpl.render(c)
+ old_umask = os.umask(0o077)
+ with open(chap_secrets, 'w') as f:
+ f.write(chap_secrets_txt)
+ os.umask(old_umask)
return c
@@ -680,7 +434,6 @@ def apply(c):
raise ConfigError('accel-pppd failed to start')
else:
_accel_cmd('restart')
- sl.syslog(sl.LOG_NOTICE, "reloading config via daemon restart")
if __name__ == '__main__':
@@ -691,4 +444,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/service-router-advert.py b/src/conf_mode/service-router-advert.py
index 1e0d28397..38c5cb2dc 100755
--- a/src/conf_mode/service-router-advert.py
+++ b/src/conf_mode/service-router-advert.py
@@ -15,55 +15,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import sys
-import jinja2
+from jinja2 import FileSystemLoader, Environment
from stat import S_IRUSR, S_IWUSR, S_IRGRP
+from sys import exit
+
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
config_file = r'/etc/radvd.conf'
-config_tmpl = """
-### Autogenerated by service-router-advert.py ###
-
-{% for i in interfaces -%}
-interface {{ i.name }} {
- IgnoreIfMissing on;
- AdvDefaultPreference {{ i.default_preference }};
- AdvManagedFlag {{ i.managed_flag }};
- MaxRtrAdvInterval {{ i.interval_max }};
-{% if i.interval_min %}
- MinRtrAdvInterval {{ i.interval_min }};
-{% endif %}
- AdvReachableTime {{ i.reachable_time }};
- AdvIntervalOpt {{ i.send_advert }};
- AdvSendAdvert {{ i.send_advert }};
-{% if i.default_lifetime %}
- AdvDefaultLifetime {{ i.default_lifetime }};
-{% endif %}
-{% if i.link_mtu %}
- AdvLinkMTU {{ i.link_mtu }};
-{% endif %}
- AdvOtherConfigFlag {{ i.other_config_flag }};
- AdvRetransTimer {{ i.retrans_timer }};
- AdvCurHopLimit {{ i.hop_limit }};
-{% for p in i.prefixes %}
- prefix {{ p.prefix }} {
- AdvAutonomous {{ p.autonomous_flag }};
- AdvValidLifetime {{ p.valid_lifetime }};
- AdvOnLink {{ p.on_link }};
- AdvPreferredLifetime {{ p.preferred_lifetime }};
- };
-{% endfor %}
-{% if i.name_server %}
- RDNSS {{ i.name_server | join(" ") }} {
- };
-{% endif %}
-};
-{% endfor -%}
-"""
-
default_config_data = {
'interfaces': []
}
@@ -175,7 +137,12 @@ def generate(rtradv):
if not rtradv['interfaces']:
return None
- tmpl = jinja2.Template(config_tmpl, trim_blocks=True)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'router-advert')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader, trim_blocks=True)
+
+ tmpl = env.get_template('radvd.conf.tmpl')
config_text = tmpl.render(rtradv)
with open(config_file, 'w') as f:
f.write(config_text)
@@ -189,13 +156,13 @@ def generate(rtradv):
def apply(rtradv):
if not rtradv['interfaces']:
# bail out early - looks like removal from running config
- os.system('sudo systemctl stop radvd.service')
+ os.system('systemctl stop radvd.service')
if os.path.exists(config_file):
os.unlink(config_file)
return None
- os.system('sudo systemctl restart radvd.service')
+ os.system('systemctl restart radvd.service')
return None
if __name__ == '__main__':
@@ -206,4 +173,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py
index 34832aac1..ed8c1d7e1 100755
--- a/src/conf_mode/snmp.py
+++ b/src/conf_mode/snmp.py
@@ -15,16 +15,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import jinja2
from binascii import hexlify
from time import sleep
from stat import S_IRWXU, S_IXGRP, S_IXOTH, S_IROTH, S_IRGRP
from sys import exit
+from jinja2 import FileSystemLoader, Environment
+from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos.validate import is_ipv4, is_addr_assigned
from vyos.version import get_version_data
-from vyos.config import Config
from vyos import ConfigError
config_file_client = r'/etc/snmp/snmp.conf'
@@ -42,168 +43,6 @@ OIDs = {
'none': '.1.3.6.1.6.3.10.1.2.1'
}
-# SNMP template (/etc/snmp/snmp.conf) - be careful if you edit the template.
-client_config_tmpl = """
-### Autogenerated by snmp.py ###
-{% if trap_source -%}
-clientaddr {{ trap_source }}
-{% endif %}
-
-"""
-
-# SNMP template (/usr/share/snmp/snmpd.conf) - be careful if you edit the template.
-access_config_tmpl = """
-### Autogenerated by snmp.py ###
-{%- for u in v3_users %}
-{{ u.mode }}user {{ u.name }}
-{%- endfor %}
-
-rwuser {{ vyos_user }}
-
-"""
-
-# SNMP template (/var/lib/snmp/snmpd.conf) - be careful if you edit the template.
-user_config_tmpl = """
-### Autogenerated by snmp.py ###
-# user
-{%- for u in v3_users %}
-{%- if u.authOID == 'none' %}
-createUser {{ u.name }}
-{%- elif u.authPassword %}
-createUser {{ u.name }} {{ u.authProtocol | upper }} "{{ u.authPassword }}" {{ u.privProtocol | upper }} {{ u.privPassword }}
-{%- else %}
-usmUser 1 3 {{ v3_engineid }} "{{ u.name }}" "{{ u.name }}" NULL {{ u.authOID }} {{ u.authMasterKey }} {{ u.privOID }} {{ u.privMasterKey }} 0x
-{%- endif %}
-{%- endfor %}
-
-createUser {{ vyos_user }} MD5 "{{ vyos_user_pass }}" DES
-{%- if v3_engineid %}
-oldEngineID {{ v3_engineid }}
-{%- endif %}
-"""
-
-# SNMP template (/etc/snmp/snmpd.conf) - be careful if you edit the template.
-daemon_config_tmpl = """
-### Autogenerated by snmp.py ###
-
-# non configurable defaults
-sysObjectID 1.3.6.1.4.1.44641
-sysServices 14
-master agentx
-agentXPerms 0777 0777
-pass .1.3.6.1.2.1.31.1.1.1.18 /opt/vyatta/sbin/if-mib-alias
-smuxpeer .1.3.6.1.2.1.83
-smuxpeer .1.3.6.1.2.1.157
-smuxsocket localhost
-
-# linkUp/Down configure the Event MIB tables to monitor
-# the ifTable for network interfaces being taken up or down
-# for making internal queries to retrieve any necessary information
-iquerySecName {{ vyos_user }}
-
-# Modified from the default linkUpDownNotification
-# to include more OIDs and poll more frequently
-notificationEvent linkUpTrap linkUp ifIndex ifDescr ifType ifAdminStatus ifOperStatus
-notificationEvent linkDownTrap linkDown ifIndex ifDescr ifType ifAdminStatus ifOperStatus
-monitor -r 10 -e linkUpTrap "Generate linkUp" ifOperStatus != 2
-monitor -r 10 -e linkDownTrap "Generate linkDown" ifOperStatus == 2
-
-########################
-# configurable section #
-########################
-
-# Default system description is VyOS version
-sysDescr VyOS {{ version }}
-
-{% if description %}
-# Description
-SysDescr {{ description }}
-{%- endif %}
-
-# Listen
-agentaddress unix:/run/snmpd.socket{% if listen_on %}{% for li in listen_on %},{{ li }}{% endfor %}{% else %},udp:161{% if ipv6_enabled %},udp6:161{% endif %}{% endif %}
-
-# SNMP communities
-{%- for c in communities %}
-
-{%- if c.network_v4 %}
-{%- for network in c.network_v4 %}
-{{ c.authorization }}community {{ c.name }} {{ network }}
-{%- endfor %}
-{%- elif not c.has_source %}
-{{ c.authorization }}community {{ c.name }}
-{%- endif %}
-
-{%- if c.network_v6 %}
-{%- for network in c.network_v6 %}
-{{ c.authorization }}community6 {{ c.name }} {{ network }}
-{%- endfor %}
-{%- elif not c.has_source %}
-{{ c.authorization }}community6 {{ c.name }}
-{%- endif %}
-
-{%- endfor %}
-
-{% if contact %}
-# system contact information
-SysContact {{ contact }}
-{%- endif %}
-
-{% if location %}
-# system location information
-SysLocation {{ location }}
-{%- endif %}
-
-{% if smux_peers -%}
-# additional smux peers
-{%- for sp in smux_peers %}
-smuxpeer {{ sp }}
-{%- endfor %}
-{%- endif %}
-
-{% if trap_targets -%}
-# if there is a problem - tell someone!
-{%- for t in trap_targets %}
-trap2sink {{ t.target }}{% if t.port -%}:{{ t.port }}{% endif %} {{ t.community }}
-{%- endfor %}
-{%- endif %}
-
-{%- if v3_enabled %}
-#
-# SNMPv3 stuff goes here
-#
-# views
-{%- for v in v3_views %}
-{%- for oid in v.oids %}
-view {{ v.name }} included .{{ oid.oid }}
-{%- endfor %}
-{%- endfor %}
-
-# access
-# context sec.model sec.level match read write notif
-{%- for g in v3_groups %}
-access {{ g.name }} "" usm {{ g.seclevel }} exact {{ g.view }} {% if g.mode == 'ro' %}none{% else %}{{ g.view }}{% endif %} none
-{%- endfor %}
-
-# trap-target
-{%- for t in v3_traps %}
-trapsess -v 3 {{ '-Ci' if t.type == 'inform' }} -e {{ v3_engineid }} -u {{ t.secName }} -l {{ t.secLevel }} -a {{ t.authProtocol }} {% if t.authPassword %}-A {{ t.authPassword }}{% elif t.authMasterKey %}-3m {{ t.authMasterKey }}{% endif %} -x {{ t.privProtocol }} {% if t.privPassword %}-X {{ t.privPassword }}{% elif t.privMasterKey %}-3M {{ t.privMasterKey }}{% endif %} {{ t.ipProto }}:{{ t.ipAddr }}:{{ t.ipPort }}
-{%- endfor %}
-
-# group
-{%- for u in v3_users %}
-group {{ u.group }} usm {{ u.name }}
-{% endfor %}
-{%- endif %}
-
-{% if script_ext %}
-# extension scripts
-{%- for ext in script_ext|sort(attribute='name') %}
-extend {{ ext.name }} {{ ext.script }}
-{%- endfor %}
-{% endif %}
-"""
-
default_config_data = {
'listen_on': [],
'listen_address': [],
@@ -669,34 +508,39 @@ def generate(snmp):
# As we are manipulating the snmpd user database we have to stop it first!
# This is even save if service is going to be removed
os.system("systemctl stop snmpd.service")
- rmfile(config_file_client)
- rmfile(config_file_daemon)
- rmfile(config_file_access)
- rmfile(config_file_user)
+ config_files = [config_file_client, config_file_daemon, config_file_access,
+ config_file_user]
+ for file in config_files:
+ rmfile(file)
if snmp is None:
return None
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'snmp')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
# Write client config file
- tmpl = jinja2.Template(client_config_tmpl)
+ tmpl = env.get_template('etc.snmp.conf.tmpl')
config_text = tmpl.render(snmp)
with open(config_file_client, 'w') as f:
f.write(config_text)
# Write server config file
- tmpl = jinja2.Template(daemon_config_tmpl)
+ tmpl = env.get_template('etc.snmpd.conf.tmpl')
config_text = tmpl.render(snmp)
with open(config_file_daemon, 'w') as f:
f.write(config_text)
# Write access rights config file
- tmpl = jinja2.Template(access_config_tmpl)
+ tmpl = env.get_template('usr.snmpd.conf.tmpl')
config_text = tmpl.render(snmp)
with open(config_file_access, 'w') as f:
f.write(config_text)
# Write access rights config file
- tmpl = jinja2.Template(user_config_tmpl)
+ tmpl = env.get_template('var.snmpd.conf.tmpl')
config_text = tmpl.render(snmp)
with open(config_file_user, 'w') as f:
f.write(config_text)
diff --git a/src/conf_mode/ssh.py b/src/conf_mode/ssh.py
index 9fe22bfee..014045796 100755
--- a/src/conf_mode/ssh.py
+++ b/src/conf_mode/ssh.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,149 +13,17 @@
#
# 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 sys
import os
-
-import jinja2
+from jinja2 import FileSystemLoader, Environment
+from sys import exit
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
config_file = r'/etc/ssh/sshd_config'
-# Please be careful if you edit the template.
-config_tmpl = """
-
-### Autogenerated by ssh.py ###
-
-# Non-configurable defaults
-Protocol 2
-HostKey /etc/ssh/ssh_host_rsa_key
-HostKey /etc/ssh/ssh_host_dsa_key
-HostKey /etc/ssh/ssh_host_ecdsa_key
-HostKey /etc/ssh/ssh_host_ed25519_key
-SyslogFacility AUTH
-LoginGraceTime 120
-StrictModes yes
-PubkeyAuthentication yes
-IgnoreRhosts yes
-HostbasedAuthentication no
-PermitEmptyPasswords no
-ChallengeResponseAuthentication no
-X11Forwarding yes
-X11DisplayOffset 10
-PrintMotd no
-PrintLastLog yes
-TCPKeepAlive yes
-Banner /etc/issue.net
-Subsystem sftp /usr/lib/openssh/sftp-server
-UsePAM yes
-HostKey /etc/ssh/ssh_host_rsa_key
-
-# Specifies whether sshd should look up the remote host name,
-# and to check that the resolved host name for the remote IP
-# address maps back to the very same IP address.
-UseDNS {{ host_validation }}
-
-# Specifies the port number that sshd listens on. The default is 22.
-# Multiple options of this type are permitted.
-{% if mport|length != 0 %}
-{% for p in mport %}
-Port {{ p }}
-{% endfor %}
-{% else %}
-Port {{ port }}
-{% endif %}
-
-# Gives the verbosity level that is used when logging messages from sshd
-LogLevel {{ log_level }}
-
-# Specifies whether root can log in using ssh
-PermitRootLogin no
-
-# Specifies whether password authentication is allowed
-PasswordAuthentication {{ password_authentication }}
-
-{% if listen_on %}
-# Specifies the local addresses sshd should listen on
-{% for a in listen_on %}
-ListenAddress {{ a }}
-{% endfor %}
-{{ "\n" }}
-{% endif %}
-
-{%- if ciphers %}
-# Specifies the ciphers allowed. Multiple ciphers must be comma-separated.
-#
-# NOTE: As of now, there is no 'multi' node for 'ciphers', thus we have only one :/
-Ciphers {{ ciphers | join(",") }}
-{{ "\n" }}
-{% endif %}
-
-{%- if mac %}
-# Specifies the available MAC (message authentication code) algorithms. The MAC
-# algorithm is used for data integrity protection. Multiple algorithms must be
-# comma-separated.
-#
-# NOTE: As of now, there is no 'multi' node for 'mac', thus we have only one :/
-MACs {{ mac | join(",") }}
-{{ "\n" }}
-{% endif %}
-
-{%- if key_exchange %}
-# Specifies the available KEX (Key Exchange) algorithms. Multiple algorithms must
-# be comma-separated.
-#
-# NOTE: As of now, there is no 'multi' node for 'key-exchange', thus we have only one :/
-KexAlgorithms {{ key_exchange | join(",") }}
-{{ "\n" }}
-{% endif %}
-
-{%- if allow_users %}
-# This keyword can be followed by a list of user name patterns, separated by spaces.
-# If specified, login is allowed only for user names that match one of the patterns.
-# Only user names are valid, a numerical user ID is not recognized.
-AllowUsers {{ allow_users | join(" ") }}
-{{ "\n" }}
-{% endif %}
-
-{%- if allow_groups %}
-# This keyword can be followed by a list of group name patterns, separated by spaces.
-# If specified, login is allowed only for users whose primary group or supplementary
-# group list matches one of the patterns. Only group names are valid, a numerical group
-# ID is not recognized.
-AllowGroups {{ allow_groups | join(" ") }}
-{{ "\n" }}
-{% endif %}
-
-{%- if deny_users %}
-# This keyword can be followed by a list of user name patterns, separated by spaces.
-# Login is disallowed for user names that match one of the patterns. Only user names
-# are valid, a numerical user ID is not recognized.
-DenyUsers {{ deny_users | join(" ") }}
-{{ "\n" }}
-{% endif %}
-
-{%- if deny_groups %}
-# This keyword can be followed by a list of group name patterns, separated by spaces.
-# Login is disallowed for users whose primary group or supplementary group list matches
-# one of the patterns. Only group names are valid, a numerical group ID is not recognized.
-DenyGroups {{ deny_groups | join(" ") }}
-{{ "\n" }}
-{% endif %}
-
-{%- if client_keepalive %}
-# Sets a timeout interval in seconds after which if no data has been received from the client,
-# sshd will send a message through the encrypted channel to request a response from the client.
-# The default is 0, indicating that these messages will not be sent to the client.
-# This option applies to protocol version 2 only.
-ClientAliveInterval {{ client_keepalive }}
-{% endif %}
-"""
-
default_config_data = {
'port' : '22',
'log_level': 'INFO',
@@ -250,7 +118,12 @@ def generate(ssh):
if ssh is None:
return None
- tmpl = jinja2.Template(config_tmpl, trim_blocks=True)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'ssh')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader, trim_blocks=True)
+
+ tmpl = env.get_template('sshd_config.tmpl')
config_text = tmpl.render(ssh)
with open(config_file, 'w') as f:
f.write(config_text)
@@ -275,4 +148,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py
index 959e86e5b..7acb0a9a2 100755
--- a/src/conf_mode/system-login.py
+++ b/src/conf_mode/system-login.py
@@ -14,36 +14,21 @@
# 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 sys
import os
-import jinja2
+from jinja2 import FileSystemLoader, Environment
+from psutil import users
from pwd import getpwall, getpwnam
from stat import S_IRUSR, S_IWUSR, S_IRWXU, S_IRGRP, S_IXGRP
from subprocess import Popen, PIPE, STDOUT
-from psutil import users
+from sys import exit
from vyos.config import Config
from vyos.configdict import list_diff
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
radius_config_file = "/etc/pam_radius_auth.conf"
-radius_config_tmpl = """
-# Automatically generated by VyOS
-# RADIUS configuration file
-{%- if radius_server %}
-# server[:port] shared_secret timeout (s) source_ip
-{% for s in radius_server %}
-{%- if not s.disabled -%}
-{{ s.address }}:{{ s.port }} {{ s.key }} {{ s.timeout }} {% if radius_source_address -%}{{ radius_source_address }}{% endif %}
-{% endif %}
-{%- endfor %}
-
-priv-lvl 15
-mapped_priv_user radius_priv_user
-{% endif %}
-
-"""
default_config_data = {
'deleted': False,
@@ -229,7 +214,12 @@ def generate(login):
os.system("vyos_libexec_dir=/usr/libexec/vyos /opt/vyatta/sbin/my_set system login user '{}' authentication encrypted-password '{}' >/dev/null".format(user['name'], user['password_encrypted']))
if len(login['radius_server']) > 0:
- tmpl = jinja2.Template(radius_config_tmpl)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'system-login')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
+ tmpl = env.get_template('pam_radius_auth.conf.tmpl')
config_text = tmpl.render(login)
with open(radius_config_file, 'w') as f:
f.write(config_text)
@@ -364,4 +354,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/system-syslog.py b/src/conf_mode/system-syslog.py
index 2d47cc061..8c0a6629c 100755
--- a/src/conf_mode/system-syslog.py
+++ b/src/conf_mode/system-syslog.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,85 +13,18 @@
#
# 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 sys
import os
import re
-import subprocess
-import jinja2
+
+from jinja2 import FileSystemLoader, Environment
+from sys import exit
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
+from vyos.util import subprocess_cmd
from vyos import ConfigError
-# config templates
-
-# /etc/rsyslog.d/vyos-rsyslog.conf ###
-configs = '''
-## generated by syslog.py ##
-## file based logging
-{% if files['global']['marker'] -%}
-$ModLoad immark
-{% if files['global']['marker-interval'] %}
-$MarkMessagePeriod {{files['global']['marker-interval']}}
-{% endif %}
-{% endif -%}
-{% if files['global']['preserver_fqdn'] -%}
-$PreserveFQDN on
-{% endif -%}
-{% for file in files %}
-$outchannel {{file}},{{files[file]['log-file']}},{{files[file]['max-size']}},{{files[file]['action-on-max-size']}}
-{{files[file]['selectors']}} :omfile:${{file}}
-{% endfor %}
-{% if console %}
-## console logging
-{% for con in console %}
-{{console[con]['selectors']}} /dev/console
-{% endfor %}
-{% endif %}
-{% if hosts %}
-## remote logging
-{% for host in hosts %}
-{% if hosts[host]['proto'] == 'tcp' %}
-{% if hosts[host]['port'] %}
-{{hosts[host]['selectors']}} @@{{host}}:{{hosts[host]['port']}}
-{% else %}
-{{hosts[host]['selectors']}} @@{{host}}
-{% endif %}
-{% else %}
-{% if hosts[host]['port'] %}
-{{hosts[host]['selectors']}} @{{host}}:{{hosts[host]['port']}}
-{% else %}
-{{hosts[host]['selectors']}} @{{host}}
-{% endif %}
-{% endif %}
-{% endfor %}
-{% endif %}
-{% if user %}
-{% for u in user %}
-{{user[u]['selectors']}} :omusrmsg:{{u}}
-{% endfor %}
-{% endif %}
-'''
-
-logrotate_configs = '''
-{% for file in files %}
-{{files[file]['log-file']}} {
- missingok
- notifempty
- create
- rotate {{files[file]['max-files']}}
- size={{files[file]['max-size']//1024}}k
- postrotate
- invoke-rc.d rsyslog rotate > /dev/null
- endscript
-}
-{% endfor %}
-'''
-# config templates end
-
-
def get_config():
c = Config()
if not c.exists('system syslog'):
@@ -259,14 +192,19 @@ def generate(c):
if c == None:
return None
- tmpl = jinja2.Template(configs, trim_blocks=True)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'syslog')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader, trim_blocks=True)
+
+ tmpl = env.get_template('rsyslog.conf.tmpl')
config_text = tmpl.render(c)
with open('/etc/rsyslog.d/vyos-rsyslog.conf', 'w') as f:
f.write(config_text)
# eventually write for each file its own logrotate file, since size is
# defined it shouldn't matter
- tmpl = jinja2.Template(logrotate_configs, trim_blocks=True)
+ tmpl = env.get_template('logrotate.tmpl')
config_text = tmpl.render(c)
with open('/etc/logrotate.d/vyos-rsyslog', 'w') as f:
f.write(config_text)
@@ -315,10 +253,10 @@ def verify(c):
def apply(c):
if not c:
- subprocess.call(['sudo', 'systemctl', 'stop', 'syslog'])
- return 0
+ subprocess_cmd('systemctl stop syslog')
+ return None
- subprocess.call(['sudo', 'systemctl', 'restart', 'syslog'])
+ subprocess_cmd('systemctl restart syslog')
if __name__ == '__main__':
try:
@@ -328,4 +266,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/system-wifi-regdom.py b/src/conf_mode/system-wifi-regdom.py
index 01dc92a20..943c42274 100755
--- a/src/conf_mode/system-wifi-regdom.py
+++ b/src/conf_mode/system-wifi-regdom.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# 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
@@ -15,52 +15,34 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import jinja2
from copy import deepcopy
from sys import exit
+from jinja2 import FileSystemLoader, Environment
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
config_80211_file='/etc/modprobe.d/cfg80211.conf'
config_crda_file='/etc/default/crda'
-# Please be careful if you edit the template.
-config_80211_tmpl = """
-{%- if regdom -%}
-options cfg80211 ieee80211_regdom={{ regdom }}
-{% endif %}
-"""
-
-# Please be careful if you edit the template.
-config_crda_tmpl = """
-{%- if regdom -%}
-REGDOMAIN={{ regdom }}
-{% endif %}
-"""
-
default_config_data = {
'regdom' : '',
'deleted' : False
}
-
def get_config():
regdom = deepcopy(default_config_data)
conf = Config()
-
- # set new configuration level
- conf.set_level('system')
+ base = ['system', 'wifi-regulatory-domain']
# Check if interface has been removed
- if not conf.exists('wifi-regulatory-domain'):
+ if not conf.exists(base):
regdom['deleted'] = True
return regdom
-
- # retrieve configured regulatory domain
- if conf.exists('wifi-regulatory-domain'):
- regdom['regdom'] = conf.return_value('wifi-regulatory-domain')
+ else:
+ regdom['regdom'] = conf.return_value(base)
return regdom
@@ -85,12 +67,17 @@ def generate(regdom):
return None
- tmpl = jinja2.Template(config_80211_tmpl)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'wifi')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
+ tmpl = env.get_template('cfg80211.conf.tmpl')
config_text = tmpl.render(regdom)
with open(config_80211_file, 'w') as f:
f.write(config_text)
- tmpl = jinja2.Template(config_crda_tmpl)
+ tmpl = env.get_template('crda.tmpl')
config_text = tmpl.render(regdom)
with open(config_crda_file, 'w') as f:
f.write(config_text)
diff --git a/src/conf_mode/tftp_server.py b/src/conf_mode/tftp_server.py
index ff7cad0c9..fe2da8455 100755
--- a/src/conf_mode/tftp_server.py
+++ b/src/conf_mode/tftp_server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,32 +13,23 @@
#
# 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 sys
import os
import stat
import pwd
-import copy
-import glob
-import jinja2
-import vyos.validate
+from copy import deepcopy
+from glob import glob
+from jinja2 import FileSystemLoader, Environment
+from sys import exit
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
+from vyos.validate import is_ipv4, is_addr_assigned
from vyos import ConfigError
config_file = r'/etc/default/tftpd'
-# Please be careful if you edit the template.
-config_tmpl = """
-### 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 }}"
-
-
-"""
-
default_config_data = {
'directory': '',
'allow_upload': False,
@@ -47,23 +38,25 @@ default_config_data = {
}
def get_config():
- tftpd = copy.deepcopy(default_config_data)
+ tftpd = deepcopy(default_config_data)
conf = Config()
- if not conf.exists('service tftp-server'):
+ base = ['service', 'tftp-server']
+ if not conf.exists(base):
return None
else:
- conf.set_level('service tftp-server')
+ conf.set_level(base)
- if conf.exists('directory'):
- tftpd['directory'] = conf.return_value('directory')
+ if conf.exists(['directory']):
+ tftpd['directory'] = conf.return_value(['directory'])
- if conf.exists('allow-upload'):
+ if conf.exists(['allow-upload']):
tftpd['allow_upload'] = True
- if conf.exists('port'):
- tftpd['port'] = conf.return_value('port')
+ if conf.exists(['port']):
+ tftpd['port'] = conf.return_value(['port'])
- tftpd['listen'] = conf.return_values('listen-address')
+ if conf.exists(['listen-address']):
+ tftpd['listen'] = conf.return_values(['listen-address'])
return tftpd
@@ -80,7 +73,7 @@ def verify(tftpd):
raise ConfigError('TFTP server listen address must be configured!')
for addr in tftpd['listen']:
- if not vyos.validate.is_addr_assigned(addr):
+ if not is_addr_assigned(addr):
print('WARNING: TFTP server listen address {0} not assigned to any interface!'.format(addr))
return None
@@ -88,22 +81,27 @@ def verify(tftpd):
def generate(tftpd):
# cleanup any available configuration file
# files will be recreated on demand
- for i in glob.glob(config_file + '*'):
+ for i in glob(config_file + '*'):
os.unlink(i)
# bail out early - looks like removal from running config
if tftpd is None:
return None
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'tftp-server')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
idx = 0
for listen in tftpd['listen']:
- config = copy.deepcopy(tftpd)
- if vyos.validate.is_ipv4(listen):
+ config = deepcopy(tftpd)
+ if is_ipv4(listen):
config['listen'] = [listen + ":" + tftpd['port'] + " -4"]
else:
config['listen'] = ["[" + listen + "]" + tftpd['port'] + " -6"]
- tmpl = jinja2.Template(config_tmpl)
+ tmpl = env.get_template('default.tmpl')
config_text = tmpl.render(config)
file = config_file + str(idx)
with open(file, 'w') as f:
@@ -115,7 +113,7 @@ def generate(tftpd):
def apply(tftpd):
# stop all services first - then we will decide
- os.system('sudo systemctl stop tftpd@{0..20}')
+ os.system('systemctl stop tftpd@{0..20}')
# bail out early - e.g. service deletion
if tftpd is None:
@@ -140,7 +138,7 @@ def apply(tftpd):
idx = 0
for listen in tftpd['listen']:
- os.system('sudo systemctl restart tftpd@{0}.service'.format(idx))
+ os.system('systemctl restart tftpd@{0}.service'.format(idx))
idx = idx + 1
return None
@@ -153,4 +151,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/vpn-pptp.py b/src/conf_mode/vpn-pptp.py
index 355adf715..b1204a505 100755
--- a/src/conf_mode/vpn-pptp.py
+++ b/src/conf_mode/vpn-pptp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,19 +13,18 @@
#
# 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 sys
import os
import re
import subprocess
-import jinja2
-import socket
-import time
-import syslog as sl
+
+from jinja2 import FileSystemLoader, Environment
+from socket import socket, AF_INET, SOCK_STREAM
+from sys import exit
+from time import sleep
from vyos.config import Config
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
pidfile = r'/var/run/accel_pptp.pid'
@@ -36,117 +35,16 @@ pptp_conf = pptp_cnf_dir + '/pptp.config'
# config path creation
if not os.path.exists(pptp_cnf_dir):
os.makedirs(pptp_cnf_dir)
- sl.syslog(sl.LOG_NOTICE, pptp_cnf_dir + " created")
-
-pptp_config = '''
-### generated by accel_pptp.py ###
-[modules]
-log_syslog
-pptp
-ippool
-chap-secrets
-{% if authentication['auth_proto'] %}
-{{ authentication['auth_proto'] }}
-{% else %}
-auth_mschap_v2
-{% endif %}
-{% if authentication['mode'] == 'radius' %}
-radius
-{% endif -%}
-
-[core]
-thread-count={{thread_cnt}}
-
-[log]
-syslog=accel-pptp,daemon
-copy=1
-level=5
-
-{% if dns %}
-[dns]
-{% if dns[0] %}
-dns1={{dns[0]}}
-{% endif %}
-{% if dns[1] %}
-dns2={{dns[1]}}
-{% endif %}
-{% endif %}
-
-{% if wins %}
-[wins]
-{% if wins[0] %}
-wins1={{wins[0]}}
-{% endif %}
-{% if wins[1] %}
-wins2={{wins[1]}}
-{% endif %}
-{% endif %}
-
-[pptp]
-ifname=pptp%d
-{% if outside_addr %}
-bind={{outside_addr}}
-{% endif %}
-verbose=1
-ppp-max-mtu={{mtu}}
-mppe={{authentication['mppe']}}
-echo-interval=10
-echo-failure=3
-
-
-[client-ip-range]
-0.0.0.0/0
-
-[ip-pool]
-tunnel={{client_ip_pool}}
-gw-ip-address={{gw_ip}}
-
-{% if authentication['mode'] == 'local' %}
-[chap-secrets]
-chap-secrets=/etc/accel-ppp/pptp/chap-secrets
-{% endif %}
-
-[ppp]
-verbose=5
-check-ip=1
-single-session=replace
-
-{% if authentication['mode'] == 'radius' %}
-[radius]
-{% for rsrv in authentication['radiussrv']: %}
-server={{rsrv}},{{authentication['radiussrv'][rsrv]['secret']}},\
-req-limit={{authentication['radiussrv'][rsrv]['req-limit']}},\
-fail-time={{authentication['radiussrv'][rsrv]['fail-time']}}
-{% endfor %}
-timeout=30
-acct-timeout=30
-max-try=3
-{%endif %}
-
-[cli]
-tcp=127.0.0.1:2003
-'''
-
-# pptp chap secrets
-chap_secrets_conf = '''
-# username server password acceptable local IP addresses
-{% for user in authentication['local-users'] %}
-{% if authentication['local-users'][user]['state'] == 'enabled' %}
-{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}
-{% endif %}
-{% endfor %}
-'''
-
def _chk_con():
cnt = 0
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s = socket(AF_INET, SOCK_STREAM)
while True:
try:
s.connect(("127.0.0.1", 2003))
break
except ConnectionRefusedError:
- time.sleep(0.5)
+ sleep(0.5)
cnt += 1
if cnt == 100:
raise("failed to start pptp server")
@@ -154,16 +52,6 @@ def _chk_con():
# chap_secrets file if auth mode local
-
-def _write_chap_secrets(c):
- tmpl = jinja2.Template(chap_secrets_conf, trim_blocks=True)
- chap_secrets_txt = tmpl.render(c)
- old_umask = os.umask(0o077)
- open(chap_secrets, 'w').write(chap_secrets_txt)
- os.umask(old_umask)
- sl.syslog(sl.LOG_NOTICE, chap_secrets + ' written')
-
-
def _accel_cmd(cmd=''):
if not cmd:
return None
@@ -326,6 +214,11 @@ def generate(c):
if c == None:
return None
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'pptp')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader, trim_blocks=True)
+
# accel-cmd reload doesn't work so any change results in a restart of the daemon
try:
if os.cpu_count() == 1:
@@ -338,12 +231,18 @@ def generate(c):
else:
c['thread_cnt'] = int(os.cpu_count()/2)
- tmpl = jinja2.Template(pptp_config, trim_blocks=True)
+ tmpl = env.get_template('pptp.config.tmpl')
config_text = tmpl.render(c)
- open(pptp_conf, 'w').write(config_text)
+ with open(pptp_conf, 'w') as f:
+ f.write(config_text)
if c['authentication']['local-users']:
- _write_chap_secrets(c)
+ tmpl = env.get_template('chap-secrets.tmpl')
+ chap_secrets_txt = tmpl.render(c)
+ old_umask = os.umask(0o077)
+ with open(chap_secrets, 'w') as f:
+ f.write(chap_secrets_txt)
+ os.umask(old_umask)
return c
@@ -366,8 +265,6 @@ def apply(c):
else:
# if gw ip changes, only restart doesn't work
_accel_cmd('restart')
- sl.syslog(sl.LOG_NOTICE, "reloading config via daemon restart")
-
if __name__ == '__main__':
try:
@@ -377,4 +274,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print(e)
- sys.exit(1)
+ exit(1)
diff --git a/src/conf_mode/vpn_sstp.py b/src/conf_mode/vpn_sstp.py
index 070437443..66b1822df 100755
--- a/src/conf_mode/vpn_sstp.py
+++ b/src/conf_mode/vpn_sstp.py
@@ -15,17 +15,18 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import jinja2
from time import sleep
from sys import exit
-from subprocess import Popen, PIPE, check_output
+from subprocess import check_output
from socket import socket, AF_INET, SOCK_STREAM
from copy import deepcopy
from stat import S_IRUSR, S_IWUSR, S_IRGRP
+from jinja2 import FileSystemLoader, Environment
from vyos.config import Config
-from vyos.util import process_running
+from vyos.defaults import directories as vyos_data_dir
+from vyos.util import process_running, subprocess_cmd
from vyos import ConfigError
pidfile = r'/var/run/accel_sstp.pid'
@@ -37,140 +38,6 @@ sstp_conf = sstp_cnf_dir + '/sstp.config'
if not os.path.exists(sstp_cnf_dir):
os.makedirs(sstp_cnf_dir)
-sstp_config = """### generated by vpn_sstp.py ###
-[modules]
-log_syslog
-sstp
-shaper
-{% if auth_mode == 'local' %}
-chap-secrets
-{% elif auth_mode == 'radius' %}
-radius
-{% endif -%}
-ippool
-
-{% for proto in auth_proto %}
-{{proto}}
-{% endfor %}
-
-[core]
-thread-count={{thread_cnt}}
-
-[common]
-single-session=replace
-
-[log]
-syslog=accel-sstp,daemon
-copy=1
-level=5
-
-[client-ip-range]
-disable
-
-[sstp]
-verbose=1
-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 %}
-
-{% if dnsv4 %}
-[dns]
-{% for dns in dnsv4 -%}
-dns{{ loop.index }}={{ dns }}
-{% endfor -%}
-{% endif %}
-
-{% if auth_mode == 'local' %}
-[chap-secrets]
-chap-secrets=/etc/accel-ppp/sstp/chap-secrets
-{% elif auth_mode == 'radius' %}
-[radius]
-verbose=1
-{% for r in radius_server %}
-server={{ r.server }},{{ r.key }},auth-port={{ r.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 %}
-
-[ppp]
-verbose=1
-check-ip=1
-{% if mtu %}
-mtu={{ mtu }}
-{% endif -%}
-
-{% 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 radius_shaper_attr %}
-[shaper]
-verbose=1
-attr={{ radius_shaper_attr }}
-{% if radius_shaper_vendor %}
-vendor={{ radius_shaper_vendor }}
-{% endif -%}
-{% endif %}
-
-[cli]
-tcp=127.0.0.1:2005
-
-"""
-
-# sstp chap secrets
-chap_secrets_conf = """
-# username server password acceptable local IP addresses shaper
-{% for user in local_users %}
-{% if user.state == 'enabled' %}
-{% if user.upload and user.download %}
-{{ "%-12s" | format(user.name) }} * {{ "%-16s" | format(user.password) }} {{ "%-16s" | format(user.ip) }} {{ user.download }} / {{ user.upload }}
-{% else %}
-{{ "%-12s" | format(user.name) }} * {{ "%-16s" | format(user.password) }} {{ "%-16s" | format(user.ip) }}
-{% endif %}
-{% endif %}
-{% endfor %}
-"""
-
-def subprocess_cmd(command):
- p = Popen(command, stdout=PIPE, shell=True)
- p.communicate()
-
def chk_con():
cnt = 0
s = socket(AF_INET, SOCK_STREAM)
@@ -469,14 +336,19 @@ def generate(sstp):
if sstp is None:
return None
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'sstp')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader, trim_blocks=True)
+
# accel-cmd reload doesn't work so any change results in a restart of the daemon
- tmpl = jinja2.Template(sstp_config, trim_blocks=True)
+ tmpl = env.get_template('sstp.config.tmpl')
config_text = tmpl.render(sstp)
with open(sstp_conf, 'w') as f:
f.write(config_text)
if sstp['local_users']:
- tmpl = jinja2.Template(chap_secrets_conf, trim_blocks=True)
+ tmpl = env.get_template('chap-secrets.tmpl')
config_text = tmpl.render(sstp)
with open(chap_secrets, 'w') as f:
f.write(config_text)
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index a74b79317..8cf4b72ae 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -15,35 +15,24 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import jinja2
from sys import exit
from copy import deepcopy
+from jinja2 import FileSystemLoader, Environment
from json import loads
from subprocess import check_output, CalledProcessError
from vyos.config import Config
from vyos.configdict import list_diff
+from vyos.defaults import directories as vyos_data_dir
from vyos.ifconfig import Interface
+from vyos.util import read_file
from vyos import ConfigError
config_file = r'/etc/iproute2/rt_tables.d/vyos-vrf.conf'
-# Please be careful if you edit the template.
-config_tmpl = """
-### Autogenerated by vrf.py ###
-#
-# Routing table ID to name mapping reference
-
-# id vrf name comment
-{% for vrf in vrf_add -%}
-{{ "%-10s" | format(vrf.table) }} {{ "%-16s" | format(vrf.name) }} # {{ vrf.description }}
-{% endfor -%}
-
-"""
-
default_config_data = {
- 'bind_to_all': 0,
+ 'bind_to_all': '0',
'deleted': False,
'vrf_add': [],
'vrf_existing': [],
@@ -103,7 +92,7 @@ def get_config():
# Should services be allowed to bind to all VRFs?
if conf.exists(['bind-to-all']):
- vrf_config['bind_to_all'] = 1
+ vrf_config['bind_to_all'] = '1'
# Determine vrf interfaces (currently effective) - to determine which
# vrf interface is no longer present and needs to be removed
@@ -193,7 +182,12 @@ def verify(vrf_config):
return None
def generate(vrf_config):
- tmpl = jinja2.Template(config_tmpl)
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'vrf')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
+ tmpl = env.get_template('vrf.conf.tmpl')
config_text = tmpl.render(vrf_config)
with open(config_file, 'w') as f:
f.write(config_text)
@@ -210,12 +204,15 @@ def apply(vrf_config):
# set the default VRF global behaviour
bind_all = vrf_config['bind_to_all']
- _cmd(f'sysctl -wq net.ipv4.tcp_l3mdev_accept={bind_all}')
- _cmd(f'sysctl -wq net.ipv4.udp_l3mdev_accept={bind_all}')
+ if read_file('/proc/sys/net/ipv4/tcp_l3mdev_accept') != bind_all:
+ _cmd(f'sysctl -wq net.ipv4.tcp_l3mdev_accept={bind_all}')
+ _cmd(f'sysctl -wq net.ipv4.udp_l3mdev_accept={bind_all}')
for vrf in vrf_config['vrf_remove']:
name = vrf['name']
if os.path.isdir(f'/sys/class/net/{name}'):
+ _cmd(f'sudo ip -4 route del vrf {name} unreachable default metric 4278198272')
+ _cmd(f'sudo ip -6 route del vrf {name} unreachable default metric 4278198272')
_cmd(f'ip link delete dev {name}')
for vrf in vrf_config['vrf_add']:
diff --git a/src/conf_mode/vrrp.py b/src/conf_mode/vrrp.py
index b17f1ce82..8683faca7 100755
--- a/src/conf_mode/vrrp.py
+++ b/src/conf_mode/vrrp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,135 +13,26 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
import os
-import sys
import subprocess
-import ipaddress
-import json
-import jinja2
+
+from sys import exit
+from ipaddress import ip_address, ip_interface, IPv4Interface, IPv6Interface, IPv4Address, IPv6Address
+from jinja2 import FileSystemLoader, Environment
+from json import dumps
+from pathlib import Path
import vyos.config
import vyos.keepalived
+from vyos.defaults import directories as vyos_data_dir
from vyos import ConfigError
-from pathlib import Path
daemon_file = "/etc/default/keepalived"
config_file = "/etc/keepalived/keepalived.conf"
config_dict_path = "/run/keepalived_config.dict"
-config_tmpl = """
-# Autogenerated by VyOS
-# Do not edit this file, all your changes will be lost
-# on next commit or reboot
-
-global_defs {
- dynamic_interfaces
- script_user root
- notify_fifo /run/keepalived_notify_fifo
- notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py
-}
-
-{% for group in groups -%}
-
-{% if group.health_check_script -%}
-vrrp_script healthcheck_{{ group.name }} {
- script "{{ group.health_check_script }}"
- interval {{ group.health_check_interval }}
- fall {{ group.health_check_count }}
- rise 1
-
-}
-{% endif %}
-
-vrrp_instance {{ group.name }} {
- {% if group.description -%}
- # {{ group.description }}
- {% endif -%}
-
- state BACKUP
- interface {{ group.interface }}
- virtual_router_id {{ group.vrid }}
- priority {{ group.priority }}
- advert_int {{ group.advertise_interval }}
-
- {% if group.preempt -%}
- preempt_delay {{ group.preempt_delay }}
- {% else -%}
- nopreempt
- {% endif -%}
-
- {% if group.peer_address -%}
- unicast_peer { {{ group.peer_address }} }
- {% endif -%}
-
- {% if group.hello_source -%}
- {%- if group.peer_address -%}
- unicast_src_ip {{ group.hello_source }}
- {%- else -%}
- mcast_src_ip {{ group.hello_source }}
- {%- endif %}
- {% endif -%}
-
- {% if group.use_vmac and group.peer_address -%}
- use_vmac {{group.interface}}v{{group.vrid}}
- vmac_xmit_base
- {% elif group.use_vmac -%}
- use_vmac {{group.interface}}v{{group.vrid}}
- {% endif -%}
-
- {% if group.auth_password -%}
- authentication {
- auth_pass "{{ group.auth_password }}"
- auth_type {{ group.auth_type }}
- }
- {% endif -%}
-
- virtual_ipaddress {
- {% for addr in group.virtual_addresses -%}
- {{ addr }}
- {% endfor -%}
- }
-
- {% if group.health_check_script -%}
- track_script {
- healthcheck_{{ group.name }}
- }
- {% endif -%}
-}
-
-{% endfor -%}
-
-{% for sync_group in sync_groups -%}
-vrrp_sync_group {{ sync_group.name }} {
- group {
- {% for member in sync_group.members -%}
- {{ member }}
- {% endfor -%}
- }
-
- {% if sync_group.conntrack_sync -%}
- notify_master "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh master {{ sync_group.name }}"
- notify_backup "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh backup {{ sync_group.name }}"
- notify_fault "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh fault {{ sync_group.name }}"
- {% endif -%}
-}
-
-{% endfor -%}
-
-"""
-
-daemon_tmpl = """
-# Autogenerated by VyOS
-# Options to pass to keepalived
-
-# DAEMON_ARGS are appended to the keepalived command-line
-DAEMON_ARGS="--snmp"
-"""
-
-
def get_config():
vrrp_groups = []
sync_groups = []
@@ -209,7 +100,7 @@ def get_config():
vrrp_groups.append(group)
- config.set_level("")
+ config.set_level("")
# Get the sync group used for conntrack-sync
conntrack_sync_group = None
@@ -227,11 +118,17 @@ def get_config():
if conntrack_sync_group == sync_group_name:
sync_group["conntrack_sync"] = True
+ # add transition script configuration
+ sync_group["master_script"] = config.return_value("transition-script master")
+ sync_group["backup_script"] = config.return_value("transition-script backup")
+ sync_group["fault_script"] = config.return_value("transition-script fault")
+ sync_group["stop_script"] = config.return_value("transition-script stop")
+
sync_groups.append(sync_group)
# create a file with dict with proposed configuration
with open("{}.temp".format(config_dict_path), 'w') as dict_file:
- dict_file.write(json.dumps({'vrrp_groups': vrrp_groups, 'sync_groups': sync_groups}))
+ dict_file.write(dumps({'vrrp_groups': vrrp_groups, 'sync_groups': sync_groups}))
return (vrrp_groups, sync_groups)
@@ -256,31 +153,31 @@ def verify(data):
# XXX: filter on map object is destructive, so we force it to list.
# Additionally, filter objects always evaluate to True, empty or not,
# so we force them to lists as well.
- vaddrs = list(map(lambda i: ipaddress.ip_interface(i), group["virtual_addresses"]))
- vaddrs4 = list(filter(lambda x: isinstance(x, ipaddress.IPv4Interface), vaddrs))
- vaddrs6 = list(filter(lambda x: isinstance(x, ipaddress.IPv6Interface), vaddrs))
+ vaddrs = list(map(lambda i: ip_interface(i), group["virtual_addresses"]))
+ vaddrs4 = list(filter(lambda x: isinstance(x, IPv4Interface), vaddrs))
+ vaddrs6 = list(filter(lambda x: isinstance(x, IPv6Interface), vaddrs))
if vaddrs4 and vaddrs6:
raise ConfigError("VRRP group {0} mixes IPv4 and IPv6 virtual addresses, this is not allowed. Create separate groups for IPv4 and IPv6".format(group["name"]))
if vaddrs4:
if group["hello_source"]:
- hsa = ipaddress.ip_address(group["hello_source"])
- if isinstance(hsa, ipaddress.IPv6Address):
+ hsa = ip_address(group["hello_source"])
+ if isinstance(hsa, IPv6Address):
raise ConfigError("VRRP group {0} uses IPv4 but its hello-source-address is IPv6".format(group["name"]))
if group["peer_address"]:
- pa = ipaddress.ip_address(group["peer_address"])
- if isinstance(pa, ipaddress.IPv6Address):
+ pa = ip_address(group["peer_address"])
+ if isinstance(pa, IPv6Address):
raise ConfigError("VRRP group {0} uses IPv4 but its peer-address is IPv6".format(group["name"]))
if vaddrs6:
if group["hello_source"]:
- hsa = ipaddress.ip_address(group["hello_source"])
- if isinstance(hsa, ipaddress.IPv4Address):
+ hsa = ip_address(group["hello_source"])
+ if isinstance(hsa, IPv4Address):
raise ConfigError("VRRP group {0} uses IPv6 but its hello-source-address is IPv4".format(group["name"]))
if group["peer_address"]:
- pa = ipaddress.ip_address(group["peer_address"])
- if isinstance(pa, ipaddress.IPv4Address):
+ pa = ip_address(group["peer_address"])
+ if isinstance(pa, IPv4Address):
raise ConfigError("VRRP group {0} uses IPv6 but its peer-address is IPv4".format(group["name"]))
# Disallow same VRID on multiple interfaces
@@ -304,6 +201,11 @@ def verify(data):
def generate(data):
+ # Prepare Jinja2 template loader from files
+ tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'vrrp')
+ fs_loader = FileSystemLoader(tmpl_path)
+ env = Environment(loader=fs_loader)
+
vrrp_groups, sync_groups = data
# Remove disabled groups from the sync group member lists
@@ -315,13 +217,15 @@ def generate(data):
# Filter out disabled groups
vrrp_groups = list(filter(lambda x: x["disable"] is not True, vrrp_groups))
- tmpl = jinja2.Template(config_tmpl)
+ tmpl = env.get_template('keepalived.conf.tmpl')
config_text = tmpl.render({"groups": vrrp_groups, "sync_groups": sync_groups})
with open(config_file, 'w') as f:
f.write(config_text)
+ tmpl = env.get_template('daemon.tmpl')
+ config_text = tmpl.render()
with open(daemon_file, 'w') as f:
- f.write(daemon_tmpl)
+ f.write(config_text)
return None
@@ -362,4 +266,4 @@ if __name__ == '__main__':
apply(c)
except ConfigError as e:
print("VRRP error: {0}".format(str(e)))
- sys.exit(1)
+ exit(1)
diff --git a/src/etc/ppp/ip-down.d/0020-wirelessmodem b/src/etc/ppp/ip-down.d/0020-wirelessmodem
deleted file mode 100755
index c93c7cabe..000000000
--- a/src/etc/ppp/ip-down.d/0020-wirelessmodem
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/sh
-
-tty=$2
-ipparam=$6
-
-# Only applicable for Wireless Modems (WWAN)
-if [ -z "$(echo $tty | egrep "tty(USB|ACM)")" ]; then
- exit 0
-fi
-
-# device name and metric are received using ipparam
-device=`echo "$ipparam"|awk '{ print $1 }'`
-metric=`echo "$ipparam"|awk '{ print $2 }'`
-
-vtysh -c "conf t" -c "no ip route 0.0.0.0/0 ${device} ${metric}"
-
-DIALER_PID=$(cat /var/run/${device}.pid)
-logger -t pppd[$DIALER_PID] "removed default route via $device metric $metric"
diff --git a/src/etc/ppp/ip-up.d/0020-wirelessmodem b/src/etc/ppp/ip-up.d/0020-wirelessmodem
deleted file mode 100755
index 95549387b..000000000
--- a/src/etc/ppp/ip-up.d/0020-wirelessmodem
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/sh
-
-tty=$2
-ipparam=$6
-
-# Only applicable for Wireless Modems (WWAN)
-if [ -z "$(echo $tty | egrep "tty(USB|ACM)")" ]; then
- exit 0
-fi
-
-# device name and metric are received using ipparam
-device=`echo "$ipparam"|awk '{ print $1 }'`
-metric=`echo "$ipparam"|awk '{ print $2 }'`
-
-vtysh -c "conf t" -c "ip route 0.0.0.0/0 ${device} ${metric}"
-
-DIALER_PID=$(cat /var/run/${device}.pid)
-logger -t pppd[$DIALER_PID] "added default route via $device metric $metric"
diff --git a/src/migration-scripts/interfaces/6-to-7 b/src/migration-scripts/interfaces/6-to-7
index b4f59c363..220c7e601 100755
--- a/src/migration-scripts/interfaces/6-to-7
+++ b/src/migration-scripts/interfaces/6-to-7
@@ -35,7 +35,7 @@ if __name__ == '__main__':
# Nothing to do
sys.exit(0)
- # list all individual interface types like dummy, ethernet and so on
+ # list all individual wwan/wireless modem interfaces
for i in config.list_nodes(base):
iface = base + [i]
diff --git a/src/migration-scripts/interfaces/7-to-8 b/src/migration-scripts/interfaces/7-to-8
new file mode 100755
index 000000000..78bd2781b
--- /dev/null
+++ b/src/migration-scripts/interfaces/7-to-8
@@ -0,0 +1,58 @@
+#!/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/>.
+
+# Remove network provider name from CLI and rather use provider APN from CLI
+
+from sys import exit, argv
+from vyos.configtree import ConfigTree
+
+if __name__ == '__main__':
+ if (len(argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+ file_name = argv[1]
+ with open(file_name, 'r') as f:
+ config_file = f.read()
+
+ config = ConfigTree(config_file)
+ base = ['interfaces', 'wireguard']
+
+ if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+ # list all individual wireguard interface isntance
+ for i in config.list_nodes(base):
+ iface = base + [i]
+ for peer in config.list_nodes(iface + ['peer']):
+ base_peer = iface + ['peer', peer]
+ if config.exists(base_peer + ['endpoint']):
+ endpoint = config.return_value(base_peer + ['endpoint'])
+ address = endpoint.split(':')[0]
+ port = endpoint.split(':')[1]
+ # delete old node
+ config.delete(base_peer + ['endpoint'])
+ # setup new nodes
+ config.set(base_peer + ['address'], value=address)
+ config.set(base_peer + ['port'], value=port)
+
+ try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+ except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
diff --git a/src/op_mode/show_openvpn.py b/src/op_mode/show_openvpn.py
index 06b90296f..32918ddce 100755
--- a/src/op_mode/show_openvpn.py
+++ b/src/op_mode/show_openvpn.py
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
+import os
import jinja2
import argparse
@@ -63,6 +64,9 @@ def get_status(mode, interface):
'clients': [],
}
+ if not os.path.exists(status_file):
+ return data
+
with open(status_file, 'r') as f:
lines = f.readlines()
for line_no, line in enumerate(lines):
diff --git a/src/op_mode/wireguard.py b/src/op_mode/wireguard.py
index 512c80dda..c684f8a47 100755
--- a/src/op_mode/wireguard.py
+++ b/src/op_mode/wireguard.py
@@ -150,7 +150,7 @@ if __name__ == '__main__':
if args.listkdir:
list_key_dirs()
if args.showinterface:
- intf = WireGuardIf(args.showinterface, debug=False)
+ intf = WireGuardIf(args.showinterface, create=False, debug=False)
intf.op_show_interface()
if args.delkdir:
if args.location: