summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/PULL_REQUEST_TEMPLATE.md38
-rw-r--r--CONTRIBUTING.md94
-rw-r--r--Makefile3
-rw-r--r--data/configd-include.json3
-rw-r--r--data/templates/accel-ppp/chap-secrets.ipoe.tmpl26
-rw-r--r--data/templates/accel-ppp/config_chap_secrets_radius.j22
-rw-r--r--data/templates/accel-ppp/ipoe.config.tmpl24
-rw-r--r--data/templates/accel-ppp/l2tp.config.tmpl28
-rw-r--r--data/templates/accel-ppp/pppoe.config.tmpl2
-rw-r--r--data/templates/accel-ppp/pptp.config.tmpl18
-rw-r--r--data/templates/bcast-relay/udp-broadcast-relay.tmpl2
-rw-r--r--data/templates/dhcp-relay/config.tmpl4
-rw-r--r--data/templates/dhcp-relay/dhcrelay.conf.tmpl6
-rw-r--r--data/templates/dhcp-relay/dhcrelay6.conf.tmpl21
-rw-r--r--data/templates/dhcp-server/dhcpd.conf.tmpl180
-rw-r--r--data/templates/dhcpv6-relay/config.tmpl4
-rw-r--r--data/templates/dhcpv6-server/dhcpdv6.conf.tmpl100
-rw-r--r--data/templates/firewall/nftables-nat.tmpl142
-rw-r--r--data/templates/frr/bfd.frr.tmpl8
-rw-r--r--data/templates/frr/bgp.frr.tmpl1243
-rw-r--r--data/templates/frr/igmp.frr.tmpl50
-rw-r--r--data/templates/frr/ldpd.frr.tmpl273
-rw-r--r--data/templates/frr/pimd.frr.tmpl40
-rw-r--r--data/templates/frr/rip.frr.tmpl188
-rw-r--r--data/templates/frr/static_mcast.frr.tmpl28
-rw-r--r--data/templates/ids/fastnetmon.tmpl18
-rw-r--r--data/templates/igmp-proxy/igmpproxy.conf.tmpl51
-rw-r--r--data/templates/lldp/vyos.conf.tmpl18
-rw-r--r--data/templates/netflow/uacctd.conf.tmpl80
-rw-r--r--data/templates/ntp/ntp.conf.tmpl14
-rw-r--r--data/templates/openvpn/auth.pw.tmpl5
-rw-r--r--data/templates/openvpn/server.conf.tmpl35
-rw-r--r--data/templates/pppoe/ip-pre-up.script.tmpl2
-rw-r--r--data/templates/pppoe/ip-up.script.tmpl6
-rw-r--r--data/templates/pppoe/ipv6-up.script.tmpl8
-rw-r--r--data/templates/pppoe/peer.tmpl8
-rw-r--r--data/templates/router-advert/radvd.conf.tmpl2
-rw-r--r--data/templates/salt-minion/minion.tmpl2
-rw-r--r--data/templates/snmp/usr.snmpd.conf.tmpl4
-rw-r--r--data/templates/snmp/var.snmpd.conf.tmpl14
-rw-r--r--data/templates/syslog/rsyslog.conf.tmpl8
-rw-r--r--data/templates/vrf/vrf.conf.tmpl4
-rw-r--r--data/templates/vrrp/keepalived.conf.tmpl68
-rw-r--r--data/templates/vyos-hostsd/hosts.tmpl16
-rw-r--r--data/templates/vyos-hostsd/resolv.conf.tmpl25
-rw-r--r--data/templates/wifi/hostapd.conf.tmpl8
-rw-r--r--data/templates/wwan/ip-pre-up.script.tmpl2
-rw-r--r--debian/control1
-rw-r--r--interface-definitions/dhcp-relay.xml.in4
-rw-r--r--interface-definitions/dhcpv6-relay.xml.in1
-rw-r--r--interface-definitions/igmp-proxy.xml.in10
-rw-r--r--interface-definitions/include/nat-rule.xml.i1
-rw-r--r--interface-definitions/interfaces-tunnel.xml.in41
-rw-r--r--interface-definitions/interfaces-wireguard.xml.in1
-rw-r--r--interface-definitions/interfaces-wireless.xml.in6
-rw-r--r--interface-definitions/ntp.xml.in6
-rw-r--r--interface-definitions/policy-local-route.xml.in67
-rw-r--r--interface-definitions/protocols-mpls.xml.in9
-rw-r--r--interface-definitions/system-option.xml.in (renamed from interface-definitions/system-options.xml.in)86
-rw-r--r--op-mode-definitions/force-arp.xml24
-rw-r--r--op-mode-definitions/restart.xml8
-rw-r--r--python/vyos/configdict.py10
-rw-r--r--python/vyos/ifconfig/bridge.py59
-rw-r--r--python/vyos/ifconfig/interface.py24
-rw-r--r--python/vyos/ifconfig/tunnel.py203
-rw-r--r--python/vyos/ifconfig/wireguard.py31
-rw-r--r--python/vyos/ifconfig/wireless.py29
-rw-r--r--python/vyos/limericks.py16
-rw-r--r--python/vyos/template.py23
-rw-r--r--python/vyos/xml/definition.py11
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_openvpn.py44
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_tunnel.py6
-rwxr-xr-xsmoketest/scripts/cli/test_policy_local-route.py61
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_igmp-proxy.py83
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcp-relay.py93
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcpv6-relay.py112
-rwxr-xr-xsmoketest/scripts/cli/test_service_pppoe-server.py3
-rwxr-xr-xsmoketest/scripts/cli/test_system_ntp.py7
-rwxr-xr-xsrc/conf_mode/bcast_relay.py2
-rwxr-xr-xsrc/conf_mode/dhcp_relay.py79
-rwxr-xr-xsrc/conf_mode/dhcpv6_relay.py94
-rwxr-xr-xsrc/conf_mode/dns_forwarding.py4
-rwxr-xr-xsrc/conf_mode/dynamic_dns.py4
-rwxr-xr-xsrc/conf_mode/https.py2
-rwxr-xr-xsrc/conf_mode/igmp_proxy.py94
-rwxr-xr-xsrc/conf_mode/interfaces-bridge.py16
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py30
-rwxr-xr-xsrc/conf_mode/interfaces-pppoe.py22
-rwxr-xr-xsrc/conf_mode/interfaces-tunnel.py781
-rwxr-xr-xsrc/conf_mode/interfaces-wireguard.py3
-rwxr-xr-xsrc/conf_mode/interfaces-wireless.py7
-rwxr-xr-xsrc/conf_mode/interfaces-wirelessmodem.py10
-rwxr-xr-xsrc/conf_mode/ipsec-settings.py8
-rwxr-xr-xsrc/conf_mode/nat.py218
-rwxr-xr-xsrc/conf_mode/ntp.py4
-rwxr-xr-xsrc/conf_mode/policy-local-route.py110
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py66
-rwxr-xr-xsrc/conf_mode/protocols_igmp.py28
-rwxr-xr-xsrc/conf_mode/protocols_isis.py7
-rwxr-xr-xsrc/conf_mode/protocols_mpls.py407
-rwxr-xr-xsrc/conf_mode/protocols_pim.py28
-rwxr-xr-xsrc/conf_mode/service_ids_fastnetmon.py6
-rwxr-xr-xsrc/conf_mode/service_ipoe-server.py2
-rwxr-xr-xsrc/conf_mode/service_pppoe-server.py4
-rwxr-xr-xsrc/conf_mode/service_router-advert.py2
-rwxr-xr-xsrc/conf_mode/ssh.py4
-rwxr-xr-xsrc/conf_mode/system-login.py2
-rwxr-xr-xsrc/conf_mode/system-option.py (renamed from src/conf_mode/system-options.py)17
-rwxr-xr-xsrc/conf_mode/system-syslog.py4
-rwxr-xr-xsrc/conf_mode/system_lcd.py4
-rwxr-xr-xsrc/conf_mode/tftp_server.py2
-rwxr-xr-xsrc/conf_mode/vpn_l2tp.py2
-rwxr-xr-xsrc/conf_mode/vpn_openconnect.py17
-rwxr-xr-xsrc/conf_mode/vpn_pptp.py4
-rwxr-xr-xsrc/conf_mode/vpn_sstp.py4
-rwxr-xr-xsrc/migration-scripts/interfaces/14-to-1558
-rwxr-xr-xsrc/migration-scripts/system/19-to-2064
-rwxr-xr-xsrc/op_mode/dynamic_dns.py4
-rwxr-xr-xsrc/op_mode/lldp_op.py2
-rwxr-xr-xsrc/op_mode/show_cpu.py20
-rwxr-xr-xsrc/op_mode/show_igmpproxy.py8
-rwxr-xr-xsrc/op_mode/show_nat_statistics.py12
-rwxr-xr-xsrc/op_mode/show_openvpn.py4
-rwxr-xr-xsrc/op_mode/show_usb_serial.py4
-rwxr-xr-xsrc/op_mode/show_vrf.py4
-rwxr-xr-xsrc/op_mode/show_wireless.py17
-rwxr-xr-xsrc/op_mode/snmp_v3.py8
-rw-r--r--src/systemd/isc-dhcp-relay.service4
-rw-r--r--src/systemd/isc-dhcp-relay6.service8
129 files changed, 2741 insertions, 3579 deletions
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 000000000..cc5e2f536
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,38 @@
+<!-- All PR should follow this template to allow a clean and transparent review -->
+<!-- Text placed between these delimiters is considered a commend and is not rendered -->
+
+## Change Summary
+<!--- Provide a general summary of your changes in the Title above -->
+
+## Types of changes
+<!--- What types of changes does your code introduce? Put an 'x' in all the boxes that apply. -->
+<!--- NOTE: Markdown requires no leading or trailing whitespace inside the [ ] for checking the box, please use [x] -->
+- [ ] Bug fix (non-breaking change which fixes an issue)
+- [ ] New feature (non-breaking change which adds functionality)
+- [ ] Code style update (formatting, renaming)
+- [ ] Refactoring (no functional changes)
+- [ ] Migration from an old Vyatta component to vyos-1x, please link to related PR inside obsoleted component
+- [ ] Other (please describe):
+
+## Related Task(s)
+<!-- All submitted PRs must be linked to a Task on Phabricator. -->
+
+## Component(s) name
+<!-- A rather incomplete list of components: ethernet, wireguard, bgp, mpls, ldp, l2tp, dhcp ... -->
+
+## Proposed changes
+<!--- Describe your changes in detail -->
+
+## How to test
+<!--- Please describe in detail how you tested your changes. -->
+<!--- Include details of your testing environment, and the tests you ran to -->
+
+## Checklist:
+<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
+<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
+<!--- The entire development process is outlined here: https://docs.vyos.io/en/latest/contributing/development.html -->
+- [ ] I have read the [**CONTRIBUTING**](https://github.com/vyos/vyos-1x/blob/current/CONTRIBUTING.md) document
+- [ ] I have linked this PR to one or more Phabricator Task(s)
+- [ ] My commit headlines contain a valid Task id
+- [ ] My change requires a change to the documentation
+- [ ] I have updated the documentation accordingly
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d8177a5f5..7ac48ee4c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,12 +1,90 @@
# Contributing to VyOS
-You wan't to help us improve VyOS? This is awesome. We accept any kind of Pull
-Requests on GitHub. To make the life of the maintainers and you as future
-contributor (or maybe maintainer) much easier we have come up with some basic
-rules. Instead of copy/pasting or maintaining two instances of how to contribute
-to VyOS you can find the entire process documented in our online documentation:
-https://docs.vyos.io/en/latest/contributing/development.html
+You wan't to help us improve VyOS? This is awesome!
-Also this guide might not be complete so any PR is much appreciated.
+We accept any kind of Pull Requests on GitHub. In order to get your changes into
+the main repository as smooth as possible please take yourself some time and
+review this contribution guideline.
-It might also worth browsing our blog: https://blog.vyos.io
+The following paragraphs are an excerpt from our Documentation.
+
+## Bug Report/Issue
+Issues or bugs are found in any software project. VyOS is not an exception.
+
+All issues should be reported to the developers. This lets the developers know
+what is not working properly. Without this sort of feedback every developer
+will believe that everything is working correctly.
+
+### I have found a bug, what should I do?
+
+When you believe you have found a bug, it is always a good idea to verify the
+issue prior to opening a bug request.
+
+* Consult our [Documentation](https://docs.vyos.io) to ensure that you have
+ configured your system correctly
+* Get community support via [Slack](https://slack.vyos.io) or our online
+ [Forum](https://forum.vyos.io)
+
+#### Ensure the problem is reproducible
+
+When you are able to verify that it is actually a bug, spend some time to
+document how to reproduce the issue. This documentation can be invaluable.
+
+When you wish to have a developer fix a bug that you found, helping them
+reproduce the issue is beneficial to everyone. Be sure to include information
+about the hardware you are using, commands that you were running, any other
+activities that you may have been doing at the time. This additional
+information can be very useful.
+
+* What were you attempting to achieve?
+* What was the configuration prior to the change?
+* What commands did you use? Use e.g. ``show configuration commands``
+
+#### Include output
+
+The output you get when you find a bug can provide lots of information. If you
+get an error message on the screen, copy it exactly. Having the exact message
+can provide detail that the developers can use. Like wise if you have any log
+messages that also are from the time of the issue, include those. They may
+also contain information that is helpful for the development team.
+
+### Reporting
+
+In order to open up a bug-report/feature request you need to create yourself
+an account on [Phabricator](https://phabricator.vyos.net). On the left
+side of the specific project (VyOS 1.2 or VyOS 1.3) you will find quick-links
+for opening a bug-report/feature request.
+
+* Provide as much information as you can
+* Which version of VyOS are you using? Use operational level command:
+ ``show version``
+* How can we reproduce this Bug? Please include a CLI configuration, you can
+ use ``show configuration command | strip-private`` to remove sensitive
+ information before publishing.
+
+## Feature Request
+
+You have an idea of how to make VyOS better or you are in need of a specific
+feature which all users of VyOS would benefit from? To send a feature request
+please search [Phabricator](https://phabricator.vyos.net) if there is already a
+request pending. You can enhance it or if you don't find one, create a new one
+by use the quick link in the left side under the specific project.
+
+## Code Contribution
+
+For contributing code to VyOS please take a short moment and review the guideline
+outlined in our Documentation at
+https://docs.vyos.io/en/latest/contributing/development.html#submit-a-patch
+
+### Coding Guidelines
+
+We have some small coding guidelines which are defined in a separate section of
+at https://docs.vyos.io/en/latest/contributing/development.html#coding-guidelines.
+The guidelines cover how to create the necessary XML structure for new features
+and also how to read in the code from the CLI into the Python based scripting
+backend.
+
+Thank you for taking the time reading this guide.
+
+It might also worth browsing our [Blog](https://blog.vyos.io) for additional
+info and updates.
diff --git a/Makefile b/Makefile
index d18676619..b116735da 100644
--- a/Makefile
+++ b/Makefile
@@ -76,9 +76,11 @@ interface_definitions: $(BUILD_DIR) $(obj)
rm -f $(TMPL_DIR)/interfaces/wireless/node.tag/vif/node.tag/ip/node.def
rm -f $(TMPL_DIR)/interfaces/wireless/node.tag/vif/node.tag/ipv6/node.def
rm -f $(TMPL_DIR)/interfaces/wirelessmodem/node.tag/ipv6/node.def
+ rm -f $(TMPL_DIR)/interfaces/wireguard/node.tag/ipv6/node.def
rm -f $(TMPL_DIR)/protocols/node.def
rm -rf $(TMPL_DIR)/protocols/nbgp
rm -f $(TMPL_DIR)/protocols/static/node.def
+ rm -f $(TMPL_DIR)/policy/node.def
rm -f $(TMPL_DIR)/system/node.def
rm -f $(TMPL_DIR)/vpn/node.def
rm -f $(TMPL_DIR)/vpn/ipsec/node.def
@@ -100,7 +102,6 @@ op_mode_definitions:
rm -f $(OP_TMPL_DIR)/show/ipv6/node.def
rm -f $(OP_TMPL_DIR)/show/ipv6/bgp/node.def
rm -f $(OP_TMPL_DIR)/show/ipv6/route/node.def
- rm -f $(OP_TMPL_DIR)/restart/node.def
rm -f $(OP_TMPL_DIR)/monitor/node.def
rm -f $(OP_TMPL_DIR)/generate/node.def
rm -f $(OP_TMPL_DIR)/show/system/node.def
diff --git a/data/configd-include.json b/data/configd-include.json
index 2e44405ee..eb1dd13f9 100644
--- a/data/configd-include.json
+++ b/data/configd-include.json
@@ -29,6 +29,7 @@
"lldp.py",
"nat.py",
"ntp.py",
+"policy-local-route.py",
"protocols_igmp.py",
"protocols_isis.py",
"protocols_mpls.py",
@@ -46,7 +47,7 @@
"system-ip.py",
"system-ipv6.py",
"system-login-banner.py",
-"system-options.py",
+"system-option.py",
"system-syslog.py",
"system-timezone.py",
"system_console.py",
diff --git a/data/templates/accel-ppp/chap-secrets.ipoe.tmpl b/data/templates/accel-ppp/chap-secrets.ipoe.tmpl
index a7d899354..1df878fcf 100644
--- a/data/templates/accel-ppp/chap-secrets.ipoe.tmpl
+++ b/data/templates/accel-ppp/chap-secrets.ipoe.tmpl
@@ -1,18 +1,18 @@
# username server password acceptable local IP addresses shaper
-{% for interface in auth_interfaces -%}
-{% for mac in interface.mac -%}
-{% if mac.rate_upload and mac.rate_download -%}
-{% if mac.vlan_id -%}
+{% for interface in auth_interfaces %}
+{% for mac in interface.mac %}
+{% if mac.rate_upload and mac.rate_download %}
+{% if mac.vlan_id %}
{{ interface.name }}.{{ mac.vlan_id }} * {{ mac.address | lower }} * {{ mac.rate_download }}/{{ mac.rate_upload }}
-{% else -%}
+{% else %}
{{ interface.name }} * {{ mac.address | lower }} * {{ mac.rate_download }}/{{ mac.rate_upload }}
-{% endif -%}
-{% else -%}
-{% if mac.vlan_id -%}
+{% endif %}
+{% else %}
+{% if mac.vlan_id %}
{{ interface.name }}.{{ mac.vlan_id }} * {{ mac.address | lower }} *
-{% else -%}
+{% else %}
{{ interface.name }} * {{ mac.address | lower }} *
-{% endif -%}
-{% endif -%}
-{% endfor -%}
-{% endfor -%}
+{% endif %}
+{% endif %}
+{% endfor %}
+{% endfor %}
diff --git a/data/templates/accel-ppp/config_chap_secrets_radius.j2 b/data/templates/accel-ppp/config_chap_secrets_radius.j2
index c94e75a23..52d5d40e7 100644
--- a/data/templates/accel-ppp/config_chap_secrets_radius.j2
+++ b/data/templates/accel-ppp/config_chap_secrets_radius.j2
@@ -27,7 +27,7 @@ called-sid={{ authentication.radius.called_sid_format }}
{% endif %}
{% if authentication.radius.dynamic_author.server is defined and authentication.radius.dynamic_author.server is not none %}
dae-server={{ authentication.radius.dynamic_author.server }}:{{ authentication.radius.dynamic_author.port }},{{ authentication.radius.dynamic_author.key }}
-{% endif -%}
+{% endif %}
{% endif %}
{# Both chap-secrets and radius block required the gw-ip-address #}
{% if gateway_address is defined and gateway_address is not none %}
diff --git a/data/templates/accel-ppp/ipoe.config.tmpl b/data/templates/accel-ppp/ipoe.config.tmpl
index ab61f7f5a..1cf2ab0be 100644
--- a/data/templates/accel-ppp/ipoe.config.tmpl
+++ b/data/templates/accel-ppp/ipoe.config.tmpl
@@ -35,7 +35,7 @@ password=csid
{% endif %}
proxy-arp=1
-{%- for interface in interfaces %}
+{% for interface in interfaces %}
{% if (interface.shared == '0') and (interface.vlan_mon) %}
vlan-mon={{ interface.name }},{{ interface.vlan_mon | join(',') }}
{% endif %}
@@ -43,16 +43,16 @@ vlan-mon={{ interface.name }},{{ interface.vlan_mon | join(',') }}
{% if dnsv4 %}
[dns]
-{% for dns in dnsv4 -%}
+{% for dns in dnsv4 %}
dns{{ loop.index }}={{ dns }}
-{% endfor -%}
+{% endfor %}
{% endif %}
{% if dnsv6 %}
[ipv6-dns]
-{% for dns in dnsv6 -%}
+{% for dns in dnsv6 %}
{{ dns }}
-{% endfor -%}
+{% endfor %}
{% endif %}
[ipv6-nd]
@@ -79,7 +79,7 @@ chap-secrets={{ chap_secrets_file }}
verbose=1
{% for r in radius_server %}
server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }}
-{% endfor -%}
+{% endfor %}
{% if radius_acct_inter_jitter %}
acct-interim-jitter={{ radius_acct_inter_jitter }}
@@ -90,17 +90,17 @@ timeout={{ radius_timeout }}
max-try={{ radius_max_try }}
{% if radius_nas_id %}
nas-identifier={{ radius_nas_id }}
-{% endif -%}
+{% endif %}
{% if radius_nas_ip %}
nas-ip-address={{ radius_nas_ip }}
-{% endif -%}
+{% endif %}
{% if radius_source_address %}
bind={{ radius_source_address }}
-{% endif -%}
+{% endif %}
{% if radius_dynamic_author %}
dae-server={{ radius_dynamic_author.server }}:{{ radius_dynamic_author.port }},{{ radius_dynamic_author.key }}
-{% endif -%}
+{% endif %}
{% if radius_shaper_attr %}
[shaper]
@@ -108,8 +108,8 @@ verbose=1
attr={{ radius_shaper_attr }}
{% if radius_shaper_vendor %}
vendor={{ radius_shaper_vendor }}
-{% endif -%}
-{% endif -%}
+{% endif %}
+{% endif %}
{% endif %}
[cli]
diff --git a/data/templates/accel-ppp/l2tp.config.tmpl b/data/templates/accel-ppp/l2tp.config.tmpl
index 9a3c27912..070a966b7 100644
--- a/data/templates/accel-ppp/l2tp.config.tmpl
+++ b/data/templates/accel-ppp/l2tp.config.tmpl
@@ -9,7 +9,7 @@ chap-secrets
{% if auth_mode == 'radius' %}
radius
-{% endif -%}
+{% endif %}
ippool
shaper
@@ -27,23 +27,23 @@ level=5
{% if dnsv4 %}
[dns]
-{% for dns in dnsv4 -%}
+{% for dns in dnsv4 %}
dns{{ loop.index }}={{ dns }}
-{% endfor -%}
+{% endfor %}
{% endif %}
{% if dnsv6 %}
[ipv6-dns]
-{% for dns in dnsv6 -%}
+{% for dns in dnsv6 %}
{{ dns }}
-{% endfor -%}
+{% endfor %}
{% endif %}
{% if wins %}
[wins]
-{% for server in wins -%}
+{% for server in wins %}
wins{{ loop.index }}={{ server }}
-{% endfor -%}
+{% endfor %}
{% endif %}
[l2tp]
@@ -65,11 +65,11 @@ secret={{ lns_shared_secret }}
[ip-pool]
{% if client_ip_pool %}
{{ client_ip_pool }}
-{% endif -%}
+{% endif %}
{% if client_ip_subnets %}
{% for sn in client_ip_subnets %}
{{sn}}
-{% endfor -%}
+{% endfor %}
{% endif %}
{% endif %}
{% if gateway_address %}
@@ -84,7 +84,7 @@ chap-secrets={{ chap_secrets_file }}
verbose=1
{% for r in radius_server %}
server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }}
-{% endfor -%}
+{% endfor %}
{% if radius_acct_inter_jitter %}
acct-interim-jitter={{ radius_acct_inter_jitter }}
@@ -96,13 +96,13 @@ max-try={{ radius_max_try }}
{% if radius_nas_id %}
nas-identifier={{ radius_nas_id }}
-{% endif -%}
+{% endif %}
{% if radius_nas_ip %}
nas-ip-address={{ radius_nas_ip }}
-{% endif -%}
+{% endif %}
{% if radius_source_address %}
bind={{ radius_source_address }}
-{% endif -%}
+{% endif %}
{% endif %}
{% if gateway_address %}
gw-ip-address={{ gateway_address }}
@@ -144,7 +144,7 @@ verbose=1
attr={{ radius_shaper_attr }}
{% if radius_shaper_vendor %}
vendor={{ radius_shaper_vendor }}
-{% endif -%}
+{% endif %}
{% endif %}
[cli]
diff --git a/data/templates/accel-ppp/pppoe.config.tmpl b/data/templates/accel-ppp/pppoe.config.tmpl
index 19adbc890..1d6c1fefc 100644
--- a/data/templates/accel-ppp/pppoe.config.tmpl
+++ b/data/templates/accel-ppp/pppoe.config.tmpl
@@ -108,7 +108,7 @@ interface=re:{{ interface.name }}\.\d+
{% if service_name %}
service-name={{ service_name | join(',') }}
-{% endif -%}
+{% endif %}
{% if pado_delay %}
pado-delay={{ pado_delay }}
diff --git a/data/templates/accel-ppp/pptp.config.tmpl b/data/templates/accel-ppp/pptp.config.tmpl
index dc77b42e9..5a6cfe749 100644
--- a/data/templates/accel-ppp/pptp.config.tmpl
+++ b/data/templates/accel-ppp/pptp.config.tmpl
@@ -7,7 +7,7 @@ ippool
chap-secrets
{% elif auth_mode == 'radius' %}
radius
-{% endif -%}
+{% endif %}
{% for proto in auth_proto %}
{{proto}}
{% endfor %}
@@ -22,16 +22,16 @@ level=5
{% if dnsv4 %}
[dns]
-{% for dns in dnsv4 -%}
+{% for dns in dnsv4 %}
dns{{ loop.index }}={{ dns }}
-{% endfor -%}
+{% endfor %}
{% endif %}
{% if wins %}
[wins]
-{% for server in wins -%}
+{% for server in wins %}
wins{{ loop.index }}={{ server }}
-{% endfor -%}
+{% endfor %}
{% endif %}
@@ -67,7 +67,7 @@ chap-secrets={{ chap_secrets_file }}
verbose=1
{% for r in radius_server %}
server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }}
-{% endfor -%}
+{% endfor %}
{% if radius_acct_inter_jitter %}
acct-interim-jitter={{ radius_acct_inter_jitter }}
@@ -79,13 +79,13 @@ max-try={{ radius_max_try }}
{% if radius_nas_id %}
nas-identifier={{ radius_nas_id }}
-{% endif -%}
+{% endif %}
{% if radius_nas_ip %}
nas-ip-address={{ radius_nas_ip }}
-{% endif -%}
+{% endif %}
{% if radius_source_address %}
bind={{ radius_source_address }}
-{% endif -%}
+{% endif %}
{% endif %}
[cli]
diff --git a/data/templates/bcast-relay/udp-broadcast-relay.tmpl b/data/templates/bcast-relay/udp-broadcast-relay.tmpl
index d0c7d8bf9..73e9acad4 100644
--- a/data/templates/bcast-relay/udp-broadcast-relay.tmpl
+++ b/data/templates/bcast-relay/udp-broadcast-relay.tmpl
@@ -1,7 +1,7 @@
### Autogenerated by bcast_relay.py ###
# UDP broadcast relay configuration for instance {{ id }}
-{%- if description %}
+{% if description %}
# Comment: {{ description }}
{% endif %}
DAEMON_ARGS="{{ '-s ' + address if address is defined }} {{ instance }} {{ port }} {{ interface | join(' ') }}"
diff --git a/data/templates/dhcp-relay/config.tmpl b/data/templates/dhcp-relay/config.tmpl
deleted file mode 100644
index b223807cf..000000000
--- a/data/templates/dhcp-relay/config.tmpl
+++ /dev/null
@@ -1,4 +0,0 @@
-### Autogenerated by dhcp_relay.py ###
-
-# Defaults for isc-dhcp-relay6.service
-OPTIONS="{{ options | join(' ') }} -i {{ interface | join(' -i ') }} {{ server | join(' ') }}"
diff --git a/data/templates/dhcp-relay/dhcrelay.conf.tmpl b/data/templates/dhcp-relay/dhcrelay.conf.tmpl
new file mode 100644
index 000000000..a9d17ed9a
--- /dev/null
+++ b/data/templates/dhcp-relay/dhcrelay.conf.tmpl
@@ -0,0 +1,6 @@
+### Autogenerated by dhcp_relay.py ###
+
+{% set max_size = '-A ' + relay_options.max_size if relay_options.max_size is defined and relay_options.max_size is not none %}
+{# hop_count and relay_agents_packets is a default option, thus it is always present #}
+OPTIONS="-c {{ relay_options.hop_count }} -a -m {{ relay_options.relay_agents_packets }} {{ max_size }} -i {{ interface | join(' -i ') }} {{ server | join(' ') }}"
+
diff --git a/data/templates/dhcp-relay/dhcrelay6.conf.tmpl b/data/templates/dhcp-relay/dhcrelay6.conf.tmpl
new file mode 100644
index 000000000..58c216b7c
--- /dev/null
+++ b/data/templates/dhcp-relay/dhcrelay6.conf.tmpl
@@ -0,0 +1,21 @@
+### Autogenerated by dhcpv6_relay.py ###
+
+{# upstream_interface is mandatory so it's always present #}
+{% set upstream = namespace(value='') %}
+{% for interface, config in upstream_interface.items() %}
+{% for address in config.address %}
+{% set upstream.value = upstream.value + '-u ' + address + '%' + interface + ' ' %}
+{% endfor %}
+{% endfor %}
+{# listen_interface is mandatory so it's always present #}
+{% set listen = namespace(value='') %}
+{% for interface, config in listen_interface.items() %}
+{% if config.address is defined and config.address is not none %}
+{% set listen.value = listen.value + '-l ' + config.address + '%' + interface + ' ' %}
+{% else %}
+{% set listen.value = listen.value + '-l ' + interface + ' ' %}
+{% endif %}
+{% endfor %}
+
+OPTIONS="{{ listen.value }} {{ upstream.value }} -c {{ max_hop_count }} {{ '-I' if use_interface_id_option is defined }}"
+
diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl
index 5f5129451..f431d3207 100644
--- a/data/templates/dhcp-server/dhcpd.conf.tmpl
+++ b/data/templates/dhcp-server/dhcpd.conf.tmpl
@@ -23,38 +23,38 @@ on expiry {
execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", ClientName, ClientIp, ClientMac, ClientDomain);
}
{% endif %}
-{%- if host_decl_name %}
+{% if host_decl_name %}
use-host-decl-names on;
-{%- endif %}
-ddns-update-style {% if ddns_enable -%} interim {%- else -%} none {%- endif %};
-{% if static_route -%}
+{% 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 -%}
+{% endif %}
+{% if wpad %}
option wpad-url code 252 = text;
{% endif %}
-{%- if global_parameters %}
+{% 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 %}
+{% for param in global_parameters %}
{{ param }}
-{%- endfor -%}
-{%- endif %}
+{% endfor %}
+{% endif %}
# Failover configuration
{% for network in shared_network %}
-{%- if not network.disabled -%}
-{%- for subnet in network.subnet %}
-{%- if subnet.failover_name -%}
+{% if not network.disabled %}
+{% for subnet in network.subnet %}
+{% if subnet.failover_name %}
failover peer "{{ subnet.failover_name }}" {
-{%- if subnet.failover_status == 'primary' %}
+{% if subnet.failover_status == 'primary' %}
primary;
mclt 1800;
split 128;
-{%- elif subnet.failover_status == 'secondary' %}
+{% elif subnet.failover_status == 'secondary' %}
secondary;
-{%- endif %}
+{% endif %}
address {{ subnet.failover_local_addr }};
port 520;
peer address {{ subnet.failover_peer_addr }};
@@ -63,133 +63,133 @@ failover peer "{{ subnet.failover_name }}" {
max-unacked-updates 10;
load balance max seconds 3;
}
-{% endif -%}
-{% endfor -%}
-{% endif -%}
+{% endif %}
+{% endfor %}
+{% endif %}
{% endfor %}
# Shared network configration(s)
{% for network in shared_network %}
-{%- if not network.disabled -%}
+{% if not network.disabled %}
shared-network {{ network.name }} {
- {%- if network.authoritative %}
+ {% if network.authoritative %}
authoritative;
- {%- endif %}
- {%- if network.network_parameters %}
+ {% 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 %}
+ {% for param in network.network_parameters %}
{{ param }}
- {%- endfor %}
- {%- endif %}
- {%- for subnet in network.subnet %}
+ {% endfor %}
+ {% endif %}
+ {% for subnet in network.subnet %}
subnet {{ subnet.address }} netmask {{ subnet.netmask }} {
- {%- if subnet.dns_server %}
+ {% if subnet.dns_server %}
option domain-name-servers {{ subnet.dns_server | join(', ') }};
- {%- endif %}
- {%- if subnet.domain_search %}
+ {% endif %}
+ {% if subnet.domain_search %}
option domain-search {{ subnet.domain_search | join(', ') }};
- {%- endif %}
- {%- if subnet.ntp_server %}
+ {% endif %}
+ {% if subnet.ntp_server %}
option ntp-servers {{ subnet.ntp_server | join(', ') }};
- {%- endif %}
- {%- if subnet.pop_server %}
+ {% endif %}
+ {% if subnet.pop_server %}
option pop-server {{ subnet.pop_server | join(', ') }};
- {%- endif %}
- {%- if subnet.smtp_server %}
+ {% endif %}
+ {% if subnet.smtp_server %}
option smtp-server {{ subnet.smtp_server | join(', ') }};
- {%- endif %}
- {%- if subnet.time_server %}
+ {% endif %}
+ {% if subnet.time_server %}
option time-servers {{ subnet.time_server | join(', ') }};
- {%- endif %}
- {%- if subnet.wins_server %}
+ {% endif %}
+ {% if subnet.wins_server %}
option netbios-name-servers {{ subnet.wins_server | join(', ') }};
- {%- endif %}
- {%- if subnet.static_route %}
+ {% 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 %}
+ {% endif %}
+ {% if subnet.ip_forwarding %}
option ip-forwarding true;
- {%- endif -%}
- {%- if subnet.default_router %}
+ {% endif %}
+ {% if subnet.default_router %}
option routers {{ subnet.default_router }};
- {%- endif -%}
- {%- if subnet.server_identifier %}
+ {% endif %}
+ {% if subnet.server_identifier %}
option dhcp-server-identifier {{ subnet.server_identifier }};
- {%- endif -%}
- {%- if subnet.domain_name %}
+ {% endif %}
+ {% if subnet.domain_name %}
option domain-name "{{ subnet.domain_name }}";
- {%- endif -%}
- {%- if subnet.subnet_parameters %}
+ {% 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 %}
+ {% for param in subnet.subnet_parameters %}
{{ param }}
- {%- endfor -%}
- {%- endif %}
- {%- if subnet.tftp_server %}
+ {% endfor %}
+ {% endif %}
+ {% if subnet.tftp_server %}
option tftp-server-name "{{ subnet.tftp_server }}";
- {%- endif -%}
- {%- if subnet.bootfile_name %}
+ {% endif %}
+ {% if subnet.bootfile_name %}
option bootfile-name "{{ subnet.bootfile_name }}";
filename "{{ subnet.bootfile_name }}";
- {%- endif -%}
- {%- if subnet.bootfile_server %}
+ {% endif %}
+ {% if subnet.bootfile_server %}
next-server {{ subnet.bootfile_server }};
- {%- endif -%}
- {%- if subnet.time_offset %}
+ {% endif %}
+ {% if subnet.time_offset %}
option time-offset {{ subnet.time_offset }};
- {%- endif -%}
- {%- if subnet.wpad_url %}
+ {% endif %}
+ {% if subnet.wpad_url %}
option wpad-url "{{ subnet.wpad_url }}";
- {%- endif -%}
- {%- if subnet.client_prefix_length %}
+ {% endif %}
+ {% if subnet.client_prefix_length %}
option subnet-mask {{ subnet.client_prefix_length }};
- {%- endif -%}
+ {% 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 %}
+ {% 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 %}
+ {% endif %}
hardware ethernet {{ host.mac_address }};
- {%- if host.static_parameters %}
+ {% 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 %}
+ {% for param in host.static_parameters %}
{{ param }}
- {%- endfor -%}
- {%- endif %}
+ {% endfor %}
+ {% endif %}
}
- {%- endif %}
- {%- endfor %}
- {%- if subnet.failover_name %}
+ {% endif %}
+ {% endfor %}
+ {% if subnet.failover_name %}
pool {
failover peer "{{ subnet.failover_name }}";
deny dynamic bootp clients;
- {%- for range in subnet.range %}
+ {% for range in subnet.range %}
range {{ range.start }} {{ range.stop }};
- {%- endfor %}
+ {% endfor %}
}
- {%- else %}
- {%- for range in subnet.range %}
+ {% else %}
+ {% for range in subnet.range %}
range {{ range.start }} {{ range.stop }};
- {%- endfor %}
- {%- endif %}
+ {% endfor %}
+ {% endif %}
}
- {%- endfor %}
+ {% endfor %}
on commit {
set shared-networkname = "{{ network.name }}";
- {% if hostfile_update -%}
+ {% 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 %}
}
}
-{%- endif %}
+{% endif %}
{% endfor %}
diff --git a/data/templates/dhcpv6-relay/config.tmpl b/data/templates/dhcpv6-relay/config.tmpl
deleted file mode 100644
index 55035ae6c..000000000
--- a/data/templates/dhcpv6-relay/config.tmpl
+++ /dev/null
@@ -1,4 +0,0 @@
-### Autogenerated by dhcpv6_relay.py ###
-
-# Defaults for isc-dhcp-relay6.service
-OPTIONS="-l {{ listen_addr | join(' -l ') }} -u {{ upstream_addr | join(' -u ') }} {{ options | join(' ') }}"
diff --git a/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl b/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl
index bdeea71da..aa6d7fb5d 100644
--- a/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl
+++ b/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl
@@ -4,87 +4,87 @@
# https://www.isc.org/wp-content/uploads/2017/08/dhcp43options.html
log-facility local7;
-{%- if preference %}
+{% if preference %}
option dhcp6.preference {{ preference }};
-{%- endif %}
+{% endif %}
# Shared network configration(s)
{% for network in shared_network %}
-{%- if not network.disabled -%}
+{% if not network.disabled %}
shared-network {{ network.name }} {
- {%- if network.common.info_refresh_time %}
+ {% if network.common.info_refresh_time %}
option dhcp6.info-refresh-time {{ network.common.info_refresh_time }};
- {%- endif %}
- {%- if network.common.domain_search %}
+ {% endif %}
+ {% if network.common.domain_search %}
option dhcp6.domain-search "{{ network.common.domain_search | join('", "') }}";
- {%- endif %}
- {%- if network.common.dns_server %}
+ {% endif %}
+ {% if network.common.dns_server %}
option dhcp6.name-servers {{ network.common.dns_server | join(', ') }};
- {%- endif %}
- {%- for subnet in network.subnet %}
+ {% endif %}
+ {% for subnet in network.subnet %}
subnet6 {{ subnet.network }} {
- {%- for range in subnet.range6_prefix %}
+ {% for range in subnet.range6_prefix %}
range6 {{ range.prefix }}{{ " temporary" if range.temporary }};
- {%- endfor %}
- {%- for range in subnet.range6 %}
+ {% endfor %}
+ {% for range in subnet.range6 %}
range6 {{ range.start }} {{ range.stop }};
- {%- endfor %}
- {%- if subnet.domain_search %}
+ {% endfor %}
+ {% if subnet.domain_search %}
option dhcp6.domain-search "{{ subnet.domain_search | join('", "') }}";
- {%- endif %}
- {%- if subnet.lease_def %}
+ {% endif %}
+ {% if subnet.lease_def %}
default-lease-time {{ subnet.lease_def }};
- {%- endif %}
- {%- if subnet.lease_max %}
+ {% endif %}
+ {% if subnet.lease_max %}
max-lease-time {{ subnet.lease_max }};
- {%- endif %}
- {%- if subnet.lease_min %}
+ {% endif %}
+ {% if subnet.lease_min %}
min-lease-time {{ subnet.lease_min }};
- {%- endif %}
- {%- if subnet.dns_server %}
+ {% endif %}
+ {% if subnet.dns_server %}
option dhcp6.name-servers {{ subnet.dns_server | join(', ') }};
- {%- endif %}
- {%- if subnet.nis_domain %}
+ {% endif %}
+ {% if subnet.nis_domain %}
option dhcp6.nis-domain-name "{{ subnet.nis_domain }}";
- {%- endif %}
- {%- if subnet.nis_server %}
+ {% endif %}
+ {% if subnet.nis_server %}
option dhcp6.nis-servers {{ subnet.nis_server | join(', ') }};
- {%- endif %}
- {%- if subnet.nisp_domain %}
+ {% endif %}
+ {% if subnet.nisp_domain %}
option dhcp6.nisp-domain-name "{{ subnet.nisp_domain }}";
- {%- endif %}
- {%- if subnet.nisp_server %}
+ {% endif %}
+ {% if subnet.nisp_server %}
option dhcp6.nisp-servers {{ subnet.nisp_server | join(', ') }};
- {%- endif %}
- {%- if subnet.sip_address %}
+ {% endif %}
+ {% if subnet.sip_address %}
option dhcp6.sip-servers-addresses {{ subnet.sip_address | join(', ') }};
- {%- endif %}
- {%- if subnet.sip_hostname %}
+ {% endif %}
+ {% if subnet.sip_hostname %}
option dhcp6.sip-servers-names "{{ subnet.sip_hostname | join('", "') }}";
- {%- endif %}
- {%- if subnet.sntp_server %}
+ {% endif %}
+ {% if subnet.sntp_server %}
option dhcp6.sntp-servers {{ subnet.sntp_server | join(', ') }};
- {%- endif %}
- {%- for prefix in subnet.prefix_delegation %}
+ {% endif %}
+ {% for prefix in subnet.prefix_delegation %}
prefix6 {{ prefix.start }} {{ prefix.stop }} /{{ prefix.length }};
- {%- endfor %}
- {%- for host in subnet.static_mapping %}
- {% if not host.disabled -%}
+ {% endfor %}
+ {% for host in subnet.static_mapping %}
+ {% if not host.disabled %}
host {{ network.name }}_{{ host.name }} {
- {%- if host.client_identifier %}
+ {% if host.client_identifier %}
host-identifier option dhcp6.client-id {{ host.client_identifier }};
- {%- endif %}
- {%- if host.ipv6_address %}
+ {% endif %}
+ {% if host.ipv6_address %}
fixed-address6 {{ host.ipv6_address }};
- {%- endif %}
+ {% endif %}
}
- {%- endif %}
- {%- endfor %}
+ {% endif %}
+ {% endfor %}
}
- {%- endfor %}
+ {% endfor %}
on commit {
set shared-networkname = "{{ network.name }}";
}
}
-{%- endif %}
+{% endif %}
{% endfor %}
diff --git a/data/templates/firewall/nftables-nat.tmpl b/data/templates/firewall/nftables-nat.tmpl
index 286c21859..8769c2384 100644
--- a/data/templates/firewall/nftables-nat.tmpl
+++ b/data/templates/firewall/nftables-nat.tmpl
@@ -1,161 +1,143 @@
#!/usr/sbin/nft -f
-# Start with clean NAT table
-flush table nat
-
-{% if helper_functions == 'remove' %}
-{# NAT if going to be disabled - remove rules and targets from nftables #}
-
-{% set base_command = "delete rule ip raw" %}
-{{ base_command }} PREROUTING handle {{ pre_ct_ignore }}
-{{ base_command }} OUTPUT handle {{ out_ct_ignore }}
-{{ base_command }} PREROUTING handle {{ pre_ct_conntrack }}
-{{ base_command }} OUTPUT handle {{ out_ct_conntrack }}
-
-delete chain ip raw NAT_CONNTRACK
-
-{% elif helper_functions == 'add' %}
-{# NAT if enabled - add targets to nftables #}
-add chain ip raw NAT_CONNTRACK
-add rule ip raw NAT_CONNTRACK counter accept
-
-{% set base_command = "add rule ip raw" %}
-
-{{ base_command }} PREROUTING position {{ pre_ct_ignore }} counter jump VYATTA_CT_HELPER
-{{ base_command }} OUTPUT position {{ out_ct_ignore }} counter jump VYATTA_CT_HELPER
-{{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK
-{{ base_command }} OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK
-{% endif %}
-
-{% macro nat_rule(rule, chain) %}
+{% macro nat_rule(rule, config, chain) %}
{% set comment = "" %}
{% set base_log = "" %}
-
-{% set src_addr = "ip saddr " + rule.source_address if rule.source_address %}
-{% set dst_addr = "ip daddr " + rule.dest_address if rule.dest_address %}
-
+{% set src_addr = "ip saddr " + config.source.address if config.source is defined and config.source.address is defined and config.source.address is not none %}
+{% set dst_addr = "ip daddr " + config.destination.address if config.destination is defined and config.destination.address is defined and config.destination.address is not none %}
{# negated port groups need special treatment, move != in front of { } group #}
-{% if rule.source_port.startswith('!=') %}
-{% set src_port = "sport != { " + rule.source_port.replace('!=','') +" }" if rule.source_port %}
+{% if config.source is defined and config.source.port is defined and config.source.port is not none and config.source.port.startswith('!=') %}
+{% set src_port = "sport != { " + config.source.port.replace('!=','') +" }" %}
{% else %}
-{% set src_port = "sport { " + rule.source_port +" }" if rule.source_port %}
+{% set src_port = "sport { " + config.source.port +" }" if config.source is defined and config.source.port is defined and config.source.port is not none %}
{% endif %}
-
{# negated port groups need special treatment, move != in front of { } group #}
-{% if rule.dest_port.startswith('!=') %}
-{% set dst_port = "dport != { " + rule.dest_port.replace('!=','') +" }" if rule.dest_port %}
+{% if config.destination is defined and config.destination.port is defined and config.destination.port is not none and config.destination.port.startswith('!=') %}
+{% set dst_port = "dport != { " + config.destination.port.replace('!=','') +" }" %}
{% else %}
-{% set dst_port = "dport { " + rule.dest_port +" }" if rule.dest_port %}
+{% set dst_port = "dport { " + config.destination.port +" }" if config.destination is defined and config.destination.port is defined and config.destination.port is not none %}
{% endif %}
-
{% if chain == "PREROUTING" %}
-{% set comment = "DST-NAT-" + rule.number %}
-{% set base_log = "[NAT-DST-" + rule.number %}
-{% set interface = " iifname \"" + rule.interface_in + "\"" if rule.interface_in is defined and rule.interface_in != 'any' else '' %}
-{% set trns_addr = "dnat to " + rule.translation_address %}
-
+{% set comment = "DST-NAT-" + rule %}
+{% set base_log = "[NAT-DST-" + rule %}
+{% set interface = " iifname \"" + config.inbound_interface + "\"" if config.inbound_interface is defined and config.inbound_interface != 'any' else '' %}
+{% set trns_addr = "dnat to " + config.translation.address if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
{% elif chain == "POSTROUTING" %}
-{% set comment = "SRC-NAT-" + rule.number %}
-{% set base_log = "[NAT-SRC-" + rule.number %}
-{% set interface = " oifname \"" + rule.interface_out + "\"" if rule.interface_out is defined and rule.interface_out != 'any' else '' %}
-{% if rule.translation_address == 'masquerade' %}
-{% set trns_addr = rule.translation_address %}
-{% if rule.translation_port %}
+{% set comment = "SRC-NAT-" + rule %}
+{% set base_log = "[NAT-SRC-" + rule %}
+{% set interface = " oifname \"" + config.outbound_interface + "\"" if config.outbound_interface is defined and config.outbound_interface != 'any' else '' %}
+{% if config.translation is defined and config.translation.address is defined and config.translation.address == 'masquerade' %}
+{% set trns_addr = config.translation.address %}
+{% if config.translation.port is defined and config.translation.port is not none %}
{% set trns_addr = trns_addr + " to " %}
{% endif %}
{% else %}
-{% set trns_addr = "snat to " + rule.translation_address %}
+{% set trns_addr = "snat to " + config.translation.address if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
{% endif %}
{% endif %}
-{% set trns_port = ":" + rule.translation_port if rule.translation_port %}
-
-{% if rule.protocol == "tcp_udp" %}
+{% set trns_port = ":" + config.translation.port if config.translation is defined and config.translation.port is defined and config.translation.port is not none %}
+{# protocol has a default value thus it is always present #}
+{% if config.protocol == "tcp_udp" %}
{% set protocol = "tcp" %}
{% set comment = comment + " tcp_udp" %}
{% else %}
-{% set protocol = rule.protocol %}
+{% set protocol = config.protocol %}
{% endif %}
-
-{% if rule.log %}
-{% if rule.exclude %}
+{% if config.log is defined %}
+{% if config.exclude is defined %}
{% set log = base_log + "-EXCL]" %}
-{% elif rule.translation_address == 'masquerade' %}
+{% elif config.translation is defined and config.translation.address is defined and config.translation.address == 'masquerade' %}
{% set log = base_log + "-MASQ]" %}
{% else %}
{% set log = base_log + "]" %}
{% endif %}
{% endif %}
-
-{% if rule.exclude %}
+{% if config.exclude is defined %}
{# rule has been marked as "exclude" thus we simply return here #}
{% set trns_addr = "return" %}
{% set trns_port = "" %}
{% endif %}
-
{% set output = "add rule ip nat " + chain + interface %}
-
{% if protocol != "all" %}
{% set output = output + " ip protocol " + protocol %}
{% endif %}
-
{% if src_addr %}
{% set output = output + " " + src_addr %}
{% endif %}
{% if src_port %}
{% set output = output + " " + protocol + " " + src_port %}
{% endif %}
-
{% if dst_addr %}
{% set output = output + " " + dst_addr %}
{% endif %}
{% if dst_port %}
{% set output = output + " " + protocol + " " + dst_port %}
{% endif %}
-
{# Count packets #}
{% set output = output + " counter" %}
-
{# Special handling of log option, we must repeat the entire rule before the #}
{# NAT translation options are added, this is essential #}
{% if log %}
{% set log_output = output + " log prefix \"" + log + "\" comment \"" + comment + "\"" %}
{% endif %}
-
{% if trns_addr %}
{% set output = output + " " + trns_addr %}
{% endif %}
-
{% if trns_port %}
{# Do not add a whitespace here, translation port must be directly added after IP address #}
{# e.g. 192.0.2.10:3389 #}
{% set output = output + trns_port %}
{% endif %}
-
{% if comment %}
{% set output = output + " comment \"" + comment + "\"" %}
{% endif %}
-
{{ log_output if log_output }}
{{ output }}
-
{# Special handling if protocol is tcp_udp, we must repeat the entire rule with udp as protocol #}
-{% if rule.protocol == "tcp_udp" %}
+{% if config.protocol == "tcp_udp" %}
{# Beware of trailing whitespace, without it the comment tcp_udp will be changed to udp_udp #}
{{ log_output | replace("tcp ", "udp ") if log_output }}
{{ output | replace("tcp ", "udp ") }}
{% endif %}
{% endmacro %}
+# Start with clean NAT table
+flush table nat
+{% if helper_functions == 'remove' %}
+{# NAT if going to be disabled - remove rules and targets from nftables #}
+{% set base_command = "delete rule ip raw" %}
+{{ base_command }} PREROUTING handle {{ pre_ct_ignore }}
+{{ base_command }} OUTPUT handle {{ out_ct_ignore }}
+{{ base_command }} PREROUTING handle {{ pre_ct_conntrack }}
+{{ base_command }} OUTPUT handle {{ out_ct_conntrack }}
+
+delete chain ip raw NAT_CONNTRACK
+
+{% elif helper_functions == 'add' %}
+{# NAT if enabled - add targets to nftables #}
+add chain ip raw NAT_CONNTRACK
+add rule ip raw NAT_CONNTRACK counter accept
+{% set base_command = "add rule ip raw" %}
+{{ base_command }} PREROUTING position {{ pre_ct_ignore }} counter jump VYATTA_CT_HELPER
+{{ base_command }} OUTPUT position {{ out_ct_ignore }} counter jump VYATTA_CT_HELPER
+{{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK
+{{ base_command }} OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK
+{% endif %}
+
#
# Destination NAT rules build up here
#
-{% for rule in destination if not rule.disabled -%}
-{{ nat_rule(rule, 'PREROUTING') }}
-{% endfor %}
+{% if destination is defined and destination.rule is defined and destination.rule is not none %}
+{% for rule, config in destination.rule.items() if config.disable is not defined %}
+{{ nat_rule(rule, config, 'PREROUTING') }}
+{% endfor %}
+{% endif %}
#
# Source NAT rules build up here
#
-{% for rule in source if not rule.disabled -%}
-{{ nat_rule(rule, 'POSTROUTING') }}
-{% endfor %}
+{% if source is defined and source.rule is defined and source.rule is not none %}
+{% for rule, config in source.rule.items() if config.disable is not defined %}
+{{ nat_rule(rule, config, 'POSTROUTING') }}
+{% endfor %}
+{% endif %}
diff --git a/data/templates/frr/bfd.frr.tmpl b/data/templates/frr/bfd.frr.tmpl
index 7df4bfd01..95a29e06a 100644
--- a/data/templates/frr/bfd.frr.tmpl
+++ b/data/templates/frr/bfd.frr.tmpl
@@ -1,10 +1,10 @@
!
bfd
-{% for peer in old_peers -%}
+{% 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 -%}
+{% endfor %}
!
-{% for peer in new_peers -%}
+{% 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 }}
@@ -12,5 +12,5 @@ bfd
{% 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 -%}
+{% endfor %}
!
diff --git a/data/templates/frr/bgp.frr.tmpl b/data/templates/frr/bgp.frr.tmpl
index d0857ac2c..86e1aa366 100644
--- a/data/templates/frr/bgp.frr.tmpl
+++ b/data/templates/frr/bgp.frr.tmpl
@@ -1,1016 +1,287 @@
-{% set conf_bgp = nbgp -%}
-{% for asn in nbgp -%}
-!
-router bgp {{ asn }}
- no bgp default ipv4-unicast
-
-{#- set 'conf_bgp[asn].parameters' as bgp_params #}
-{%- set bgp_params = conf_bgp[asn].parameters %}
-{%- set bgp_afi = conf_bgp[asn].address_family %}
-
-{#- START Global ASN address-family section; set protocol bgp xxx address-family #}
-{%- if 'address_family' in conf_bgp[asn] %}
-{%- for type in bgp_afi %}
-{%- if type == "ipv4_unicast" %}
+{### MACRO definition for recurring peer patter, this can be either fed by a ###}
+{### peer-group or an individual BGP neighbor ###}
+{% macro bgp_neighbor(neighbor, config, peer_group=false) %}
+{% if peer_group == true %}
+ neighbor {{ neighbor }} peer-group
+{% elif config.peer_group is defined and config.peer_group is not none %}
+ neighbor {{ neighbor }} peer-group {{ config.peer_group }}
+{% endif %}
+{% if config.remote_as is defined and config.remote_as is not none %}
+ neighbor {{ neighbor }} remote-as {{ config.remote_as }}
+{% endif %}
+{% if config.bfd is defined %}
+ neighbor {{ neighbor }} bfd
+{% endif %}
+{% if config.capability is defined and config.capability is not none %}
+{% if config.capability.dynamic is defined %}
+ neighbor {{ neighbor }} capability dynamic
+{% endif %}
+{% if config.capability.extended_nexthop is defined %}
+ neighbor {{ neighbor }} capability extended-nexthop
+{% endif %}
+{% endif %}
+{% if config.description is defined and config.description is not none %}
+ neighbor {{ neighbor }} description {{ config.description }}
+{% endif %}
+{% if config.disable_capability_negotiation is defined %}
+ neighbor {{ neighbor }} disable-capability-negotiation
+{% endif %}
+{% if config.ebgp_multihop is defined and config.ebgp_multihop is not none %}
+ neighbor {{ neighbor }} ebgp-multihop {{ config.ebgp_multihop }}
+{% endif %}
+{% if config.local_as is defined and config.local_as is not none %}
+{% for local_asn in config.local_as %}
+ neighbor {{ neighbor }} local-as {{ local_asn }} {{ 'no-prepend' if config.local_as[local_asn].no_prepend is defined }}
+{% endfor %}
+{% endif %}
+{% if config.override_capability is defined %}
+ neighbor {{ neighbor }} override-capability
+{% endif %}
+{% if config.passive is defined %}
+ neighbor {{ neighbor }} passive
+{% endif %}
+{% if config.password is defined and config.password is not none %}
+ neighbor {{ neighbor }} password {{ config.password }}
+{% endif %}
+{% if config.shutdown is defined %}
+ neighbor {{ neighbor }} shutdown
+{% endif %}
+{% if config.ttl_security is defined and config.ttl_security.hops is defined and config.ttl_security.hops is not none %}
+ neighbor {{ neighbor }} ttl-security hops {{ config.ttl_security.hops }}
+{% endif %}
+{% if config.update_source is defined and config.update_source is not none %}
+ neighbor {{ neighbor }} update-source {{ config.update_source }}
+{% endif %}
!
+{% if config.address_family is defined and config.address_family is not none %}
+{% for af in config.address_family %}
+{% if af == 'ipv4_unicast' %}
address-family ipv4 unicast
-{%- if 'aggregate_address' in bgp_afi[type] %}
-{%- for ip in bgp_afi[type].aggregate_address %}
-{%- if ( ('as_set' in bgp_afi[type].aggregate_address[ip]) and ('summary_only' in bgp_afi[type].aggregate_address[ip] ) ) %}
- aggregate-address {{ ip }} as-set summary-only
-{%- elif 'as_set' in bgp_afi[type].aggregate_address[ip] %}
- aggregate-address {{ ip }} as-set
-{%- elif 'summary_only' in bgp_afi[type].aggregate_address[ip] %}
- aggregate-address {{ ip }} summary-only
-{%- else %}
- aggregate-address {{ ip }}
-{%- endif %}
-{%- endfor %}
-{%- endif %}
-{#- END aggregate address ipv4 #}
-
-{#- redistribute afi ipv4 #}
-{%- if 'redistribute' in bgp_afi[type] %}
-{%- for protocol in bgp_afi[type].redistribute %}
-{%- if ( ('route_map' in bgp_afi[type].redistribute[protocol]) and ('metric' in bgp_afi[type].redistribute[protocol] ) ) %}
- redistribute {{protocol}} metric {{bgp_afi[type].redistribute[protocol].metric}} route-map {{bgp_afi[type].redistribute[protocol].route_map}}
-{%- elif 'metric' in bgp_afi[type].redistribute[protocol] %}
- redistribute {{protocol}} metric {{bgp_afi[type].redistribute[protocol].metric}}
-{%- elif 'route_map' in bgp_afi[type].redistribute[protocol] %}
- redistribute {{protocol}} route-map {{bgp_afi[type].redistribute[protocol].route_map}}
-{%- elif 'table' in bgp_afi[type].redistribute %}
- redistribute table {{bgp_afi[type].redistribute.table}}
-{%- else %}
- redistribute {{protocol}}
-{%- endif %}
-{%- endfor %}
-{%- endif %}
-{#- END redistribute #}
-
-{%- if 'network' in bgp_afi[type] %}
-{%- for net in bgp_afi[type].network %}
- network {{ net }}
-{%- endfor %}
-{%- endif %}
- exit-address-family
- !
-{%- endif %}
-
-{%- if type == "ipv6_unicast" %}
- !
+{% elif af == 'ipv6_unicast' %}
address-family ipv6 unicast
-{%- if 'aggregate_address' in bgp_afi[type] %}
-{%- for ip in bgp_afi[type].aggregate_address %}
-{%- if ( ('as_set' in bgp_afi[type].aggregate_address[ip]) and ('summary_only' in bgp_afi[type].aggregate_address[ip] ) ) %}
- aggregate-address {{ ip }} as-set summary-only
-{%- elif 'as_set' in bgp_afi[type].aggregate_address[ip] %}
- aggregate-address {{ ip }} as-set
-{%- elif 'summary_only' in bgp_afi[type].aggregate_address[ip] %}
- aggregate-address {{ ip }} summary-only
-{%- else %}
- aggregate-address {{ ip }}
-{%- endif %}
-{%- endfor %}
-{%- endif %}
-{#- END aggregate address ipv6 #}
-
-{#- redistribute afi ipv6 #}
-{%- if 'redistribute' in bgp_afi[type] %}
-{%- for protocol in bgp_afi[type].redistribute %}
-{%- if ( ('route_map' in bgp_afi[type].redistribute[protocol]) and ('metric' in bgp_afi[type].redistribute[protocol] ) ) %}
- redistribute {{protocol}} metric {{bgp_afi[type].redistribute[protocol].metric}} route-map {{bgp_afi[type].redistribute[protocol].route_map}}
-{%- elif 'metric' in bgp_afi[type].redistribute[protocol] %}
- redistribute {{protocol}} metric {{bgp_afi[type].redistribute[protocol].metric}}
-{%- elif 'route_map' in bgp_afi[type].redistribute[protocol] %}
- redistribute {{protocol}} route-map {{bgp_afi[type].redistribute[protocol].route_map}}
-{%- elif 'table' in bgp_afi[type].redistribute %}
- redistribute table {{bgp_afi[type].redistribute.table}}
-{%- else %}
- redistribute {{protocol}}
-{%- endif %}
-{%- endfor %}
-{%- endif %}
-{#- END redistribute #}
-
-{%- if 'network' in bgp_afi[type] %}
-{%- for net in bgp_afi[type].network %}
- network {{ net }}
-{%- endfor %}
-{%- endif %}
+{% endif %}
+{% if config.address_family[af].allowas_in is defined and config.address_family[af].allowas_in is not none %}
+ neighbor {{ neighbor }} allowas-in {{ config.address_family[af].allowas_in.number if config.address_family[af].allowas_in.number is defined }}
+{% endif %}
+{% if config.address_family[af].remove_private_as is defined %}
+ neighbor {{ neighbor }} remove-private-AS
+{% endif %}
+{% if config.address_family[af].route_reflector_client is defined %}
+ neighbor {{ neighbor }} route-reflector-client
+{% endif %}
+{% if config.address_family[af].weight is defined and config.address_family[af].weight is not none %}
+ neighbor {{ neighbor }} weight {{ config.address_family[af].weight }}
+{% endif %}
+{% if config.address_family[af].attribute_unchanged is defined and config.address_family[af].attribute_unchanged is not none %}
+ neighbor {{ neighbor }} attribute-unchanged {{ 'as-path ' if config.address_family[af].attribute_unchanged.as_path is defined }}{{ 'med ' if config.address_family[af].attribute_unchanged.med is defined }}{{ 'next-hop ' if config.address_family[af].attribute_unchanged.next_hop is defined }}
+{% endif %}
+{% if config.address_family[af].capability is defined and config.address_family[af].capability.orf is defined and config.address_family[af].capability.orf.prefix_list is defined and config.address_family[af].capability.orf.prefix_list is not none %}
+ neighbor {{ neighbor }} capability orf prefix-list {{ config.address_family[af].capability.orf.prefix_list }}
+{% endif %}
+{% if config.address_family[af].default_originate is defined %}
+ neighbor {{ neighbor }} default-originate {{ 'route-map ' + config.address_family[af].default_originate.route_map if config.address_family[af].default_originate.route_map is defined }}
+{% endif %}
+{% if config.address_family[af].distribute_list is defined and config.address_family[af].distribute_list is not none %}
+{% if config.address_family[af].distribute_list.export is defined and config.address_family[af].distribute_list.export is not none %}
+ neighbor {{ neighbor }} distribute-list {{ config.address_family[af].distribute_list.export }} out
+{% elif config.address_family[af].distribute_list.import is defined and config.address_family[af].distribute_list.import is not none %}
+ neighbor {{ neighbor }} distribute-list {{ config.address_family[af].distribute_list.export }} in
+{% endif %}
+{% endif %}
+{% if config.address_family[af].filter_list is defined and config.address_family[af].filter_list is not none %}
+{% if config.address_family[af].filter_list.export is defined and config.address_family[af].filter_list.export is not none %}
+ neighbor {{ neighbor }} filter-list {{ config.address_family[af].filter_list.export }} out
+{% elif config.address_family[af].filter_list.import is defined and config.address_family[af].filter_list.import is not none %}
+ neighbor {{ neighbor }} filter-list {{ config.address_family[af].filter_list.import }} in
+{% endif %}
+{% endif %}
+{% if config.address_family[af].maximum_prefix is defined and config.address_family[af].maximum_prefix is not none %}
+ neighbor {{ neighbor }} maximum-prefix {{ config.address_family[af].maximum_prefix }}
+{% endif %}
+{% if config.address_family[af].nexthop_self is defined %}
+{# https://phabricator.vyos.net/T1817 #}
+ neighbor {{ neighbor }} next-hop-self {{ 'force' if config.address_family[af].nexthop_self.force is defined }}
+{% endif %}
+{% if config.address_family[af].route_server_client is defined %}
+ neighbor {{ neighbor }} route-server-client
+{% endif %}
+{% if config.address_family[af].route_map is defined and config.address_family[af].route_map is not none %}
+{% if config.address_family[af].route_map.export is defined and config.address_family[af].route_map.export is not none %}
+ neighbor {{ neighbor }} route-map {{ config.address_family[af].route_map.export }} out
+{% elif config.address_family[af].route_map.import is defined and config.address_family[af].route_map.import is not none %}
+ neighbor {{ neighbor }} route-map {{ config.address_family[af].route_map.import }} in
+{% endif %}
+{% endif %}
+{% if config.address_family[af].prefix_list is defined and config.address_family[af].prefix_list is not none %}
+{% if config.address_family[af].prefix_list.export is defined and config.address_family[af].prefix_list.export is not none %}
+ neighbor {{ neighbor }} route-map {{ config.address_family[af].prefix_list.export }} out
+{% elif config.address_family[af].prefix_list.import is defined and config.address_family[af].prefix_list.import is not none %}
+ neighbor {{ neighbor }} route-map {{ config.address_family[af].prefix_list.export }} in
+{% endif %}
+{% endif %}
+{% if config.address_family[af].soft_reconfiguration is defined and config.address_family[af].soft_reconfiguration.inbound is defined %}
+ neighbor {{ neighbor }} soft-reconfiguration inbound
+{% endif %}
+{% if config.address_family[af].unsuppress_map is defined and config.address_family[af].unsuppress_map is not none %}
+ neighbor {{ neighbor }} unsuppress-map {{ config.address_family[af].unsuppress_map }}
+{% endif %}
+ neighbor {{ neighbor }} activate
exit-address-family
+ !
+{% endfor %}
+{% endif %}
+{% endmacro %}
!
-{%- endif %}
-{%- endfor %}
-{%- endif %}
-{#- END Global ASN address-family section; set protocols bgp 65001 address-family #}
-
-{#- set protocols nbgp xxxx maximum-paths ibgp x, Generated by default for afi_4 #}
-{#- We don't have this parameter in afi_6. But this is supported in the FRR #}
-{%- if 'maximum_paths' in conf_bgp[asn] %}
-{%- if 'ebgp' in conf_bgp[asn].maximum_paths %}
+router bgp {{ asn }}
+ no bgp default ipv4-unicast
+{% if address_family is defined and address_family is not none %}
+{% for af in address_family %}
!
+{% if af == 'ipv4_unicast' %}
address-family ipv4 unicast
- maximum-paths {{ conf_bgp[asn].maximum_paths.ebgp }}
+{% elif af == 'ipv6_unicast' %}
+ address-family ipv6 unicast
+{% endif %}
+{% if address_family[af].aggregate_address is defined and address_family[af].aggregate_address is not none %}
+{% for ip in address_family[af].aggregate_address %}
+ aggregate-address {{ ip }}{{ ' as-set' if address_family[af].aggregate_address[ip].as_set is defined }}{{ ' summary-only' if address_family[af].aggregate_address[ip].summary_only is defined }}
+{% endfor %}
+{% endif %}
+{% if address_family[af].redistribute is defined and address_family[af].redistribute is not none %}
+{% for protocol in address_family[af].redistribute %}
+{% if protocol == 'table' %}
+ redistribute table {{ address_family[af].redistribute[protocol].table }}
+{% else %}
+ redistribute {{ protocol }}{% if address_family[af].redistribute[protocol].metric is defined %} metric {{ address_family[af].redistribute[protocol].metric }}{% endif %}{% if address_family[af].redistribute[protocol].route_map is defined %} route-map {{ address_family[af].redistribute[protocol].route_map }}{% endif %}
+{####### we need this blank line!! #######}
+
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if address_family[af].network is defined and address_family[af].network is not none %}
+{% for network in address_family[af].network %}
+ network {{ network }}{% if address_family[af].network[network].route_map is defined %} route-map {{ address_family[af].network[network].route_map }}{% endif %}{% if address_family[af].network[network].backdoor is defined %} backdoor{% endif %}
+{####### we need this blank line!! #######}
+
+{% endfor %}
+{% endif %}
exit-address-family
+{% endfor %}
+{% endif %}
!
-{%- endif %}
-{%- if 'ibgp' in conf_bgp[asn].maximum_paths %}
+{# set protocols bgp xxxx maximum-paths ibgp x, Generated by default for afi_4 #}
+{# We don't have this parameter in afi_6. But this is supported in FRR #}
+{% if maximum_paths is defined and maximum_paths is not none %}
+{% if maximum_paths.ebgp is defined and maximum_paths.ebgp is not none %}
!
address-family ipv4 unicast
- maximum-paths ibgp {{ conf_bgp[asn].maximum_paths.ibgp }}
+ maximum-paths {{ maximum_paths.ebgp }}
exit-address-family
!
-{%- endif %}
-{%- endif %}
-
-{#- START peer-group; set protocol bgp xxx peer-group #}
-{%- if 'peer_group' in conf_bgp[asn] %}
-{%- for pr_group in conf_bgp[asn].peer_group %}
-{%- set conf_peer_group = conf_bgp[asn].peer_group[pr_group] %}
- neighbor {{pr_group}} peer-group
-
-{#- First parameter for peer-group - remote-as #}
-{%- if 'remote_as' in conf_peer_group %}
- neighbor {{ pr_group }} remote-as {{ conf_peer_group.remote_as }}
-{%- endif %}
-
-{%- if 'bfd' in conf_peer_group %}
- neighbor {{ pr_group }} bfd
-{%- endif %}
-
-{%- if 'capability' in conf_peer_group %}
-{%- if 'dynamic' in conf_peer_group.capability %}
- neighbor {{ pr_group }} capability dynamic
-{%- endif %}
-{%- if 'extended_nexthop' in conf_peer_group.capability %}
- neighbor {{ pr_group }} capability extended-nexthop
-{%- endif %}
-{%- endif %}
-
-{%- if 'description' in conf_peer_group %}
- neighbor {{ pr_group }} description {{ conf_peer_group.description }}
-{%- endif %}
-
-{%- if 'disable_capability_negotiation' in conf_peer_group %}
- neighbor {{ pr_group }} disable-capability-negotiation
-{%- endif %}
-
-{#- https://phabricator.vyos.net/T2844. 'disable-send-community' only for afi #}
-{%- if 'disable_send_community' in conf_peer_group %}
- !
-{%- endif %}
-
-{%- if 'ebgp_multihop' in conf_peer_group %}
- neighbor {{ pr_group }} ebgp-multihop {{conf_peer_group.ebgp_multihop}}
-{%- endif %}
-
-{%- if 'local_as' in conf_peer_group %}
-{%- for loc_asn in conf_peer_group.local_as %}
-{%- if 'no_prepend' in conf_peer_group.local_as[loc_asn] %}
- neighbor {{ pr_group }} local-as {{loc_asn}} no-prepend
-{%- else %}
- neighbor {{ pr_group }} local-as {{loc_asn}}
-{%- endif %}
-{%- endfor %}
-{%- endif %}
-
-{%- if 'override_capability' in conf_peer_group %}
- neighbor {{ pr_group }} override-capability
-{%- endif %}
-
-{%- if 'passive' in conf_peer_group %}
- neighbor {{ pr_group }} passive
-{%- endif %}
-
-{%- if 'password' in conf_peer_group %}
- neighbor {{ pr_group }} password {{ conf_peer_group.password }}
-{%- endif %}
-
-{%- if 'shutdown' in conf_peer_group %}
- neighbor {{ pr_group }} shutdown
-{%- endif %}
-
-{%- if 'ttl_security' in conf_peer_group %}
-{%- if 'hops' in conf_peer_group.ttl_security %}
- neighbor {{ pr_group }} ttl-security hops {{conf_peer_group.ttl_security.hops}}
-{%- endif %}
-{%- endif %}
-
-{%- if 'update_source' in conf_peer_group %}
- neighbor {{ pr_group }} update-source {{ conf_peer_group.update_source }}
-{%- endif %}
-
-{#- START peer-group afi; set protocols bgp xxx peer-group FOO address-family #}
-{%- if 'address_family' in conf_peer_group %}
-{%- for afi in conf_peer_group.address_family %}
-{%- if afi == "ipv4_unicast" %}
+{% endif %}
+{% if maximum_paths.ibgp is defined and maximum_paths.ibgp is not none %}
!
address-family ipv4 unicast
-
-{%- if 'allowas_in' in conf_peer_group.address_family.ipv4_unicast %}
-{%- if 'number' in conf_peer_group.address_family.ipv4_unicast.allowas_in %}
- neighbor {{ pr_group }} allowas-in {{ conf_peer_group.address_family.ipv4_unicast.allowas_in.number }}
-{%- else %}
- neighbor {{ pr_group }} allowas-in
-{%- endif %}
-{%- endif %}
-
-{#- START Single Params for peer-group; set protocols bgp xxx peer-group FOO address-family ipv4-unicast #}
-
-{%- if 'remove_private_as' in conf_peer_group.address_family.ipv4_unicast %}
- neighbor {{ pr_group }} remove-private-AS
-{%- endif %}
-
-{%- if 'route_reflector_client' in conf_peer_group.address_family.ipv4_unicast %}
- neighbor {{ pr_group }} route-reflector-client
-{%- endif %}
-
-{%- if 'weight' in conf_peer_group.address_family.ipv4_unicast %}
- neighbor {{ pr_group }} weight {{ conf_peer_group.address_family.ipv4_unicast.weight }}
-{%- endif %}
-{#- END single params for peer-group #}
-
-{%- if 'attribute_unchanged' in conf_peer_group.address_family.ipv4_unicast %}
-{%- if ( ('as_path' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged) and ('med' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged) ) %}
- neighbor {{ pr_group }} attribute-unchanged as-path med
-{%- elif ( ('as_path' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged) and ('next_hop' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged) ) %}
- neighbor {{ pr_group }} attribute-unchanged as-path next-hop
-{%- elif ( ('med' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged) and ('next_hop' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged) ) %}
- neighbor {{ pr_group }} attribute-unchanged med next-hop
-{%- elif 'as_path' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged %}
- neighbor {{ pr_group }} attribute-unchanged as-path
-{%- elif 'med' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged %}
- neighbor {{ pr_group }} attribute-unchanged med
-{%- elif 'next_hop' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged %}
- neighbor {{ pr_group }} attribute-unchanged next-hop
-{%- else %}
- neighbor {{ pr_group }} attribute-unchanged as-path next-hop med
-{%- endif %}
-{%- endif %}
-{#- END attribute-unchanged #}
-
-{%- if 'capability' in conf_peer_group.address_family.ipv4_unicast %}
-{%- if 'orf' in conf_peer_group.address_family.ipv4_unicast.capability %}
-{%- if 'receive' in conf_peer_group.address_family.ipv4_unicast.capability.orf.prefix_list %}
- neighbor {{ pr_group }} capability orf prefix-list receive
-{%- endif %}
-{%- if 'send' in conf_peer_group.address_family.ipv4_unicast.capability.orf.prefix_list %}
- neighbor {{ pr_group }} capability orf prefix-list send
-{%- endif %}
-{%- endif %}
-{%- endif %}
-
-{%- if 'default_originate' in conf_peer_group.address_family.ipv4_unicast %}
-{%- if 'route_map' in conf_peer_group.address_family.ipv4_unicast.default_originate %}
- neighbor {{ pr_group }} default-originate route-map {{ conf_peer_group.address_family.ipv4_unicast.default_originate.route_map }}
-{%- else %}
- neighbor {{ pr_group }} default-originate
-{%- endif %}
-{%- endif %}
-
-{%- if 'distribute_list' in conf_peer_group.address_family.ipv4_unicast %}
-{%- if 'export' in conf_peer_group.address_family.ipv4_unicast.distribute_list %}
- neighbor {{ pr_group }} distribute-list {{conf_peer_group.address_family.ipv4_unicast.distribute_list.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer_group.address_family.ipv4_unicast.distribute_list %}
- neighbor {{ pr_group }} distribute-list {{conf_peer_group.address_family.ipv4_unicast.distribute_list.import}} in
-{%- endif %}
-{%- endif %}
-
-{%- if 'filter_list' in conf_peer_group.address_family.ipv4_unicast %}
-{%- if 'export' in conf_peer_group.address_family.ipv4_unicast.filter_list %}
- neighbor {{ pr_group }} filter-list {{conf_peer_group.address_family.ipv4_unicast.filter_list.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer_group.address_family.ipv4_unicast.filter_list %}
- neighbor {{ pr_group }} filter-list {{conf_peer_group.address_family.ipv4_unicast.filter_list.import}} in
-{%- endif %}
-{%- endif %}
-
-{%- if 'maximum_prefix' in conf_peer_group.address_family.ipv4_unicast %}
- neighbor {{ pr_group }} maximum-prefix {{ conf_peer_group.address_family.ipv4_unicast.maximum_prefix }}
-{%- endif %}
-
-{#- https://phabricator.vyos.net/T1817 #}
-{%- if 'nexthop_self' in conf_peer_group.address_family.ipv4_unicast %}
-{%- if 'force' in conf_peer_group.address_family.ipv4_unicast.nexthop_self %}
- neighbor {{ pr_group }} next-hop-self force
- neighbor {{ pr_group }} next-hop-self
-{%- else %}
- neighbor {{ pr_group }} next-hop-self
-{%- endif %}
-{%- endif %}
-
-{%- if 'route_server_client' in conf_peer_group.address_family.ipv4_unicast %}
- neighbor {{ pr_group }} route-server-client
-{%- endif %}
-
-{%- if 'route_map' in conf_peer_group.address_family.ipv4_unicast %}
-{%- if 'export' in conf_peer_group.address_family.ipv4_unicast.route_map %}
- neighbor {{ pr_group }} route-map {{conf_peer_group.address_family.ipv4_unicast.route_map.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer_group.address_family.ipv4_unicast.route_map %}
- neighbor {{ pr_group }} route-map {{conf_peer_group.address_family.ipv4_unicast.route_map.import}} in
-{%- endif %}
-{%- endif %}
-{%- if 'prefix_list' in conf_peer_group.address_family.ipv4_unicast %}
-{%- if 'export' in conf_peer_group.address_family.ipv4_unicast.prefix_list %}
- neighbor {{ pr_group }} prefix-list {{conf_peer_group.address_family.ipv4_unicast.prefix_list.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer_group.address_family.ipv4_unicast.prefix_list %}
- neighbor {{ pr_group }} prefix-list {{conf_peer_group.address_family.ipv4_unicast.prefix_list.import}} in
-{%- endif %}
-{%- endif %}
-
-{%- if 'soft_reconfiguration' in conf_peer_group.address_family.ipv4_unicast %}
-{%- if 'inbound' is defined %}
- neighbor {{ pr_group }} soft-reconfiguration inbound
-{%- endif %}
-{%- endif %}
-
-{%- if 'unsuppress_map' in conf_peer_group.address_family.ipv4_unicast %}
- neighbor {{ pr_group }} unsuppress-map {{conf_peer_group.address_family.ipv4_unicast.unsuppress_map}}
-{%- endif %}
- neighbor {{ pr_group }} activate
+ maximum-paths ibgp {{ maximum_paths.ibgp }}
exit-address-family
!
-{%- endif %}
-
-{%- if afi == "ipv6_unicast" %}
+{% endif %}
+{% endif %}
!
- address-family ipv6 unicast
-
-{%- if 'allowas_in' in conf_peer_group.address_family.ipv6_unicast %}
-{%- if 'number' in conf_peer_group.address_family.ipv6_unicast.allowas_in %}
- neighbor {{ pr_group }} allowas-in {{ conf_peer_group.address_family.ipv6_unicast.allowas_in.number }}
-{%- else %}
- neighbor {{ pr_group }} allowas-in
-{%- endif %}
-{%- endif %}
-
-{#- START Single Params for peer-group afi6; set protocols bgp xxx peer-group FOO address-family ipv6-unicast #}
-{%- if 'remove_private_as' in conf_peer_group.address_family.ipv6_unicast %}
- neighbor {{ pr_group }} remove-private-AS
-{%- endif %}
-
-{%- if 'route_reflector_client' in conf_peer_group.address_family.ipv6_unicast %}
- neighbor {{ pr_group }} route-reflector-client
-{%- endif %}
-
-{%- if 'weight' in conf_peer_group.address_family.ipv6_unicast %}
- neighbor {{ pr_group }} weight {{ conf_peer_group.address_family.ipv6_unicast.weight }}
-{%- endif %}
-{#- END single params for peer-group afi6 #}
-
-{%- if 'attribute_unchanged' in conf_peer_group.address_family.ipv6_unicast %}
-{%- if ( ('as_path' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged) and ('med' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged) ) %}
- neighbor {{ pr_group }} attribute-unchanged as-path med
-{%- elif ( ('as_path' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged) and ('next_hop' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged) ) %}
- neighbor {{ pr_group }} attribute-unchanged as-path next-hop
-{%- elif ( ('med' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged) and ('next_hop' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged) ) %}
- neighbor {{ pr_group }} attribute-unchanged med next-hop
-{%- elif 'as_path' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged %}
- neighbor {{ pr_group }} attribute-unchanged as-path
-{%- elif 'med' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged %}
- neighbor {{ pr_group }} attribute-unchanged med
-{%- elif 'next_hop' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged %}
- neighbor {{ pr_group }} attribute-unchanged next-hop
-{%- else %}
- neighbor {{ pr_group }} attribute-unchanged as-path next-hop med
-{%- endif %}
-{%- endif %}
-{#- END attribute-unchanged ipv6 #}
-
-{%- if 'capability' in conf_peer_group.address_family.ipv6_unicast %}
-{%- if 'dynamic' in conf_peer_group.address_family.ipv6_unicast.capability %}
-{#- exit from afi ipv6 unicast because 'dynamic' its a global parameter for peer-group in afi6. Other checks are ongoing in afi6. Also related T3037 #}
- exit-address-family
- neighbor {{ pr_group }} capability dynamic
- address-family ipv6 unicast
-{%- endif %}
-{%- if 'orf' in conf_peer_group.address_family.ipv6_unicast.capability %}
-{%- if 'receive' in conf_peer_group.address_family.ipv6_unicast.capability.orf.prefix_list %}
- neighbor {{ pr_group }} capability orf prefix-list receive
-{%- endif %}
-{%- if 'send' in conf_peer_group.address_family.ipv6_unicast.capability.orf.prefix_list %}
- neighbor {{ pr_group }} capability orf prefix-list send
-{%- endif %}
-{%- endif %}
-{%- endif %}
-
-{%- if 'default_originate' in conf_peer_group.address_family.ipv6_unicast %}
-{%- if 'route_map' in conf_peer_group.address_family.ipv6_unicast.default_originate %}
- neighbor {{ pr_group }} default-originate route-map {{ conf_peer_group.address_family.ipv6_unicast.default_originate.route_map }}
-{%- else %}
- neighbor {{ pr_group }} default-originate
-{%- endif %}
-{%- endif %}
-
-{%- if 'distribute_list' in conf_peer_group.address_family.ipv6_unicast %}
-{%- if 'export' in conf_peer_group.address_family.ipv6_unicast.distribute_list %}
- neighbor {{ pr_group }} distribute-list {{conf_peer_group.address_family.ipv6_unicast.distribute_list.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer_group.address_family.ipv6_unicast.distribute_list %}
- neighbor {{ pr_group }} distribute-list {{conf_peer_group.address_family.ipv6_unicast.distribute_list.import}} in
-{%- endif %}
-{%- endif %}
-
-{%- if 'filter_list' in conf_peer_group.address_family.ipv6_unicast %}
-{%- if 'export' in conf_peer_group.address_family.ipv6_unicast.filter_list %}
- neighbor {{ pr_group }} filter-list {{conf_peer_group.address_family.ipv6_unicast.filter_list.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer_group.address_family.ipv6_unicast.filter_list %}
- neighbor {{ pr_group }} filter-list {{conf_peer_group.address_family.ipv6_unicast.filter_list.import}} in
-{%- endif %}
-{%- endif %}
-
-{%- if 'maximum_prefix' in conf_peer_group.address_family.ipv6_unicast %}
- neighbor {{ pr_group }} maximum-prefix {{ conf_peer_group.address_family.ipv6_unicast.maximum_prefix }}
-{%- endif %}
-
-{#- https://phabricator.vyos.net/T1817 #}
-{%- if 'nexthop_self' in conf_peer_group.address_family.ipv6_unicast %}
-{%- if 'force' in conf_peer_group.address_family.ipv6_unicast.nexthop_self %}
- neighbor {{ pr_group }} next-hop-self force
- neighbor {{ pr_group }} next-hop-self
-{%- else %}
- neighbor {{ pr_group }} next-hop-self
-{%- endif %}
-{%- endif %}
-
-{%- if 'route_server_client' in conf_peer_group.address_family.ipv6_unicast %}
- neighbor {{ pr_group }} route-server-client
-{%- endif %}
-
-{%- if 'route_map' in conf_peer_group.address_family.ipv6_unicast %}
-{%- if 'export' in conf_peer_group.address_family.ipv6_unicast.route_map %}
- neighbor {{ pr_group }} route-map {{conf_peer_group.address_family.ipv6_unicast.route_map.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer_group.address_family.ipv6_unicast.route_map %}
- neighbor {{ pr_group }} route-map {{conf_peer_group.address_family.ipv6_unicast.route_map.import}} in
-{%- endif %}
-{%- endif %}
-{%- if 'prefix_list' in conf_peer_group.address_family.ipv6_unicast %}
-{%- if 'export' in conf_peer_group.address_family.ipv6_unicast.prefix_list %}
- neighbor {{ pr_group }} prefix-list {{conf_peer_group.address_family.ipv6_unicast.prefix_list.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer_group.address_family.ipv6_unicast.prefix_list %}
- neighbor {{ pr_group }} prefix-list {{conf_peer_group.address_family.ipv6_unicast.prefix_list.import}} in
-{%- endif %}
-{%- endif %}
-
-{%- if 'soft_reconfiguration' in conf_peer_group.address_family.ipv6_unicast %}
-{%- if 'inbound' is defined %}
- neighbor {{ pr_group }} soft-reconfiguration inbound
-{%- endif %}
-{%- endif %}
-
-{%- if 'unsuppress_map' in conf_peer_group.address_family.ipv6_unicast %}
- neighbor {{ pr_group }} unsuppress-map {{conf_peer_group.address_family.ipv6_unicast.unsuppress_map}}
-{%- endif %}
- neighbor {{ pr_group }} activate
- exit-address-family
+{% if peer_group is defined and peer_group is not none %}
+{% for peer, config in peer_group.items() %}
+{{ bgp_neighbor(peer, config, true) }}
+{% endfor %}
+{% endif %}
!
-{%- endif %}
-
-{%- endfor %}
-{%- endif %}
-{#- END peer-group afi; set protocols bgp xxx peer-group FOO address-family #}
-
-{%- endfor %}
-{%- endif %}
-{#- END peer-group; set protocol bgp xxx peer-group #}
-
-{#- START peer section; set protocol bgp xxx neighbor #}
-{%- for peer in conf_bgp[asn].neighbor %}
-{#- set peer-group as conf_peer #}
-{%- set conf_peer = conf_bgp[asn].neighbor[peer] %}
-
-{#- First parameter for peer neighbor - remote-as #}
-{%- if 'remote_as' in conf_peer %}
- neighbor {{ peer }} remote-as {{ conf_peer.remote_as }}
-{%- endif %}
-
-{%- if 'advertisement_interval' in conf_peer %}
- neighbor {{ peer }} advertisement-interval {{ conf_peer.advertisement_interval }}
-{%- endif %}
-
-{%- if 'bfd' in conf_peer %}
-{%- if 'check_control_plane_failure' in conf_peer.bfd %}
- neighbor {{ peer }} bfd
- neighbor {{ peer }} bfd check-control-plane-failure
-{%- else %}
- neighbor {{ peer }} bfd
-{%- endif %}
-{%- endif %}
-
-{%- if 'capability' in conf_peer %}
-{%- if 'dynamic' in conf_peer.capability %}
- neighbor {{ peer }} capability dynamic
-{%- endif %}
-{%- if 'extended_nexthop' in conf_peer.capability %}
- neighbor {{ peer }} capability extended-nexthop
-{%- endif %}
-{%- endif %}
-
-{%- if 'disable_capability_negotiation' in conf_peer %}
- neighbor {{ peer }} disable-capability-negotiation
-{%- endif %}
-
-{#- https://phabricator.vyos.net/T2844. 'disable-send-community' only for afi #}
-{%- if 'disable_send_community' in conf_peer %}
+{% if neighbor is defined and neighbor is not none %}
+{% for n, config in neighbor.items() %}
+{{ bgp_neighbor(n, config) }}
+{% endfor %}
+{% endif %}
!
-{%- endif %}
-
-{%- if 'ebgp_multihop' in conf_peer %}
- neighbor {{ peer }} ebgp-multihop {{conf_peer.ebgp_multihop}}
-{%- endif %}
-
-{#- Need to check. 'Peer-group' needs to define before this section #}
-{%- if 'interface' in conf_peer %}
-{%- if 'peer_group' in conf_peer.interface %}
- neighbor {{ peer }} interface peer-group {{conf_peer.interface.peer_group}}
-{%- endif %}
-{%- if 'remote_as' in conf_peer.interface %}
- neighbor {{ peer }} interface remote-as {{conf_peer.interface.remote_as}}
-{%- endif %}
-{%- if 'v6only' in conf_peer.interface %}
-{%- if 'peer_group' in conf_peer.interface.v6only %}
- neighbor {{ peer }} peer-group {{conf_peer.interface.peer_group}}
-{%- endif %}
-{%- if 'remote_as' in conf_peer.interface.v6only %}
- neighbor {{ peer }} interface v6only remote-as {{conf_peer.interface.v6only.remote_as}}
-{%- endif %}
-{%- endif %}
-{%- endif %}
-
-{%- if 'local_as' in conf_peer %}
-{%- for loc_asn in conf_peer.local_as %}
-{%- if 'no_prepend' in conf_peer.local_as[loc_asn] %}
- neighbor {{ peer }} local-as {{loc_asn}} no-prepend
-{%- else %}
- neighbor {{ peer }} local-as {{loc_asn}}
-{%- endif %}
-{%- endfor %}
-{%- endif %}
-
-{%- if 'override_capability' in conf_peer %}
- neighbor {{ peer }} override-capability
-{%- endif %}
-
-{%- if 'passive' in conf_peer %}
- neighbor {{ peer }} passive
-{%- endif %}
-
-{%- if 'password' in conf_peer %}
- neighbor {{ peer }} password {{ conf_peer.password }}
-{%- endif %}
-
-{%- if 'peer_group' in conf_peer %}
- neighbor {{ peer }} peer-group {{ conf_peer.peer_group }}
-{%- endif %}
-
-{%- if 'port' in conf_peer %}
- neighbor {{ peer }} port {{ conf_peer.port }}
-{%- endif %}
-
-{%- if 'shutdown' in conf_peer %}
- neighbor {{ peer }} shutdown
-{%- endif %}
-
-{%- if 'strict_capability_match' in conf_peer %}
- neighbor {{ peer }} strict-capability-match
-{%- endif %}
-
-{#- set protocols bgp xxx neighbor x.x.x.x timers #}
-{%- if 'timers' in conf_peer %}
-{%- if ( ('connect' in conf_peer.timers) and ('holdtime' in conf_peer.timers) and ('keepalive' in conf_peer.timers ) ) %}
- neighbor {{ peer }} timers {{conf_peer.timers.keepalive}} {{conf_peer.timers.holdtime}}
- neighbor {{ peer }} timers connect {{conf_peer.timers.connect}}
-{%- elif ( ('holdtime' in conf_peer.timers) and ('keepalive' in conf_peer.timers ) ) %}
- neighbor {{ peer }} timers {{conf_peer.timers.keepalive}} {{conf_peer.timers.holdtime}}
-{%- elif 'connect' in conf_peer.timers %}
- neighbor {{ peer }} timers connect {{conf_peer.timers.connect}}
-{%- endif %}
-{%- endif %}
-
-{%- if 'ttl_security' in conf_peer %}
-{%- if 'hops' in conf_peer.ttl_security %}
- neighbor {{ peer }} ttl-security hops {{conf_peer.ttl_security.hops}}
-{%- endif %}
-{%- endif %}
-
-{%- if 'update_source' in conf_peer %}
- neighbor {{ peer }} update-source {{ conf_peer.update_source }}
-{%- endif %}
-
-{%- if 'description' in conf_peer %}
- neighbor {{ peer }} description {{ conf_peer.description }}
-{%- endif %}
-
-{#- START address family for peer; set protocols bgp xxx neighbor x.x.x.x address-family ipvX-unicast #}
-{%- if 'address_family' in conf_peer %}
-{%- for afi in conf_peer.address_family %}
-{%- if afi == "ipv4_unicast" %}
- !
- address-family ipv4 unicast
-
-{%- if 'allowas_in' in conf_peer.address_family.ipv4_unicast %}
-{%- if 'number' in conf_peer.address_family.ipv4_unicast.allowas_in %}
- neighbor {{ peer }} allowas-in {{ conf_peer.address_family.ipv4_unicast.allowas_in.number }}
-{%- else %}
- neighbor {{ peer }} allowas-in
-{%- endif %}
-{%- endif %}
-
-{#- START Single Params for neighbor; #}
-{%- if 'as_override' in conf_peer.address_family.ipv4_unicast %}
- neighbor {{ peer }} as-override
-{%- endif %}
-
-{%- if 'remove_private_as' in conf_peer.address_family.ipv4_unicast %}
- neighbor {{ peer }} remove-private-AS
-{%- endif %}
-
-{%- if 'route_reflector_client' in conf_peer.address_family.ipv4_unicast %}
- neighbor {{ peer }} route-reflector-client
-{%- endif %}
-
-{%- if 'weight' in conf_peer.address_family.ipv4_unicast %}
- neighbor {{ peer }} weight {{ conf_peer.address_family.ipv4_unicast.weight }}
-{%- endif %}
-{#- END single params for neighbor #}
-
-{%- if 'attribute_unchanged' in conf_peer.address_family.ipv4_unicast %}
-{%- if ( ('as_path' in conf_peer.address_family.ipv4_unicast.attribute_unchanged) and ('med' in conf_peer.address_family.ipv4_unicast.attribute_unchanged) ) %}
- neighbor {{ peer }} attribute-unchanged as-path med
-{%- elif ( ('as_path' in conf_peer.address_family.ipv4_unicast.attribute_unchanged) and ('next_hop' in conf_peer.address_family.ipv4_unicast.attribute_unchanged) ) %}
- neighbor {{ peer }} attribute-unchanged as-path next-hop
-{%- elif ( ('med' in conf_peer.address_family.ipv4_unicast.attribute_unchanged) and ('next_hop' in conf_peer.address_family.ipv4_unicast.attribute_unchanged) ) %}
- neighbor {{ peer }} attribute-unchanged med next-hop
-{%- elif 'as_path' in conf_peer.address_family.ipv4_unicast.attribute_unchanged %}
- neighbor {{ peer }} attribute-unchanged as-path
-{%- elif 'med' in conf_peer.address_family.ipv4_unicast.attribute_unchanged %}
- neighbor {{ peer }} attribute-unchanged med
-{%- elif 'next_hop' in conf_peer.address_family.ipv4_unicast.attribute_unchanged %}
- neighbor {{ peer }} attribute-unchanged next-hop
-{%- else %}
- neighbor {{ peer }} attribute-unchanged as-path next-hop med
-{%- endif %}
-{%- endif %}
-{#- END attribute-unchanged #}
-
-{%- if 'capability' in conf_peer.address_family.ipv4_unicast %}
-{%- if 'orf' in conf_peer.address_family.ipv4_unicast.capability %}
-{%- if 'receive' in conf_peer.address_family.ipv4_unicast.capability.orf.prefix_list %}
- neighbor {{ peer }} capability orf prefix-list receive
-{%- endif %}
-{%- if 'send' in conf_peer.address_family.ipv4_unicast.capability.orf.prefix_list %}
- neighbor {{ peer }} capability orf prefix-list send
-{%- endif %}
-{%- endif %}
-{%- endif %}
-
-{%- if 'default_originate' in conf_peer.address_family.ipv4_unicast %}
-{%- if 'route_map' in conf_peer.address_family.ipv4_unicast.default_originate %}
- neighbor {{ peer }} default-originate route-map {{ conf_peer.address_family.ipv4_unicast.default_originate.route_map }}
-{%- else %}
- neighbor {{ peer }} default-originate
-{%- endif %}
-{%- endif %}
-
-{%- if 'distribute_list' in conf_peer.address_family.ipv4_unicast %}
-{%- if 'export' in conf_peer.address_family.ipv4_unicast.distribute_list %}
- neighbor {{ peer }} distribute-list {{conf_peer.address_family.ipv4_unicast.distribute_list.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer.address_family.ipv4_unicast.distribute_list %}
- neighbor {{ peer }} distribute-list {{conf_peer.address_family.ipv4_unicast.distribute_list.import}} in
-{%- endif %}
-{%- endif %}
-
-{%- if 'filter_list' in conf_peer.address_family.ipv4_unicast %}
-{%- if 'export' in conf_peer.address_family.ipv4_unicast.filter_list %}
- neighbor {{ peer }} filter-list {{conf_peer.address_family.ipv4_unicast.filter_list.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer.address_family.ipv4_unicast.filter_list %}
- neighbor {{ peer }} filter-list {{conf_peer.address_family.ipv4_unicast.filter_list.import}} in
-{%- endif %}
-{%- endif %}
-
-{%- if 'maximum_prefix' in conf_peer.address_family.ipv4_unicast %}
- neighbor {{ peer }} maximum-prefix {{ conf_peer.address_family.ipv4_unicast.maximum_prefix }}
-{%- endif %}
-
-{#- https://phabricator.vyos.net/T1817 #}
-{%- if 'nexthop_self' in conf_peer.address_family.ipv4_unicast %}
-{%- if 'force' in conf_peer.address_family.ipv4_unicast.nexthop_self %}
- neighbor {{ peer }} next-hop-self force
- neighbor {{ peer }} next-hop-self
-{%- else %}
- neighbor {{ peer }} next-hop-self
-{%- endif %}
-{%- endif %}
-
-{%- if 'route_server_client' in conf_peer.address_family.ipv4_unicast %}
- neighbor {{ peer }} route-server-client
-{%- endif %}
-
-{%- if 'route_map' in conf_peer.address_family.ipv4_unicast %}
-{%- if 'export' in conf_peer.address_family.ipv4_unicast.route_map %}
- neighbor {{ peer }} route-map {{conf_peer.address_family.ipv4_unicast.route_map.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer.address_family.ipv4_unicast.route_map %}
- neighbor {{ peer }} route-map {{conf_peer.address_family.ipv4_unicast.route_map.import}} in
-{%- endif %}
-{%- endif %}
-{%- if 'prefix_list' in conf_peer.address_family.ipv4_unicast %}
-{%- if 'export' in conf_peer.address_family.ipv4_unicast.prefix_list %}
- neighbor {{ peer }} prefix-list {{conf_peer.address_family.ipv4_unicast.prefix_list.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer.address_family.ipv4_unicast.prefix_list %}
- neighbor {{ peer }} prefix-list {{conf_peer.address_family.ipv4_unicast.prefix_list.import}} in
-{%- endif %}
-{%- endif %}
-
-{%- if 'soft_reconfiguration' in conf_peer.address_family.ipv4_unicast %}
-{%- if 'inbound' is defined %}
- neighbor {{ peer }} soft-reconfiguration inbound
-{%- endif %}
-{%- endif %}
-
-{%- if 'unsuppress_map' in conf_peer.address_family.ipv4_unicast %}
- neighbor {{ peer }} unsuppress-map {{conf_peer.address_family.ipv4_unicast.unsuppress_map}}
-{%- endif %}
- neighbor {{ peer }} activate
- exit-address-family
- !
-{%- endif %}
-
-{%- if afi == "ipv6_unicast" %}
- !
- address-family ipv6 unicast
-
-{%- if 'allowas_in' in conf_peer.address_family.ipv6_unicast %}
-{%- if 'number' in conf_peer.address_family.ipv6_unicast.allowas_in %}
- neighbor {{ peer }} allowas-in {{ conf_peer.address_family.ipv6_unicast.allowas_in.number }}
-{%- else %}
- neighbor {{ peer }} allowas-in
-{%- endif %}
-{%- endif %}
-
-{#- START Single Params for neighbor #}
-{%- if 'as_override' in conf_peer.address_family.ipv6_unicast %}
- neighbor {{ peer }} as-override
-{%- endif %}
-
-{%- if 'remove_private_as' in conf_peer.address_family.ipv6_unicast %}
- neighbor {{ peer }} remove-private-AS
-{%- endif %}
-
-{%- if 'route_reflector_client' in conf_peer.address_family.ipv6_unicast %}
- neighbor {{ peer }} route-reflector-client
-{%- endif %}
-
-{%- if 'weight' in conf_peer.address_family.ipv6_unicast %}
- neighbor {{ peer }} weight {{ conf_peer.address_family.ipv6_unicast.weight }}
-{%- endif %}
-{#- END single params for neighbor #}
-
-{%- if 'attribute_unchanged' in conf_peer.address_family.ipv6_unicast %}
-{%- if ( ('as_path' in conf_peer.address_family.ipv6_unicast.attribute_unchanged) and ('med' in conf_peer.address_family.ipv6_unicast.attribute_unchanged) ) %}
- neighbor {{ peer }} attribute-unchanged as-path med
-{%- elif ( ('as_path' in conf_peer.address_family.ipv6_unicast.attribute_unchanged) and ('next_hop' in conf_peer.address_family.ipv6_unicast.attribute_unchanged) ) %}
- neighbor {{ peer }} attribute-unchanged as-path next-hop
-{%- elif ( ('med' in conf_peer.address_family.ipv6_unicast.attribute_unchanged) and ('next_hop' in conf_peer.address_family.ipv6_unicast.attribute_unchanged) ) %}
- neighbor {{ peer }} attribute-unchanged med next-hop
-{%- elif 'as_path' in conf_peer.address_family.ipv6_unicast.attribute_unchanged %}
- neighbor {{ peer }} attribute-unchanged as-path
-{%- elif 'med' in conf_peer.address_family.ipv6_unicast.attribute_unchanged %}
- neighbor {{ peer }} attribute-unchanged med
-{%- elif 'next_hop' in conf_peer.address_family.ipv6_unicast.attribute_unchanged %}
- neighbor {{ peer }} attribute-unchanged next-hop
-{%- else %}
- neighbor {{ peer }} attribute-unchanged as-path next-hop med
-{%- endif %}
-{%- endif %}
-{#- END attribute-unchanged #}
-
-{%- if 'capability' in conf_peer.address_family.ipv6_unicast %}
-{%- if 'orf' in conf_peer.address_family.ipv6_unicast.capability %}
-{%- if 'receive' in conf_peer.address_family.ipv6_unicast.capability.orf.prefix_list %}
- neighbor {{ peer }} capability orf prefix-list receive
-{%- endif %}
-{%- if 'send' in conf_peer.address_family.ipv6_unicast.capability.orf.prefix_list %}
- neighbor {{ peer }} capability orf prefix-list send
-{%- endif %}
-{%- endif %}
-{%- endif %}
-
-{%- if 'default_originate' in conf_peer.address_family.ipv6_unicast %}
-{%- if 'route_map' in conf_peer.address_family.ipv6_unicast.default_originate %}
- neighbor {{ peer }} default-originate route-map {{ conf_peer.address_family.ipv6_unicast.default_originate.route_map }}
-{%- else %}
- neighbor {{ peer }} default-originate
-{%- endif %}
-{%- endif %}
-
-{%- if 'distribute_list' in conf_peer.address_family.ipv6_unicast %}
-{%- if 'export' in conf_peer.address_family.ipv6_unicast.distribute_list %}
- neighbor {{ peer }} distribute-list {{conf_peer.address_family.ipv6_unicast.distribute_list.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer.address_family.ipv6_unicast.distribute_list %}
- neighbor {{ peer }} distribute-list {{conf_peer.address_family.ipv6_unicast.distribute_list.import}} in
-{%- endif %}
-{%- endif %}
-
-{%- if 'filter_list' in conf_peer.address_family.ipv6_unicast %}
-{%- if 'export' in conf_peer.address_family.ipv6_unicast.filter_list %}
- neighbor {{ peer }} filter-list {{conf_peer.address_family.ipv6_unicast.filter_list.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer.address_family.ipv6_unicast.filter_list %}
- neighbor {{ peer }} filter-list {{conf_peer.address_family.ipv6_unicast.filter_list.import}} in
-{%- endif %}
-{%- endif %}
-
-{%- if 'maximum_prefix' in conf_peer.address_family.ipv6_unicast %}
- neighbor {{ peer }} maximum-prefix {{ conf_peer.address_family.ipv6_unicast.maximum_prefix }}
-{%- endif %}
-
-{#- https://phabricator.vyos.net/T1817 #}
-{%- if 'nexthop_self' in conf_peer.address_family.ipv6_unicast %}
-{%- if 'force' in conf_peer.address_family.ipv6_unicast.nexthop_self %}
- neighbor {{ peer }} next-hop-self force
- neighbor {{ peer }} next-hop-self
-{%- else %}
- neighbor {{ peer }} next-hop-self
-{%- endif %}
-{%- endif %}
-
-{%- if 'route_server_client' in conf_peer.address_family.ipv6_unicast %}
- neighbor {{ peer }} route-server-client
-{%- endif %}
-
-{%- if 'route_map' in conf_peer.address_family.ipv6_unicast %}
-{%- if 'export' in conf_peer.address_family.ipv6_unicast.route_map %}
- neighbor {{ peer }} route-map {{conf_peer.address_family.ipv6_unicast.route_map.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer.address_family.ipv6_unicast.route_map %}
- neighbor {{ peer }} route-map {{conf_peer.address_family.ipv6_unicast.route_map.import}} in
-{%- endif %}
-{%- endif %}
-{%- if 'prefix_list' in conf_peer.address_family.ipv6_unicast %}
-{%- if 'export' in conf_peer.address_family.ipv6_unicast.prefix_list %}
- neighbor {{ peer }} prefix-list {{conf_peer.address_family.ipv6_unicast.prefix_list.export}} out
-{%- endif %}
-{%- if 'import' in conf_peer.address_family.ipv6_unicast.prefix_list %}
- neighbor {{ peer }} prefix-list {{conf_peer.address_family.ipv6_unicast.prefix_list.import}} in
-{%- endif %}
-{%- endif %}
-
-{%- if 'soft_reconfiguration' in conf_peer.address_family.ipv6_unicast %}
-{%- if 'inbound' is defined %}
- neighbor {{ peer }} soft-reconfiguration inbound
-{%- endif %}
-{%- endif %}
-
-{%- if 'unsuppress_map' in conf_peer.address_family.ipv6_unicast %}
- neighbor {{ peer }} unsuppress-map {{conf_peer.address_family.ipv6_unicast.unsuppress_map}}
-{%- endif %}
- neighbor {{ peer }} activate
- exit-address-family
- !
-{%- endif %}
-
-{%- endfor %}
-{%- endif %}
-{#- END address family for peer #}
-
-{%- endfor %}
-{#- END peer section; set protocols bgp xxx neighbor #}
-
-{#- START parameters section; set protocol bgp xxx parameters #}
-{%- if 'always_compare_med' in bgp_params %}
+{% if parameters is defined %}
+{% if parameters.always_compare_med is defined %}
bgp always-compare-med
-{%- endif %}
-
-{%- if 'bestpath' in bgp_params %}
-{%- if 'compare_routerid' in bgp_params.bestpath %}
+{% endif %}
+{% if parameters.bestpath is defined and parameters.bestpath is not none %}
+{% if parameters.bestpath.compare_routerid is defined %}
bgp bestpath compare-routerid
-{%- endif %}
-{%- if 'as_path' in bgp_params.bestpath %}
-{%- if 'confed' in bgp_params.bestpath.as_path %}
- bgp bestpath as-path confed
-{%- endif %}
-{%- if 'ignore' in bgp_params.bestpath.as_path %}
- bgp bestpath as-path ignore
-{%- endif %}
-{%- if 'multipath_relax' in bgp_params.bestpath.as_path %}
- bgp bestpath as-path multipath-relax
-{%- endif %}
-{%- endif %}
-{%- if 'med' in bgp_params.bestpath %}
-{%- if ( ('confed' in bgp_params.bestpath.med) and ('missing_as_worst' in bgp_params.bestpath.med ) ) %}
- bgp bestpath med confed missing-as-worst
-{%- elif 'confed' in bgp_params.bestpath.med %}
- bgp bestpath med confed
-{%- elif 'missing_as_worst' in bgp_params.bestpath.med %}
- bgp bestpath med missing-as-worst
-{%- endif%}
-{%- endif %}
-{%- endif %}
-
-{%- if 'cluster_id' in bgp_params %}
- bgp cluster-id {{ bgp_params.cluster_id }}
-{%- endif %}
-
-{%- if 'confederation' in bgp_params %}
-{%- if 'identifier' in bgp_params.confederation %}
- bgp confederation identifier {{ bgp_params.confederation.identifier }}
-{%- endif %}
-{%- if 'peers' in bgp_params.confederation %}
- bgp confederation peers {{ bgp_params.confederation.peers }}
-{%- endif %}
-{%- endif %}
-
-{#- Doesn't work in current FRR configuration; vtysh (bgp dampening 16 751 2001 61) #}
-{%- if 'dampening' in bgp_params %}
-{%- if ( ('half_life' in bgp_params.dampening) and ('max_suppress_time' in bgp_params.dampening) and ('re_use' in bgp_params.dampening) and ('start_suppress_time' in bgp_params.dampening ) ) %}
- bgp dampening {{ bgp_params.dampening.half_life }} {{ bgp_params.dampening.re_use }} {{ bgp_params.dampening.start_suppress_time }} {{ bgp_params.dampening.max_suppress_time }}
-{%- endif %}
-{%- endif %}
-
-{%- if 'default' in bgp_params %}
-{%- if 'local_pref' in bgp_params.default %}
- bgp default local-preference {{ bgp_params.default.local_pref }}
-{%- endif %}
-{#- We use this is parameter as default in template (5-th string) #}
-{%- if 'no_ipv4_unicast' in bgp_params.default %}
+{% endif %}
+{% if parameters.bestpath.as_path is defined and parameters.bestpath.as_path is not none %}
+{% for option in parameters.bestpath.as_path %}
+ bgp bestpath as-path {{ option|replace('_', '-') }}
+{% endfor %}
+{% endif %}
+{% if parameters.bestpath.med is defined and parameters.bestpath.med is not none %}
+ bgp bestpath med {{ 'confed' if parameters.bestpath.med.confed is defined }} {{ 'missing-as-worst' if parameters.bestpath.med.missing_as_worst is defined }}
+{% endif %}
+{% endif %}
+{% if parameters.cluster_id is defined and parameters.cluster_id is not none %}
+ bgp cluster-id {{ parameters.cluster_id }}
+{% endif %}
+{% if parameters.confederation is defined and parameters.confederation is not none %}
+{% if parameters.confederation.identifier is defined and parameters.confederation.identifier is not none %}
+ bgp confederation identifier {{ parameters.confederation.identifier }}
+{% endif %}
+{% if parameters.confederation.peers is defined and parameters.confederation.peers is not none %}
+ bgp confederation peers {{ parameters.confederation.peers }}
+{% endif %}
+{% endif %}
+{% if parameters.dampening is defined and parameters.dampening is defined and parameters.dampening.half_life is defined and parameters.dampening.half_life is not none %}
+{# Doesn't work in current FRR configuration; vtysh (bgp dampening 16 751 2001 61) #}
+ bgp dampening {{ parameters.dampening.half_life }} {{ parameters.dampening.re_use if parameters.dampening.re_use is defined }} {{ parameters.dampening.start_suppress_time if parameters.dampening.start_suppress_time is defined }} {{ parameters.dampening.max_suppress_time if parameters.dampening.max_suppress_time is defined }}
+{% endif %}
+{% if parameters.default is defined and parameters.default is not none %}
+{% if parameters.default.local_pref is defined and parameters.default.local_pref is not none %}
+ bgp default local-preference {{ parameters.default.local_pref }}
+{% endif %}
+{% if parameters.default.no_ipv4_unicast is defined %}
+{# We use this is parameter as default in template (5-th string) #}
no bgp default ipv4-unicast
-{%- endif %}
-{%- endif %}
-
-{%- if 'deterministic_med' in bgp_params %}
- bgp deterministic-med
-{%- endif %}
-
-{%- if 'distance' in bgp_params %}
-{%- if 'global' in bgp_params.distance %}
-{%- if ( ('external' in bgp_params.distance.global) and ('internal' in bgp_params.distance.global) and ('local' in bgp_params.distance.global ) ) %}
+{% endif %}
+{% endif %}
+{% if parameters.deterministic_med is defined %}
+ bgp deterministic-med
+{% endif %}
+{% if parameters.distance is defined and parameters.distance is not none %}
!
address-family ipv4 unicast
- distance bgp {{ bgp_params.distance.global.external }} {{ bgp_params.distance.global.internal }} {{ bgp_params.distance.global.local }}
+{% if parameters.distance.global is defined and parameters.distance.global.external is defined and parameters.distance.global.internal is defined and parameters.distance.global.local is defined %}
+ distance bgp {{ parameters.distance.global.external }} {{ parameters.distance.global.internal }} {{ parameters.distance.global.local }}
+{% endif %}
+{% if parameters.distance.prefix is defined and parameters.distance.prefix is not none %}
+{% for prefix in parameters.distance.prefix %}
+ distance {{ parameters.distance.prefix[prefix].distance }} {{ prefix }}
+{% endfor %}
+{% endif %}
exit-address-family
-!
-{%- endif %}
-{%- endif %}
-{%- if 'prefix' in bgp_params.distance %}
!
- address-family ipv4 unicast
-{%- for prfx in bgp_params.distance.prefix %}
- distance {{ bgp_params.distance.prefix[prfx].distance }} {{ prfx }}
-{%- endfor %}
- exit-address-family
-!
-{%- endif %}
-{%- endif %}
-
-{%- if 'graceful_restart' in bgp_params %}
-{%- if 'stalepath_time' in bgp_params.graceful_restart %}
- bgp graceful-restart stalepath-time {{ bgp_params.graceful_restart.stalepath_time }}
-{%- endif %}
-{%- endif %}
-
-{%- if 'log_neighbor_changes' in bgp_params %}
+{% endif %}
+{% if parameters.graceful_restart is defined %}
+ bgp graceful-restart {{ 'stalepath-time ' + parameters.graceful_restart.stalepath_time if parameters.graceful_restart.stalepath_time is defined }}
+{% endif %}
+{% if parameters.log_neighbor_changes is defined %}
bgp log-neighbor-changes
-{%- endif %}
-
-{%- if 'network_import_check' in bgp_params %}
- bgp network import-check
-{%- endif %}
-
-{%- if 'no_client_to_client_reflection' in bgp_params %}
+{% endif %}
+{% if parameters.network_import_check is defined %}
+ bgp network import-check
+{% endif %}
+{% if parameters.no_client_to_client_reflection is defined %}
no bgp client-to-client reflection
-{%- endif %}
-
-{%- if 'no_fast_external_failover' in bgp_params %}
+{% endif %}
+{% if parameters.no_fast_external_failover is defined %}
no bgp fast-external-failover
-{%- endif %}
-
-{%- if 'router_id' in bgp_params %}
- bgp router-id {{ bgp_params.router_id }}
-{%- endif %}
-
-{#- END parameters; set protocols bgp xxx parameters #}
-
-{%- if 'timers' in conf_bgp[asn] %}
-{%- if ( ('holdtime' in conf_bgp[asn].timers) and ('keepalive' in conf_bgp[asn].timers ) ) %}
- timers bgp {{conf_bgp[asn].timers.keepalive}} {{conf_bgp[asn].timers.holdtime}}
-{%- endif %}
-{%- endif %}
-
-{%- if 'route_map' in conf_bgp[asn] %}
-!
-ip protocol bgp route-map {{conf_bgp[asn].route_map}}
-{%- endif %}
-!
-{%- endfor -%}
-{#- END asn; router bgp xxx #}
+{% endif %}
+{% if parameters.router_id is defined and parameters.router_id is not none %}
+ bgp router-id {{ parameters.router_id }}
+{% endif %}
+{% endif %}
+{% if timers is defined and timers.keepalive is defined and timers.holdtime is defined %}
+ timers bgp {{ timers.keepalive }} {{ timers.holdtime }}
+{% endif %}
+ !
+{% if route_map is defined and route_map is not none %}
+ ip protocol bgp route-map {{ route_map }}
+{% endif %}
+ !
diff --git a/data/templates/frr/igmp.frr.tmpl b/data/templates/frr/igmp.frr.tmpl
index de4696c1f..cdb7ee6cc 100644
--- a/data/templates/frr/igmp.frr.tmpl
+++ b/data/templates/frr/igmp.frr.tmpl
@@ -1,41 +1,41 @@
!
-{% for iface in old_ifaces -%}
+{% 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] -%}
+{% 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 -%}
+{% endfor %}
+{% else %}
no ip igmp join {{ group }}
-{% endif -%}
-{% endfor -%}
+{% endif %}
+{% endfor %}
no ip igmp
!
-{% endfor -%}
-{% for iface in ifaces -%}
+{% endfor %}
+{% for iface in ifaces %}
interface {{ iface }}
-{% if ifaces[iface].version -%}
+{% if ifaces[iface].version %}
ip igmp version {{ ifaces[iface].version }}
-{% else -%}
+{% else %}
{# IGMP default version 3 #}
ip igmp
-{% endif -%}
-{% if ifaces[iface].query_interval -%}
+{% endif %}
+{% if ifaces[iface].query_interval %}
ip igmp query-interval {{ ifaces[iface].query_interval }}
-{% endif -%}
-{% if ifaces[iface].query_max_resp_time -%}
+{% 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] -%}
+{% 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 -%}
+{% endfor %}
+{% else %}
ip igmp join {{ group }}
-{% endif -%}
-{% endfor -%}
+{% endif %}
+{% endfor %}
!
-{% endfor -%}
+{% endfor %}
!
diff --git a/data/templates/frr/ldpd.frr.tmpl b/data/templates/frr/ldpd.frr.tmpl
index 4b7e5c5ea..280df41eb 100644
--- a/data/templates/frr/ldpd.frr.tmpl
+++ b/data/templates/frr/ldpd.frr.tmpl
@@ -1,187 +1,120 @@
!
-{% if mpls_ldp -%}
+{% if ldp is defined %}
mpls ldp
-{% if old_router_id -%}
-no router-id {{ old_router_id }}
-{% endif -%}
-{% if router_id -%}
-router-id {{ router_id }}
-{% endif -%}
-{% if old_ldp.cisco_interop_tlv -%}
-no dual-stack cisco-interop
-{% endif -%}
-{% if ldp.cisco_interop_tlv -%}
+{% if ldp.router_id is defined %}
+router-id {{ ldp.router_id }}
+{% endif %}
+{% if ldp.parameters is defined %}
+{% if ldp.parameters.cisco_interop_tlv is defined %}
dual-stack cisco-interop
-{% endif -%}
-{% if old_ldp.transport_prefer_ipv4 -%}
-no dual-stack transport-connection prefer ipv4
-{% endif -%}
-{% if ldp.transport_prefer_ipv4 -%}
+{% endif %}
+{% if ldp.parameters.transport_prefer_ipv4 is defined%}
dual-stack transport-connection prefer ipv4
-{% endif -%}
-{% for neighbor_id in old_ldp.neighbors -%}
-no neighbor {{neighbor_id}} password {{old_ldp.neighbors[neighbor_id].password}}
-{% if 'ttl_security' is defined -%}
-{% if 'disable' in old_ldp.neighbors[neighbor_id].ttl_security %}
-no neighbor {{neighbor_id}} ttl-security disable
-{% else -%}
-no neighbor {{neighbor_id}} ttl-security hops {{old_ldp.neighbors[neighbor_id].ttl_security}}
-{% endif -%}
-{% endif -%}
-{% if 'session_holdtime' is defined -%}
-no neighbor {{neighbor_id}} session holdtime {{old_ldp.neighbors[neighbor_id].session_holdtime}}
-{% endif -%}
-{% endfor -%}
-{% for neighbor_id in ldp.neighbors -%}
-neighbor {{neighbor_id}} password {{ldp.neighbors[neighbor_id].password}}
-{% if 'ttl_security' is defined -%}
-{% if 'disable' in ldp.neighbors[neighbor_id].ttl_security %}
-neighbor {{neighbor_id}} ttl-security disable
-{% else -%}
-neighbor {{neighbor_id}} ttl-security hops {{ldp.neighbors[neighbor_id].ttl_security}}
-{% endif -%}
-{% endif -%}
-{% if 'session_holdtime' is defined -%}
-neighbor {{neighbor_id}} session holdtime {{ldp.neighbors[neighbor_id].session_holdtime}}
-{% endif -%}
-{% endfor -%}
+{% endif %}
+{% endif %}
+{% if ldp.neighbor is defined %}
+{% for neighbors in ldp.neighbor %}
+{% if ldp.neighbor[neighbors].password is defined %}
+neighbor {{neighbors}} password {{ldp.neighbor[neighbors].password}}
+{% endif %}
+{% if ldp.neighbor[neighbors].ttl_security is defined %}
+{% if 'disable' in ldp.neighbor[neighbors].ttl_security %}
+neighbor {{neighbors}} ttl-security disable
+{% else %}
+neighbor {{neighbors}} ttl-security hops {{ldp.neighbor[neighbors].ttl_security}}
+{% endif %}
+{% endif %}
+{% if ldp.neighbor[neighbors].session_holdtime is defined %}
+neighbor {{neighbors}} session holdtime {{ldp.neighbor[neighbors].session_holdtime}}
+{% endif %}
+{% endfor %}
+{% endif %}
!
+{% if ldp.discovery is defined %}
+{% if ldp.discovery.transport_ipv4_address is defined %}
address-family ipv4
label local allocate host-routes
-{% if old_ldp.export_ipv4_exp -%}
-no label local advertise explicit-null
-{% endif -%}
-{% if ldp.export_ipv4_exp -%}
+{% if ldp.discovery.transport_ipv4_address is defined %}
+discovery transport-address {{ ldp.discovery.transport_ipv4_address }}
+{% endif %}
+{% if ldp.discovery.hello_ipv4_holdtime is defined %}
+discovery hello holdtime {{ ldp.discovery.hello_ipv4_holdtime }}
+{% endif %}
+{% if ldp.discovery.hello_ipv4_interval is defined %}
+discovery hello interval {{ ldp.discovery.hello_ipv4_interval }}
+{% endif %}
+{% if ldp.discovery.session_ipv4_holdtime is defined %}
+session holdtime {{ ldp.discovery.session_ipv4_holdtime }}
+{% endif %}
+{% if ldp.export is defined %}
+{% if ldp.export.ipv4.explicit_null is defined %}
label local advertise explicit-null
-{% endif -%}
-{% 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 -%}
-{% if old_ldp.hello_ipv4_holdtime -%}
-no discovery hello holdtime {{ old_ldp.hello_ipv4_holdtime }}
-{% endif -%}
-{% if ldp.hello_ipv4_holdtime -%}
-discovery hello holdtime {{ ldp.hello_ipv4_holdtime }}
-{% endif -%}
-{% if old_ldp.hello_ipv4_interval -%}
-no discovery hello interval {{ old_ldp.hello_ipv4_interval }}
-{% endif -%}
-{% if ldp.hello_ipv4_interval -%}
-discovery hello interval {{ ldp.hello_ipv4_interval }}
-{% endif -%}
-{% if old_ldp.ses_ipv4_hold -%}
-no session holdtime {{ old_ldp.ses_ipv4_hold }}
-{% endif -%}
-{% if ldp.ses_ipv4_hold -%}
-session holdtime {{ ldp.ses_ipv4_hold }}
-{% endif -%}
-{% if old_ldp.target_ipv4_enable -%}
-no discovery targeted-hello accept
-{% endif -%}
-{% if ldp.target_ipv4_enable -%}
+{% endif %}
+{% endif %}
+{% if ldp.targeted_neighbor is defined %}
+{% if ldp.targeted_neighbor.ipv4.enable is defined %}
discovery targeted-hello accept
-{% endif -%}
-{% if old_ldp.target_ipv4_hello_int -%}
-no discovery targeted-hello interval {{ old_ldp.target_ipv4_hello_int }}
-{% endif -%}
-{% if ldp.target_ipv4_hello_int -%}
-discovery targeted-hello interval {{ ldp.target_ipv4_hello_int }}
-{% endif -%}
-{% if old_ldp.target_ipv4_hello_hold -%}
-no discovery targeted-hello holdtime {{ old_ldp.target_ipv4_hello_hold }}
-{% endif -%}
-{% if ldp.target_ipv4_hello_hold -%}
-discovery targeted-hello holdtime {{ ldp.target_ipv4_hello_hold }}
-{% endif -%}
-{% for address in old_ldp.target_ipv4_addresses -%}
-no neighbor {{address}} targeted
-{% endfor -%}
-{% for address in ldp.target_ipv4_addresses -%}
-neighbor {{address}} targeted
-{% endfor -%}
-{% for interface in old_ldp.interfaces -%}
-no interface {{interface}}
-{% endfor -%}
-{% for interface in ldp.interfaces -%}
-interface {{interface}}
-{% endfor -%}
-!
-!
+{% endif %}
+{% if ldp.targeted_neighbor.ipv4.hello_holdtime is defined %}
+discovery targeted-hello holdtime {{ ldp.targeted_neighbor.ipv4.hello_holdtime }}
+{% endif %}
+{% if ldp.targeted_neighbor.ipv4.hello_interval is defined %}
+discovery targeted-hello interval {{ ldp.targeted_neighbor.ipv4.hello_interval }}
+{% endif %}
+{% for addresses in ldp.targeted_neighbor.ipv4.address %}
+neighbor {{addresses}} targeted
+{% endfor %}
+{% endif %}
+{% for interfaces in ldp.interface %}
+interface {{interfaces}}
+{% endfor %}
exit-address-family
+{% else %}
+no address-family ipv4
+{% endif %}
+{% endif %}
!
-{% if ldp.d_transp_ipv6 -%}
+{% if ldp.discovery is defined %}
+{% if ldp.discovery.transport_ipv6_address is defined %}
address-family ipv6
label local allocate host-routes
-{% if old_ldp.export_ipv6_exp -%}
-no label local advertise explicit-null
-{% endif -%}
-{% if ldp.export_ipv6_exp -%}
+{% if ldp.discovery.transport_ipv6_address is defined %}
+discovery transport-address {{ ldp.discovery.transport_ipv6_address }}
+{% endif %}
+{% if ldp.discovery.hello_ipv6_holdtime is defined %}
+discovery hello holdtime {{ ldp.discovery.hello_ipv6_holdtime }}
+{% endif %}
+{% if ldp.discovery.hello_ipv6_interval is defined %}
+discovery hello interval {{ ldp.discovery.hello_ipv6_interval }}
+{% endif %}
+{% if ldp.discovery.session_ipv6_holdtime is defined %}
+session holdtime {{ ldp.discovery.session_ipv6_holdtime }}
+{% endif %}
+{% if ldp.export is defined %}
+{% if ldp.export.ipv6.explicit_null is defined %}
label local advertise explicit-null
-{% endif -%}
-{% if old_ldp.ses_ipv6_hold -%}
-no session holdtime {{ old_ldp.ses_ipv6_hold }}
-{% endif -%}
-{% if ldp.ses_ipv6_hold -%}
-session holdtime {{ ldp.ses_ipv6_hold }}
-{% endif -%}
-{% if old_ldp.d_transp_ipv6 -%}
-no discovery transport-address {{ old_ldp.d_transp_ipv6 }}
-{% endif -%}
-{% if ldp.d_transp_ipv6 -%}
-discovery transport-address {{ ldp.d_transp_ipv6 }}
-{% endif -%}
-{% if old_ldp.hello_ipv6_holdtime -%}
-no discovery hello holdtime {{ old_ldp.hello_ipv6_holdtime }}
-{% endif -%}
-{% if ldp.hello_ipv6_holdtime -%}
-discovery hello holdtime {{ ldp.hello_ipv6_holdtime }}
-{% endif -%}
-{% if old_ldp.hello_ipv6_interval -%}
-no discovery hello interval {{ old_ldp.hello_ipv6_interval }}
-{% endif -%}
-{% if ldp.hello_ipv6_interval -%}
-discovery hello interval {{ ldp.hello_ipv6_interval }}
-{% endif -%}
-{% if old_ldp.target_ipv6_enable -%}
-no discovery targeted-hello accept
-{% endif -%}
-{% if ldp.target_ipv6_enable -%}
+{% endif %}
+{% endif %}
+{% if ldp.targeted_neighbor is defined %}
+{% if ldp.targeted_neighbor.ipv6.enable is defined %}
discovery targeted-hello accept
-{% endif -%}
-{% if old_ldp.target_ipv6_hello_int -%}
-no discovery targeted-hello interval {{ old_ldp.target_ipv6_hello_int }}
-{% endif -%}
-{% if ldp.target_ipv6_hello_int -%}
-discovery targeted-hello interval {{ ldp.target_ipv6_hello_int }}
-{% endif -%}
-{% if old_ldp.target_ipv6_hello_hold -%}
-no discovery targeted-hello holdtime {{ old_ldp.target_ipv6_hello_hold }}
-{% endif -%}
-{% if ldp.target_ipv6_hello_hold -%}
-discovery targeted-hello holdtime {{ ldp.target_ipv6_hello_hold }}
-{% endif -%}
-{% for address in old_ldp.target_ipv6_addresses -%}
-no neighbor {{address}} targeted
-{% endfor -%}
-{% for address in ldp.target_ipv6_addresses -%}
-neighbor {{address}} targeted
-{% endfor -%}
-{% for interface in old_ldp.interfaces -%}
-no interface {{interface}}
-{% endfor -%}
-{% for interface in ldp.interfaces -%}
-interface {{interface}}
-{% endfor -%}
-!
+{% endif %}
+{% if ldp.targeted_neighbor.ipv6.hello_holdtime is defined %}
+discovery targeted-hello holdtime {{ ldp.targeted_neighbor.ipv6.hello_holdtime }}
+{% endif %}
+{% if ldp.targeted_neighbor.ipv6.hello_interval is defined %}
+discovery targeted-hello interval {{ ldp.targeted_neighbor.ipv6.hello_interval }}
+{% endif %}
+{% for addresses in ldp.targeted_neighbor.ipv6.address %}
+neighbor {{addresses}} targeted
+{% endfor %}
+{% endif %}
+{% for interfaces in ldp.interface %}
+interface {{interfaces}}
+{% endfor %}
exit-address-family
-{% else -%}
+{% else %}
no address-family ipv6
-{% endif -%}
-!
-{% else -%}
-no mpls ldp
-{% endif -%}
-! \ No newline at end of file
+{% endif %}
+{% endif %}
+{% endif %}
diff --git a/data/templates/frr/pimd.frr.tmpl b/data/templates/frr/pimd.frr.tmpl
index 1d1532c60..a5b56223a 100644
--- a/data/templates/frr/pimd.frr.tmpl
+++ b/data/templates/frr/pimd.frr.tmpl
@@ -1,34 +1,34 @@
!
-{% for rp_addr in old_pim.rp -%}
-{% for group in old_pim.rp[rp_addr] -%}
+{% 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 -%}
+{% 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 -%}
+{% endif %}
+{% for iface in old_pim.ifaces %}
interface {{ iface }}
no ip pim
!
-{% endfor -%}
-{% for iface in pim.ifaces -%}
+{% endfor %}
+{% for iface in pim.ifaces %}
interface {{ iface }}
ip pim
-{% if pim.ifaces[iface].dr_prio -%}
+{% if pim.ifaces[iface].dr_prio %}
ip pim drpriority {{ pim.ifaces[iface].dr_prio }}
-{% endif -%}
-{% if pim.ifaces[iface].hello -%}
+{% endif %}
+{% if pim.ifaces[iface].hello %}
ip pim hello {{ pim.ifaces[iface].hello }}
-{% endif -%}
+{% endif %}
!
-{% endfor -%}
-{% for rp_addr in pim.rp -%}
-{% for group in pim.rp[rp_addr] -%}
+{% 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 -%}
+{% endfor %}
+{% endfor %}
+{% if pim.rp_keep_alive %}
ip pim rp keep-alive-timer {{ pim.rp_keep_alive }}
-{% endif -%}
+{% endif %}
!
diff --git a/data/templates/frr/rip.frr.tmpl b/data/templates/frr/rip.frr.tmpl
index 60bc686bd..83df4e203 100644
--- a/data/templates/frr/rip.frr.tmpl
+++ b/data/templates/frr/rip.frr.tmpl
@@ -1,143 +1,143 @@
!
-{% if rip_conf -%}
+{% if rip_conf %}
router rip
-{% if old_default_distance -%}
+{% if old_default_distance %}
no distance {{old_default_distance}}
-{% endif -%}
-{% if default_distance -%}
+{% endif %}
+{% if default_distance %}
distance {{default_distance}}
-{% endif -%}
-{% if old_default_originate -%}
+{% endif %}
+{% if old_default_originate %}
no default-information originate
-{% endif -%}
-{% if default_originate -%}
+{% endif %}
+{% if default_originate %}
default-information originate
-{% endif -%}
-{% if old_rip.default_metric -%}
+{% endif %}
+{% if old_rip.default_metric %}
no default-metric {{old_rip.default_metric}}
-{% endif -%}
-{% if rip.default_metric -%}
+{% endif %}
+{% if rip.default_metric %}
default-metric {{rip.default_metric}}
-{% endif -%}
-{% for protocol in old_rip.redist -%}
-{% if old_rip.redist[protocol]['metric'] and old_rip.redist[protocol]['route_map'] -%}
+{% endif %}
+{% for protocol in old_rip.redist %}
+{% if old_rip.redist[protocol]['metric'] and old_rip.redist[protocol]['route_map'] %}
no redistribute {{protocol}} metric {{rip.redist[protocol]['metric']}} route-map {{rip.redist[protocol]['route_map']}}
-{% elif old_rip.redist[protocol]['metric'] -%}
+{% elif old_rip.redist[protocol]['metric'] %}
no redistribute {{protocol}} metric {{old_rip.redist[protocol]['metric']}}
-{% elif old_rip.redist[protocol]['route_map'] -%}
+{% elif old_rip.redist[protocol]['route_map'] %}
no redistribute {{protocol}} route-map {{old_rip.redist[protocol]['route_map']}}
-{% else -%}
+{% else %}
no redistribute {{protocol}}
-{% endif -%}
-{% endfor -%}
-{% for protocol in rip.redist -%}
-{% if rip.redist[protocol]['metric'] and rip.redist[protocol]['route_map'] -%}
+{% endif %}
+{% endfor %}
+{% for protocol in rip.redist %}
+{% if rip.redist[protocol]['metric'] and rip.redist[protocol]['route_map'] %}
redistribute {{protocol}} metric {{rip.redist[protocol]['metric']}} route-map {{rip.redist[protocol]['route_map']}}
-{% elif rip.redist[protocol]['metric'] -%}
+{% elif rip.redist[protocol]['metric'] %}
redistribute {{protocol}} metric {{rip.redist[protocol]['metric']}}
-{% elif rip.redist[protocol]['route_map'] -%}
+{% elif rip.redist[protocol]['route_map'] %}
redistribute {{protocol}} route-map {{rip.redist[protocol]['route_map']}}
-{% else -%}
+{% else %}
redistribute {{protocol}}
-{% endif -%}
-{% endfor -%}
-{% for iface in old_rip.distribute -%}
-{% if old_rip.distribute[iface].iface_access_list_in -%}
+{% endif %}
+{% endfor %}
+{% for iface in old_rip.distribute %}
+{% if old_rip.distribute[iface].iface_access_list_in %}
no distribute-list {{old_rip.distribute[iface].iface_access_list_in}} in {{iface}}
-{% endif -%}
-{% if old_rip.distribute[iface].iface_access_list_out -%}
+{% endif %}
+{% if old_rip.distribute[iface].iface_access_list_out %}
no distribute-list {{old_rip.distribute[iface].iface_access_list_out}} out {{iface}}
-{% endif -%}
-{% if old_rip.distribute[iface].iface_prefix_list_in -%}
+{% endif %}
+{% if old_rip.distribute[iface].iface_prefix_list_in %}
no distribute-list prefix {{old_rip.distribute[iface].iface_prefix_list_in}} in {{iface}}
-{% endif -%}
-{% if old_rip.distribute[iface].iface_prefix_list_out -%}
+{% endif %}
+{% if old_rip.distribute[iface].iface_prefix_list_out %}
no distribute-list prefix {{old_rip.distribute[iface].iface_prefix_list_out}} out {{iface}}
-{% endif -%}
-{% endfor -%}
-{% for iface in rip.distribute -%}
-{% if rip.distribute[iface].iface_access_list_in -%}
+{% endif %}
+{% endfor %}
+{% for iface in rip.distribute %}
+{% if rip.distribute[iface].iface_access_list_in %}
distribute-list {{rip.distribute[iface].iface_access_list_in}} in {{iface}}
-{% endif -%}
-{% if rip.distribute[iface].iface_access_list_out -%}
+{% endif %}
+{% if rip.distribute[iface].iface_access_list_out %}
distribute-list {{rip.distribute[iface].iface_access_list_out}} out {{iface}}
-{% endif -%}
-{% if rip.distribute[iface].iface_prefix_list_in -%}
+{% endif %}
+{% if rip.distribute[iface].iface_prefix_list_in %}
distribute-list prefix {{rip.distribute[iface].iface_prefix_list_in}} in {{iface}}
-{% endif -%}
-{% if rip.distribute[iface].iface_prefix_list_out -%}
+{% endif %}
+{% if rip.distribute[iface].iface_prefix_list_out %}
distribute-list prefix {{rip.distribute[iface].iface_prefix_list_out}} out {{iface}}
-{% endif -%}
-{% endfor -%}
-{% if old_rip.dist_acl_in -%}
+{% endif %}
+{% endfor %}
+{% if old_rip.dist_acl_in %}
no distribute-list {{old_rip.dist_acl_in}} in
-{% endif -%}
-{% if rip.dist_acl_in -%}
+{% endif %}
+{% if rip.dist_acl_in %}
distribute-list {{rip.dist_acl_in}} in
-{% endif -%}
-{% if old_rip.dist_acl_out -%}
+{% endif %}
+{% if old_rip.dist_acl_out %}
no distribute-list {{old_rip.dist_acl_out}} out
-{% endif -%}
-{% if rip.dist_acl_out -%}
+{% endif %}
+{% if rip.dist_acl_out %}
distribute-list {{rip.dist_acl_out}} out
-{% endif -%}
-{% if old_rip.dist_prfx_in -%}
+{% endif %}
+{% if old_rip.dist_prfx_in %}
no distribute-list prefix {{old_rip.dist_prfx_in}} in
-{% endif -%}
-{% if rip.dist_prfx_in -%}
+{% endif %}
+{% if rip.dist_prfx_in %}
distribute-list prefix {{rip.dist_prfx_in}} in
-{% endif -%}
-{% if old_rip.dist_prfx_out -%}
+{% endif %}
+{% if old_rip.dist_prfx_out %}
no distribute-list prefix {{old_rip.dist_prfx_out}} out
-{% endif -%}
-{% if rip.dist_prfx_out -%}
+{% endif %}
+{% if rip.dist_prfx_out %}
distribute-list prefix {{rip.dist_prfx_out}} out
-{% endif -%}
-{% for network in old_rip.networks -%}
+{% endif %}
+{% for network in old_rip.networks %}
no network {{network}}
-{% endfor -%}
-{% for network in rip.networks -%}
+{% endfor %}
+{% for network in rip.networks %}
network {{network}}
-{% endfor -%}
-{% for iface in old_rip.ifaces -%}
+{% endfor %}
+{% for iface in old_rip.ifaces %}
no network {{iface}}
-{% endfor -%}
-{% for iface in rip.ifaces -%}
+{% endfor %}
+{% for iface in rip.ifaces %}
network {{iface}}
-{% endfor -%}
-{% for neighbor in old_rip.neighbors -%}
+{% endfor %}
+{% for neighbor in old_rip.neighbors %}
no neighbor {{neighbor}}
-{% endfor -%}
-{% for neighbor in rip.neighbors -%}
+{% endfor %}
+{% for neighbor in rip.neighbors %}
neighbor {{neighbor}}
-{% endfor -%}
-{% for net in rip.net_distance -%}
-{% if rip.net_distance[net].access_list and rip.net_distance[net].distance -%}
+{% endfor %}
+{% for net in rip.net_distance %}
+{% if rip.net_distance[net].access_list and rip.net_distance[net].distance %}
distance {{rip.net_distance[net].distance}} {{net}} {{rip.net_distance[net].access_list}}
-{% else -%}
+{% else %}
distance {{rip.net_distance[net].distance}} {{net}}
-{% endif -%}
-{% endfor -%}
-{% for passive_iface in old_rip.passive_iface -%}
+{% endif %}
+{% endfor %}
+{% for passive_iface in old_rip.passive_iface %}
no passive-interface {{passive_iface}}
-{% endfor -%}
-{% for passive_iface in rip.passive_iface -%}
+{% endfor %}
+{% for passive_iface in rip.passive_iface %}
passive-interface {{passive_iface}}
-{% endfor -%}
-{% for route in old_rip.route -%}
+{% endfor %}
+{% for route in old_rip.route %}
no route {{route}}
-{% endfor -%}
-{% for route in rip.route -%}
+{% endfor %}
+{% for route in rip.route %}
route {{route}}
-{% endfor -%}
-{% if old_rip.timer_update or old_rip.timer_timeout or old_rip.timer_garbage -%}
+{% endfor %}
+{% if old_rip.timer_update or old_rip.timer_timeout or old_rip.timer_garbage %}
no timers basic
-{% endif -%}
-{% if rip.timer_update or rip.timer_timeout or rip.timer_garbage -%}
+{% endif %}
+{% if rip.timer_update or rip.timer_timeout or rip.timer_garbage %}
timers basic {{rip.timer_update}} {{rip.timer_timeout}} {{rip.timer_garbage}}
-{% endif -%}
+{% endif %}
!
-{% else -%}
+{% else %}
no router rip
!
-{% endif -%}
+{% endif %}
diff --git a/data/templates/frr/static_mcast.frr.tmpl b/data/templates/frr/static_mcast.frr.tmpl
index 86d619ab0..38635af32 100644
--- a/data/templates/frr/static_mcast.frr.tmpl
+++ b/data/templates/frr/static_mcast.frr.tmpl
@@ -1,20 +1,20 @@
!
-{% for route_gr in old_mroute -%}
-{% for nh in old_mroute[route_gr] -%}
-{% if old_mroute[route_gr][nh] -%}
+{% for route_gr in old_mroute %}
+{% for nh in old_mroute[route_gr] %}
+{% if old_mroute[route_gr][nh] %}
no ip mroute {{ route_gr }} {{ nh }} {{ old_mroute[route_gr][nh] }}
-{% else -%}
+{% else %}
no ip mroute {{ route_gr }} {{ nh }}
-{% endif -%}
-{% endfor -%}
-{% endfor -%}
-{% for route_gr in mroute -%}
-{% for nh in mroute[route_gr] -%}
-{% if mroute[route_gr][nh] -%}
+{% endif %}
+{% endfor %}
+{% endfor %}
+{% for route_gr in mroute %}
+{% for nh in mroute[route_gr] %}
+{% if mroute[route_gr][nh] %}
ip mroute {{ route_gr }} {{ nh }} {{ mroute[route_gr][nh] }}
-{% else -%}
+{% else %}
ip mroute {{ route_gr }} {{ nh }}
-{% endif -%}
-{% endfor -%}
-{% endfor -%}
+{% endif %}
+{% endfor %}
+{% endfor %}
!
diff --git a/data/templates/ids/fastnetmon.tmpl b/data/templates/ids/fastnetmon.tmpl
index 71a1b2bd7..1f6a1c808 100644
--- a/data/templates/ids/fastnetmon.tmpl
+++ b/data/templates/ids/fastnetmon.tmpl
@@ -27,34 +27,34 @@ enable_subnet_counters = off
{% if "mirror" in mode %}
mirror_afpacket = on
-{% endif -%}
+{% endif %}
{% if "in" in direction %}
process_incoming_traffic = on
-{% endif -%}
+{% endif %}
{% if "out" in direction %}
process_outgoing_traffic = on
-{% endif -%}
+{% endif %}
{% for th in threshold %}
{% if th == "fps" %}
ban_for_flows = on
threshold_flows = {{ threshold[th] }}
-{% endif -%}
+{% endif %}
{% if th == "mbps" %}
ban_for_bandwidth = on
threshold_mbps = {{ threshold[th] }}
-{% endif -%}
+{% endif %}
{% if th == "pps" %}
ban_for_pps = on
threshold_pps = {{ threshold[th] }}
-{% endif -%}
-{% endfor -%}
+{% endif %}
+{% endfor %}
{% if listen_interface %}
{% set value = listen_interface if listen_interface is string else listen_interface | join(',') %}
interfaces = {{ value }}
-{% endif -%}
+{% endif %}
{% if alert_script %}
notify_script_path = {{ alert_script }}
-{% endif -%}
+{% endif %}
diff --git a/data/templates/igmp-proxy/igmpproxy.conf.tmpl b/data/templates/igmp-proxy/igmpproxy.conf.tmpl
index c7fc5cef5..e3966def3 100644
--- a/data/templates/igmp-proxy/igmpproxy.conf.tmpl
+++ b/data/templates/igmp-proxy/igmpproxy.conf.tmpl
@@ -2,36 +2,39 @@
#
# autogenerated by igmp_proxy.py
#
-# The configuration file must define one upstream
-# interface, and one or more downstream interfaces.
+# 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...)
+# If multicast traffic originates outside the upstream subnet, the "altnet"
+# option can be used in order to define legal multicast sources.
#
-# 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.
+# 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 -%}
+{% if disable_quickleave is not defined %}
quickleave
-{% endif -%}
+{% endif %}
+{% if interface is defined and interface is not none %}
+{% for iface, config in interface.items() %}
-{% 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 %}
+# Configuration for {{ iface }} ({{ config.role }} interface)
+{% if config.role == 'disabled' %}
+phyint {{ iface }} disabled
+{% else %}
+phyint {{ iface }} {{ config.role }} ratelimit 0 threshold {{ config.threshold }}
+{% endif %}
+{% if config.alt_subnet is defined and config.alt_subnet is not none %}
+{% for subnet in config.alt_subnet %}
altnet {{ subnet }}
-{%- endfor %}
-{%- for subnet in interface.whitelist %}
+{% endfor %}
+{% endif %}
+{% if config.whitelist is defined and config.whitelist is not none %}
+{% for subnet in config.whitelist %}
whitelist {{ subnet }}
-{%- endfor %}
-{% endfor %}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
diff --git a/data/templates/lldp/vyos.conf.tmpl b/data/templates/lldp/vyos.conf.tmpl
index e724f42c6..07bbaf604 100644
--- a/data/templates/lldp/vyos.conf.tmpl
+++ b/data/templates/lldp/vyos.conf.tmpl
@@ -2,19 +2,19 @@
configure system platform VyOS
configure system description "VyOS {{ options.description }}"
-{% if options.listen_on -%}
+{% 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 -%}
+{% endif %}
+{% if options.mgmt_addr %}
configure system ip management pattern {{ options.mgmt_addr | join(",") }}
-{%- endif %}
-{%- for loc in location -%}
-{%- if loc.elin %}
+{% endif %}
+{% for loc in location %}
+{% if loc.elin %}
configure ports {{ loc.name }} med location elin "{{ loc.elin }}"
-{%- endif %}
-{%- if loc.coordinate_based %}
+{% 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 %}
+{% endif %}
{% endfor %}
diff --git a/data/templates/netflow/uacctd.conf.tmpl b/data/templates/netflow/uacctd.conf.tmpl
index d8615566f..fdf96e7c3 100644
--- a/data/templates/netflow/uacctd.conf.tmpl
+++ b/data/templates/netflow/uacctd.conf.tmpl
@@ -8,62 +8,62 @@ 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 %}
+{% if templatecfg['syslog-facility'] != none %}
syslog: {{ templatecfg['syslog-facility'] }}
-{%- endif %}
-{%- if templatecfg['disable-imt'] == none %}
+{% endif %}
+{% if templatecfg['disable-imt'] == none %}
imt_path: /tmp/uacctd.pipe
imt_mem_pools_number: 169
-{%- endif %}
+{% endif %}
plugins:
-{%- if templatecfg['netflow']['servers'] != none -%}
+{% 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 -%}
+ {% 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'] -%}
+{% 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 -%}
+ {% 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'] %}
+{% 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 %}
+{% if templatecfg['netflow']['engine-id'] != none %}
nfprobe_engine[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['engine-id'] }}
-{%- endif %}
-{%- if templatecfg['netflow']['max-flows'] != none %}
+{% endif %}
+{% if templatecfg['netflow']['max-flows'] != none %}
nfprobe_maxflows[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['max-flows'] }}
-{%- endif %}
-{%- if templatecfg['netflow']['sampling-rate'] != none %}
+{% endif %}
+{% if templatecfg['netflow']['sampling-rate'] != none %}
sampling_rate[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['sampling-rate'] }}
-{%- endif %}
-{%- if templatecfg['netflow']['source-ip'] != none %}
+{% endif %}
+{% if templatecfg['netflow']['source-ip'] != none %}
nfprobe_source_ip[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['source-ip'] }}
-{%- endif %}
-{%- if templatecfg['netflow']['timeout_string'] != '' %}
+{% 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'] %}
+{% 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 %}
+{% if templatecfg['sflow']['sampling-rate'] != none %}
sampling_rate[sf_{{ server['address'] }}]: {{ templatecfg['sflow']['sampling-rate'] }}
-{%- endif %}
-{%- endfor %}
+{% endif %}
+{% endfor %}
{% endif %}
diff --git a/data/templates/ntp/ntp.conf.tmpl b/data/templates/ntp/ntp.conf.tmpl
index bb0067bfb..3f319c89b 100644
--- a/data/templates/ntp/ntp.conf.tmpl
+++ b/data/templates/ntp/ntp.conf.tmpl
@@ -13,13 +13,13 @@ restrict -6 ::1
#
# Configurable section
#
-{% if server %}
-{% for srv in server %}
-{% set options = '' %}
-{% set options = options + 'noselect ' if server[srv].noselect is defined else '' %}
-{% set options = options + 'preempt ' if server[srv].preempt is defined else '' %}
-{% set options = options + 'prefer ' if server[srv].prefer is defined else '' %}
-server {{ srv | replace('_', '-') }} iburst {{ options }}
+{% if server is defined and server is not none %}
+{% for server, config in server.items() %}
+{% set association = 'server' %}
+{% if config.pool is defined %}
+{% set association = 'pool' %}
+{% endif %}
+{{ association }} {{ server | replace('_', '-') }} iburst {{ 'noselect' if config.noselect is defined }} {{ 'preempt' if config.preempt is defined }} {{ 'prefer' if config.prefer is defined }}
{% endfor %}
{% endif %}
diff --git a/data/templates/openvpn/auth.pw.tmpl b/data/templates/openvpn/auth.pw.tmpl
new file mode 100644
index 000000000..9b20c9742
--- /dev/null
+++ b/data/templates/openvpn/auth.pw.tmpl
@@ -0,0 +1,5 @@
+{# Autogenerated by interfaces-openvpn.py #}
+{% if authentication is defined and authentication is not none %}
+{{ authentication.username }}
+{{ authentication.password }}
+{% endif %}
diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl
index 1fdf6b848..a1daaa078 100644
--- a/data/templates/openvpn/server.conf.tmpl
+++ b/data/templates/openvpn/server.conf.tmpl
@@ -14,9 +14,9 @@ dev {{ ifname }}
persist-key
iproute /usr/libexec/vyos/system/unpriv-ip
{% if protocol == 'tcp-active' %}
-proto tcp6-client
+proto tcp-client
{% elif protocol == 'tcp-passive' %}
-proto tcp6-server
+proto tcp-server
{% else %}
proto udp
{% endif %}
@@ -52,13 +52,14 @@ push "redirect-gateway def1"
compress lzo
{% endif %}
-{% if 'client' in mode %}
+{% if mode == 'client' %}
#
# OpenVPN Client mode
#
client
nobind
-{% elif 'server' in mode %}
+
+{% elif mode == 'server' %}
#
# OpenVPN Server mode
#
@@ -94,7 +95,7 @@ max-clients {{ server.max_connections }}
client-config-dir /run/openvpn/ccd/{{ ifname }}
{% endif %}
{% endif %}
-keepalive {{ keep_alive.interval }} {{ keep_alive.failure_count }}
+keepalive {{ keep_alive.interval }} {{ keep_alive.interval|int * keep_alive.failure_count|int }}
management /run/openvpn/openvpn-mgmt-intf unix
{% if server is defined and server is not none %}
{% if server.reject_unconfigured_clients is defined %}
@@ -129,6 +130,7 @@ push "route-ipv6 {{ route6 }}"
push "dhcp-option DNS6 {{ ns6 }}"
{% endfor %}
{% endif %}
+
{% else %}
#
# OpenVPN site-2-site mode
@@ -136,19 +138,24 @@ push "dhcp-option DNS6 {{ ns6 }}"
ping {{ keep_alive.interval }}
ping-restart {{ keep_alive.failure_count }}
-{% for laddr, laddr_conf in local_address.items() if laddr | is_ipv4 %}
-{% if laddr_conf is defined and laddr_conf.subnet_mask is defined and laddr_conf.subnet_mask is not none %}
+{% if device_type == 'tap' %}
+{% for laddr, laddr_conf in local_address.items() if laddr | is_ipv4 %}
+{% if laddr_conf is defined and laddr_conf.subnet_mask is defined and laddr_conf.subnet_mask is not none %}
ifconfig {{ laddr }} {{ laddr_conf.subnet_mask }}
-{% else %}
-{% for raddr in remote_address %}
-{% if raddr | is_ipv4 %}
+{% endif %}
+{% endfor %}
+{% else %}
+{% for laddr in local_address if laddr | is_ipv4 %}
+{% for raddr in remote_address if raddr | is_ipv4 %}
ifconfig {{ laddr }} {{ raddr }}
-{% else %}
+{% endfor %}
+{% endfor %}
+{% for laddr in local_address if laddr | is_ipv6 %}
+{% for raddr in remote_address if raddr | is_ipv6 %}
ifconfig-ipv6 {{ laddr }} {{ raddr }}
-{% endif %}
{% endfor %}
-{% endif %}
-{% endfor %}
+{% endfor %}
+{% endif %}
{% endif %}
{% if tls is defined and tls is not none %}
diff --git a/data/templates/pppoe/ip-pre-up.script.tmpl b/data/templates/pppoe/ip-pre-up.script.tmpl
index cf85ed067..a54e4e9bd 100644
--- a/data/templates/pppoe/ip-pre-up.script.tmpl
+++ b/data/templates/pppoe/ip-pre-up.script.tmpl
@@ -12,7 +12,7 @@ logger -t pppd[$DIALER_PID] "executing $0"
echo "{{ description }}" > /sys/class/net/{{ ifname }}/ifalias
-{% if vrf -%}
+{% if vrf %}
logger -t pppd[$DIALER_PID] "configuring dialer interface $6 for VRF {{ vrf }}"
ip link set dev {{ ifname }} master {{ vrf }}
{% endif %}
diff --git a/data/templates/pppoe/ip-up.script.tmpl b/data/templates/pppoe/ip-up.script.tmpl
index 568e21c4e..302756960 100644
--- a/data/templates/pppoe/ip-up.script.tmpl
+++ b/data/templates/pppoe/ip-up.script.tmpl
@@ -11,7 +11,7 @@ fi
DIALER_PID=$(cat /var/run/{{ ifname }}.pid)
logger -t pppd[$DIALER_PID] "executing $0"
-{% if default_route != 'none' -%}
+{% if default_route != 'none' %}
# See https://phabricator.vyos.net/T2248 & T2220. Determine if we are enslaved
# to a VRF, this is needed to properly insert the default route.
@@ -28,14 +28,14 @@ if [ -d /sys/class/net/{{ ifname }}/upper_* ]; then
VRF_NAME="vrf ${VRF}"
fi
-{% if default_route == 'auto' -%}
+{% if default_route == 'auto' %}
# 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 default_route == 'force' -%}
+{% elif default_route == 'force' %}
# 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}"
diff --git a/data/templates/pppoe/ipv6-up.script.tmpl b/data/templates/pppoe/ipv6-up.script.tmpl
index d0a62478c..ed6102d6c 100644
--- a/data/templates/pppoe/ipv6-up.script.tmpl
+++ b/data/templates/pppoe/ipv6-up.script.tmpl
@@ -7,7 +7,7 @@ if [ "$6" != "{{ ifname }}" ]; then
exit
fi
-{% if ipv6 is defined and ipv6.address is defined and ipv6.address.autoconf is defined -%}
+{% if ipv6 is defined and ipv6.address is defined and ipv6.address.autoconf is defined %}
# add some info to syslog
DIALER_PID=$(cat /var/run/{{ ifname }}.pid)
logger -t pppd[$DIALER_PID] "executing $0"
@@ -45,7 +45,7 @@ echo 1 > /proc/sys/net/ipv6/conf/{{ ifname }}/autoconf
systemctl start dhcp6c@{{ ifname }}.service
{% endif %}
-{% if default_route != 'none' -%}
+{% if default_route != 'none' %}
# See https://phabricator.vyos.net/T2248 & T2220. Determine if we are enslaved
# to a VRF, this is needed to properly insert the default route.
@@ -62,14 +62,14 @@ if [ -d /sys/class/net/{{ ifname }}/upper_* ]; then
VRF_NAME="vrf ${VRF}"
fi
-{% if default_route == 'auto' -%}
+{% if default_route == 'auto' %}
# 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 | wc -l)
if [ "$routes" -ne 0 ]; then
exit 1
fi
-{% elif default_route == 'force' -%}
+{% elif default_route == 'force' %}
# Retrieve current static default routes and remove it from the routing table
vtysh -c "show running-config" | sed -n "/${SED_OPT}/,/!/p" | grep ::/0 | while read route ; do
vtysh -c "conf t" ${VTY_OPT} -c "no ${route} ${VRF_NAME}"
diff --git a/data/templates/pppoe/peer.tmpl b/data/templates/pppoe/peer.tmpl
index e909843a5..dd4272a98 100644
--- a/data/templates/pppoe/peer.tmpl
+++ b/data/templates/pppoe/peer.tmpl
@@ -53,12 +53,12 @@ mru {{ mtu }}
{{ "usepeerdns" if no_peer_dns is not defined }}
-{% if ipv6 is defined and ipv6.enable is defined -%}
+{% if ipv6 is defined and ipv6.enable is defined %}
+ipv6
ipv6cp-use-ipaddr
{% endif %}
-{% if service_name is defined -%}
+{% if service_name is defined %}
rp_pppoe_service "{{ service_name }}"
{% endif %}
@@ -67,9 +67,9 @@ demand
# See T2249. PPP default route options should only be set when in on-demand
# mode. As soon as we are not in on-demand mode the default-route handling is
# passed to the ip-up.d/ip-down.s scripts which is required for VRF support.
-{% if 'auto' in default_route -%}
+{% if 'auto' in default_route %}
defaultroute
-{% elif 'force' in default_route -%}
+{% elif 'force' in default_route %}
defaultroute
replacedefaultroute
{% endif %}
diff --git a/data/templates/router-advert/radvd.conf.tmpl b/data/templates/router-advert/radvd.conf.tmpl
index cebfc54b5..2fde78fec 100644
--- a/data/templates/router-advert/radvd.conf.tmpl
+++ b/data/templates/router-advert/radvd.conf.tmpl
@@ -43,5 +43,5 @@ interface {{ iface }} {
};
{% endif %}
};
-{% endfor -%}
+{% endfor %}
{% endif %}
diff --git a/data/templates/salt-minion/minion.tmpl b/data/templates/salt-minion/minion.tmpl
index 9369573a4..405fb9131 100644
--- a/data/templates/salt-minion/minion.tmpl
+++ b/data/templates/salt-minion/minion.tmpl
@@ -35,7 +35,7 @@ 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 -%}
+{% for host in master %}
- {{ host }}
{% endfor %}
diff --git a/data/templates/snmp/usr.snmpd.conf.tmpl b/data/templates/snmp/usr.snmpd.conf.tmpl
index 9c0337fa8..e2c5ec102 100644
--- a/data/templates/snmp/usr.snmpd.conf.tmpl
+++ b/data/templates/snmp/usr.snmpd.conf.tmpl
@@ -1,6 +1,6 @@
### Autogenerated by snmp.py ###
-{%- for u in v3_users %}
+{% for u in v3_users %}
{{ u.mode }}user {{ u.name }}
-{%- endfor %}
+{% endfor %}
rwuser {{ vyos_user }}
diff --git a/data/templates/snmp/var.snmpd.conf.tmpl b/data/templates/snmp/var.snmpd.conf.tmpl
index 6cbc687ef..c779587df 100644
--- a/data/templates/snmp/var.snmpd.conf.tmpl
+++ b/data/templates/snmp/var.snmpd.conf.tmpl
@@ -1,14 +1,14 @@
### Autogenerated by snmp.py ###
# user
-{%- for u in v3_users %}
-{%- if u.authOID == 'none' %}
+{% for u in v3_users %}
+{% if u.authOID == 'none' %}
createUser {{ u.name }}
-{%- else %}
+{% else %}
usmUser 1 3 0x{{ v3_engineid }} "{{ u.name }}" "{{ u.name }}" NULL {{ u.authOID }} 0x{{ u.authMasterKey }} {{ u.privOID }} 0x{{ u.privMasterKey }} 0x
-{%- endif %}
-{%- endfor %}
+{% endif %}
+{% endfor %}
createUser {{ vyos_user }} MD5 "{{ vyos_user_pass }}" DES
-{%- if v3_engineid %}
+{% if v3_engineid %}
oldEngineID 0x{{ v3_engineid }}
-{%- endif %}
+{% endif %}
diff --git a/data/templates/syslog/rsyslog.conf.tmpl b/data/templates/syslog/rsyslog.conf.tmpl
index a610d132f..10fbb9d3c 100644
--- a/data/templates/syslog/rsyslog.conf.tmpl
+++ b/data/templates/syslog/rsyslog.conf.tmpl
@@ -1,14 +1,14 @@
## generated by syslog.py ##
## file based logging
-{% if files['global']['marker'] -%}
+{% if files['global']['marker'] %}
$ModLoad immark
{% if files['global']['marker-interval'] %}
$MarkMessagePeriod {{files['global']['marker-interval']}}
{% endif %}
-{% endif -%}
-{% if files['global']['preserver_fqdn'] -%}
+{% endif %}
+{% if files['global']['preserver_fqdn'] %}
$PreserveFQDN on
-{% endif -%}
+{% 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}}
diff --git a/data/templates/vrf/vrf.conf.tmpl b/data/templates/vrf/vrf.conf.tmpl
index 761b0bb6f..6d01d2b89 100644
--- a/data/templates/vrf/vrf.conf.tmpl
+++ b/data/templates/vrf/vrf.conf.tmpl
@@ -3,6 +3,6 @@
# Routing table ID to name mapping reference
# id vrf name comment
-{% for vrf in vrf_add -%}
+{% for vrf in vrf_add %}
{{ "%-10s" | format(vrf.table) }} {{ "%-16s" | format(vrf.name) }} # {{ vrf.description }}
-{% endfor -%}
+{% endfor %}
diff --git a/data/templates/vrrp/keepalived.conf.tmpl b/data/templates/vrrp/keepalived.conf.tmpl
index 210621681..d51522e45 100644
--- a/data/templates/vrrp/keepalived.conf.tmpl
+++ b/data/templates/vrrp/keepalived.conf.tmpl
@@ -9,9 +9,9 @@ global_defs {
notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py
}
-{% for group in groups -%}
+{% for group in groups %}
-{% if group.health_check_script -%}
+{% if group.health_check_script %}
vrrp_script healthcheck_{{ group.name }} {
script "{{ group.health_check_script }}"
interval {{ group.health_check_interval }}
@@ -22,9 +22,9 @@ vrrp_script healthcheck_{{ group.name }} {
{% endif %}
vrrp_instance {{ group.name }} {
- {% if group.description -%}
+ {% if group.description %}
# {{ group.description }}
- {% endif -%}
+ {% endif %}
state BACKUP
interface {{ group.interface }}
@@ -32,74 +32,74 @@ vrrp_instance {{ group.name }} {
priority {{ group.priority }}
advert_int {{ group.advertise_interval }}
- {% if group.preempt -%}
+ {% if group.preempt %}
preempt_delay {{ group.preempt_delay }}
- {% else -%}
+ {% else %}
nopreempt
- {% endif -%}
+ {% endif %}
- {% if group.peer_address -%}
+ {% if group.peer_address %}
unicast_peer { {{ group.peer_address }} }
- {% endif -%}
+ {% endif %}
- {% if group.hello_source -%}
- {%- if group.peer_address -%}
+ {% if group.hello_source %}
+ {% if group.peer_address %}
unicast_src_ip {{ group.hello_source }}
- {%- else -%}
+ {% else %}
mcast_src_ip {{ group.hello_source }}
- {%- endif %}
- {% endif -%}
+ {% endif %}
+ {% endif %}
- {% if group.use_vmac and group.peer_address -%}
+ {% if group.use_vmac and group.peer_address %}
use_vmac {{group.interface}}v{{group.vrid}}
vmac_xmit_base
- {% elif group.use_vmac -%}
+ {% elif group.use_vmac %}
use_vmac {{group.interface}}v{{group.vrid}}
- {% endif -%}
+ {% endif %}
- {% if group.auth_password -%}
+ {% if group.auth_password %}
authentication {
auth_pass "{{ group.auth_password }}"
auth_type {{ group.auth_type }}
}
- {% endif -%}
+ {% endif %}
virtual_ipaddress {
- {% for addr in group.virtual_addresses -%}
+ {% for addr in group.virtual_addresses %}
{{ addr }}
- {% endfor -%}
+ {% endfor %}
}
- {% if group.virtual_addresses_excluded -%}
+ {% if group.virtual_addresses_excluded %}
virtual_ipaddress_excluded {
- {% for addr in group.virtual_addresses_excluded -%}
+ {% for addr in group.virtual_addresses_excluded %}
{{ addr }}
- {% endfor -%}
+ {% endfor %}
}
- {% endif -%}
+ {% endif %}
- {% if group.health_check_script -%}
+ {% if group.health_check_script %}
track_script {
healthcheck_{{ group.name }}
}
- {% endif -%}
+ {% endif %}
}
-{% endfor -%}
+{% endfor %}
-{% for sync_group in sync_groups -%}
+{% for sync_group in sync_groups %}
vrrp_sync_group {{ sync_group.name }} {
group {
- {% for member in sync_group.members -%}
+ {% for member in sync_group.members %}
{{ member }}
- {% endfor -%}
+ {% endfor %}
}
- {% if sync_group.conntrack_sync -%}
+ {% 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 -%}
+ {% endif %}
}
-{% endfor -%}
+{% endfor %}
diff --git a/data/templates/vyos-hostsd/hosts.tmpl b/data/templates/vyos-hostsd/hosts.tmpl
index 566f9a5dd..be8692104 100644
--- a/data/templates/vyos-hostsd/hosts.tmpl
+++ b/data/templates/vyos-hostsd/hosts.tmpl
@@ -12,15 +12,15 @@ ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
-{% if hosts -%}
+{% if hosts %}
# From 'system static-host-mapping' and DHCP server
-{%- for tag, taghosts in hosts.items() %}
+{% for tag, taghosts in hosts.items() %}
# {{ tag }}
-{%- for host, hostprops in taghosts.items() %}
-{%- if hostprops['address'] %}
+{% for host, hostprops in taghosts.items() %}
+{% if hostprops['address'] %}
{{ hostprops['address'] }} {{ host }}{% for a in hostprops['aliases'] %} {{ a }}{% endfor %}
-{%- endif %}
-{%- endfor %}
-{%- endfor %}
-{%- endif %}
+{% endif %}
+{% endfor %}
+{% endfor %}
+{% endif %}
diff --git a/data/templates/vyos-hostsd/resolv.conf.tmpl b/data/templates/vyos-hostsd/resolv.conf.tmpl
index b920b2e5f..58a5f9312 100644
--- a/data/templates/vyos-hostsd/resolv.conf.tmpl
+++ b/data/templates/vyos-hostsd/resolv.conf.tmpl
@@ -1,26 +1,25 @@
### Autogenerated by VyOS ###
### Do not edit, your changes will get overwritten ###
-{#- the code below ensures the order of nameservers is determined first by #}
+{# the code below ensures the order of nameservers is determined first by #}
{# the order of tags, then by the order of nameservers within that tag #}
-{%- for tag in name_server_tags_system %}
-{%- if tag in name_servers %}
+{% for tag in name_server_tags_system %}
+{% if tag in name_servers %}
# {{ tag }}
-{%- for ns in name_servers[tag] %}
+{% for ns in name_servers[tag] %}
nameserver {{ ns }}
-{%- endfor %}
-{%- endif %}
-{%- endfor %}
+{% endfor %}
+{% endif %}
+{% endfor %}
-{%- if domain_name %}
+{% if domain_name %}
domain {{ domain_name }}
-{%- endif %}
+{% endif %}
{% for tag in name_server_tags_system %}
-{%- if tag in search_domains %}
+{% if tag in search_domains %}
# {{ tag }}
search {{ search_domains[tag]|join(' ') }}
-{%- endif %}
-{%- endfor %}
-
+{% endif %}
+{% endfor %}
diff --git a/data/templates/wifi/hostapd.conf.tmpl b/data/templates/wifi/hostapd.conf.tmpl
index 16d9f7c98..e66e3472b 100644
--- a/data/templates/wifi/hostapd.conf.tmpl
+++ b/data/templates/wifi/hostapd.conf.tmpl
@@ -451,14 +451,6 @@ macaddr_acl=0
max_num_sta={{ max_stations }}
{% endif %}
-{% if wds is defined %}
-# WDS (4-address frame) mode with per-station virtual interfaces
-# (only supported with driver=nl80211)
-# This mode allows associated stations to use 4-address frames to allow layer 2
-# bridging to be used.
-wds_sta=1
-{% endif %}
-
{% if isolate_stations is defined %}
# Client isolation can be used to prevent low-level bridging of frames between
# associated stations in the BSS. By default, this bridging is allowed.
diff --git a/data/templates/wwan/ip-pre-up.script.tmpl b/data/templates/wwan/ip-pre-up.script.tmpl
index efc065bad..199150947 100644
--- a/data/templates/wwan/ip-pre-up.script.tmpl
+++ b/data/templates/wwan/ip-pre-up.script.tmpl
@@ -17,7 +17,7 @@ logger -t pppd[$DIALER_PID] "executing $0"
echo "{{ description }}" > /sys/class/net/{{ ifname }}/ifalias
-{% if vrf -%}
+{% if vrf %}
logger -t pppd[$DIALER_PID] "configuring interface {{ ifname }} for VRF {{ vrf }}"
ip link set dev {{ ifname }} master {{ vrf }}
{% endif %}
diff --git a/debian/control b/debian/control
index a290952a3..5e9e708b4 100644
--- a/debian/control
+++ b/debian/control
@@ -114,7 +114,6 @@ Depends:
vyos-utils,
wide-dhcpv6-client,
wireguard-tools,
- wireguard-modules,
wireless-regdb,
wpasupplicant (>= 0.6.7)
Description: VyOS configuration scripts and data
diff --git a/interface-definitions/dhcp-relay.xml.in b/interface-definitions/dhcp-relay.xml.in
index b83402aa1..8c95239d9 100644
--- a/interface-definitions/dhcp-relay.xml.in
+++ b/interface-definitions/dhcp-relay.xml.in
@@ -35,6 +35,7 @@
</constraint>
<constraintErrorMessage>hop-count must be a value between 1 and 255</constraintErrorMessage>
</properties>
+ <defaultValue>10</defaultValue>
</leafNode>
<leafNode name="max-size">
<properties>
@@ -72,9 +73,10 @@
<description>discard packet (default action if giaddr not set in packet)</description>
</valueHelp>
<constraint>
- <regex>(append|replace|forward|discard)</regex>
+ <regex>^(append|replace|forward|discard)$</regex>
</constraint>
</properties>
+ <defaultValue>forward</defaultValue>
</leafNode>
</children>
</node>
diff --git a/interface-definitions/dhcpv6-relay.xml.in b/interface-definitions/dhcpv6-relay.xml.in
index 0beb09d05..308f94a01 100644
--- a/interface-definitions/dhcpv6-relay.xml.in
+++ b/interface-definitions/dhcpv6-relay.xml.in
@@ -43,6 +43,7 @@
</constraint>
<constraintErrorMessage>max-hop-count must be a value between 1 and 255</constraintErrorMessage>
</properties>
+ <defaultValue>10</defaultValue>
</leafNode>
<tagNode name="upstream-interface">
<properties>
diff --git a/interface-definitions/igmp-proxy.xml.in b/interface-definitions/igmp-proxy.xml.in
index 74fec6b48..b9c52794f 100644
--- a/interface-definitions/igmp-proxy.xml.in
+++ b/interface-definitions/igmp-proxy.xml.in
@@ -44,7 +44,7 @@
</leafNode>
<leafNode name="role">
<properties>
- <help>Role of this IGMP interface</help>
+ <help>IGMP interface role (default: downstream)</help>
<completionHelp>
<list>upstream downstream disabled</list>
</completionHelp>
@@ -61,13 +61,14 @@
<description>Disabled interface</description>
</valueHelp>
<constraint>
- <regex>(upstream|downstream|disabled)</regex>
+ <regex>^(upstream|downstream|disabled)$</regex>
</constraint>
</properties>
+ <defaultValue>downstream</defaultValue>
</leafNode>
<leafNode name="threshold">
<properties>
- <help>TTL threshold</help>
+ <help>TTL threshold (default: 1)</help>
<valueHelp>
<format>1-255</format>
<description>TTL threshold for the interfaces (default: 1)</description>
@@ -75,8 +76,9 @@
<constraint>
<validator name="numeric" argument="--range 1-255"/>
</constraint>
- <constraintErrorMessage>threshold must be between 1 and 255</constraintErrorMessage>
+ <constraintErrorMessage>Threshold must be between 1 and 255</constraintErrorMessage>
</properties>
+ <defaultValue>1</defaultValue>
</leafNode>
<leafNode name="whitelist">
<properties>
diff --git a/interface-definitions/include/nat-rule.xml.i b/interface-definitions/include/nat-rule.xml.i
index f91834ae0..e70cfd796 100644
--- a/interface-definitions/include/nat-rule.xml.i
+++ b/interface-definitions/include/nat-rule.xml.i
@@ -290,6 +290,7 @@
<validator name="ip-protocol"/>
</constraint>
</properties>
+ <defaultValue>all</defaultValue>
</leafNode>
<node name="source">
<properties>
diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in
index c3f178d59..b322374b3 100644
--- a/interface-definitions/interfaces-tunnel.xml.in
+++ b/interface-definitions/interfaces-tunnel.xml.in
@@ -39,7 +39,6 @@
<script>${vyos_completion_dir}/list_local.py</script>
</completionHelp>
<constraint>
- <!-- does it need fixing/changing to be more restrictive ? -->
<validator name="ip-address"/>
</constraint>
</properties>
@@ -104,7 +103,7 @@
<script>${vyos_completion_dir}/list_interfaces.py</script>
</completionHelp>
<constraint>
- <regex>(en|eth|br|bond|gnv|vxlan|wg|tun)[0-9]+</regex>
+ <regex>^(en|eth|br|bond|gnv|vxlan|wg|tun)[0-9]+$</regex>
</constraint>
</properties>
</leafNode>
@@ -112,36 +111,40 @@
<properties>
<help>Encapsulation of this tunnel interface</help>
<completionHelp>
- <list>gre gre-bridge ipip sit ipip6 ip6ip6 ip6gre</list>
+ <list>gre gre-bridge ip6gre ip6ip6 ipip ipip6 sit</list>
</completionHelp>
<valueHelp>
+ <format>gre</format>
+ <description>Generic Routing Encapsulation</description>
+ </valueHelp>
+ <valueHelp>
<format>gre-bridge</format>
<description>Generic Routing Encapsulation bridge interface</description>
</valueHelp>
<valueHelp>
- <format>ipip</format>
- <description>IP in IP encapsulation</description>
+ <format>ip6gre</format>
+ <description>GRE over IPv6 network</description>
</valueHelp>
<valueHelp>
- <format>sit</format>
- <description>Simple Internet Transition encapsulation</description>
+ <format>ip6ip6</format>
+ <description>IP6 in IP6 encapsulation</description>
</valueHelp>
<valueHelp>
- <format>ipip6</format>
- <description>IP in IP6 encapsulation</description>
+ <format>ipip</format>
+ <description>IP in IP encapsulation</description>
</valueHelp>
<valueHelp>
- <format>ip6ip6</format>
- <description>IP6 in IP6 encapsulation</description>
+ <format>ipip6</format>
+ <description>IP in IP6 encapsulation</description>
</valueHelp>
<valueHelp>
- <format>ip6gre</format>
- <description>GRE over IPv6 network</description>
+ <format>sit</format>
+ <description>Simple Internet Transition encapsulation</description>
</valueHelp>
<constraint>
- <regex>(gre|gre-bridge|ipip|sit|ipip6|ip6ip6|ip6gre)</regex>
+ <regex>^(gre|gre-bridge|ip6gre|ip6ip6|ipip|ipip6|sit)$</regex>
</constraint>
- <constraintErrorMessage>Must be one of 'gre' 'gre-bridge' 'ipip' 'sit' 'ipip6' 'ip6ip6' 'ip6gre'</constraintErrorMessage>
+ <constraintErrorMessage>Invalid encapsulation, must be one of: gre, gre-bridge, ipip, sit, ipip6, ip6ip6, ip6gre</constraintErrorMessage>
</properties>
</leafNode>
<leafNode name="multicast">
@@ -159,7 +162,7 @@
<description>Disable Multicast (default)</description>
</valueHelp>
<constraint>
- <regex>(enable|disable)</regex>
+ <regex>^(enable|disable)$</regex>
</constraint>
<constraintErrorMessage>Must be 'disable' or 'enable'</constraintErrorMessage>
</properties>
@@ -186,6 +189,7 @@
</constraint>
<constraintErrorMessage>TTL must be between 0 and 255</constraintErrorMessage>
</properties>
+ <defaultValue>255</defaultValue>
</leafNode>
<leafNode name="tos">
<properties>
@@ -199,6 +203,7 @@
</constraint>
<constraintErrorMessage>TOS must be between 0 and 99</constraintErrorMessage>
</properties>
+ <defaultValue>inherit</defaultValue>
</leafNode>
<leafNode name="key">
<properties>
@@ -232,6 +237,7 @@
</constraint>
<constraintErrorMessage>key must be between 0-255</constraintErrorMessage>
</properties>
+ <defaultValue>4</defaultValue>
</leafNode>
<leafNode name="flowlabel">
<properties>
@@ -245,6 +251,7 @@
</constraint>
<constraintErrorMessage>Must be 'inherit' or a number</constraintErrorMessage>
</properties>
+ <defaultValue>inherit</defaultValue>
</leafNode>
<leafNode name="hoplimit">
<properties>
@@ -258,6 +265,7 @@
</constraint>
<constraintErrorMessage>hoplimit must be between 0-255</constraintErrorMessage>
</properties>
+ <defaultValue>64</defaultValue>
</leafNode>
<leafNode name="tclass">
<properties>
@@ -271,6 +279,7 @@
</constraint>
<constraintErrorMessage>Must be 'inherit' or a number</constraintErrorMessage>
</properties>
+ <defaultValue>inherit</defaultValue>
</leafNode>
</children>
</node>
diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in
index aa63e4ac7..84f7803a0 100644
--- a/interface-definitions/interfaces-wireguard.xml.in
+++ b/interface-definitions/interfaces-wireguard.xml.in
@@ -22,6 +22,7 @@
#include <include/interface-vrf.xml.i>
#include <include/port-number.xml.i>
#include <include/interface-mtu-68-16000.xml.i>
+ #include <include/interface-ipv6-options.xml.i>
<leafNode name="fwmark">
<properties>
<help>A 32-bit fwmark value set on all outgoing packets</help>
diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in
index fdea1e3ab..6b238e313 100644
--- a/interface-definitions/interfaces-wireless.xml.in
+++ b/interface-definitions/interfaces-wireless.xml.in
@@ -771,12 +771,6 @@
</leafNode>
#include <include/vif.xml.i>
#include <include/vif-s.xml.i>
- <leafNode name="wds">
- <properties>
- <help>Enable WDS (Wireless Distribution System)</help>
- <valueless/>
- </properties>
- </leafNode>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/ntp.xml.in b/interface-definitions/ntp.xml.in
index 6070cafe0..6a8eb0818 100644
--- a/interface-definitions/ntp.xml.in
+++ b/interface-definitions/ntp.xml.in
@@ -20,6 +20,12 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="pool">
+ <properties>
+ <help>Associate with a number of remote servers</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="preempt">
<properties>
<help>Specifies the association as preemptable rather than the default persistent</help>
diff --git a/interface-definitions/policy-local-route.xml.in b/interface-definitions/policy-local-route.xml.in
new file mode 100644
index 000000000..5536c71c8
--- /dev/null
+++ b/interface-definitions/policy-local-route.xml.in
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<!-- Policy local-route -->
+<interfaceDefinition>
+ <node name="policy">
+ <children>
+ <node name="local-route" owner="${vyos_conf_scripts_dir}/policy-local-route.py">
+ <properties>
+ <help>IPv4 policy route of local traffic</help>
+ </properties>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Policy local-route rule set number</help>
+ <valueHelp>
+ <!-- table main with prio 32766 -->
+ <format>&lt;1-32765&gt;</format>
+ <description>Local-route rule number (1-219)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-32765"/>
+ </constraint>
+ </properties>
+ <children>
+ <node name="set">
+ <properties>
+ <help>Packet modifications</help>
+ </properties>
+ <children>
+ <leafNode name="table">
+ <properties>
+ <help>Routing table to forward packet with</help>
+ <valueHelp>
+ <format>&lt;1-200&gt;</format>
+ <description>Table number</description>
+ </valueHelp>
+ <completionHelp>
+ <list>main</list>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="source">
+ <properties>
+ <help>Source address or prefix</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Address to match against</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>Prefix to match against</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ip-prefix"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/interface-definitions/protocols-mpls.xml.in b/interface-definitions/protocols-mpls.xml.in
index 4df2be4e7..16286f2c4 100644
--- a/interface-definitions/protocols-mpls.xml.in
+++ b/interface-definitions/protocols-mpls.xml.in
@@ -375,6 +375,15 @@
</leafNode>
</children>
</node>
+ <leafNode name="interface">
+ <properties>
+ <help>Enable MPLS packet processing on interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ <multi/>
+ </properties>
+ </leafNode>
</children>
</node>
</children>
diff --git a/interface-definitions/system-options.xml.in b/interface-definitions/system-option.xml.in
index 12e4044af..26b78c8a4 100644
--- a/interface-definitions/system-options.xml.in
+++ b/interface-definitions/system-option.xml.in
@@ -2,35 +2,29 @@
<interfaceDefinition>
<node name="system">
<children>
- <node name="options" owner="${vyos_conf_scripts_dir}/system-options.py">
+ <node name="option" owner="${vyos_conf_scripts_dir}/system-option.py">
<properties>
<help>System Options</help>
<priority>9999</priority>
</properties>
<children>
- <leafNode name="beep-if-fully-booted">
+ <leafNode name="ctrl-alt-delete">
<properties>
- <help>plays sound via system speaker when you can login</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="ctrl-alt-del-action">
- <properties>
- <help>Ctrl-Alt-Delete action</help>
+ <help>System action on Ctrl-Alt-Delete keystroke</help>
<completionHelp>
<list>ignore reboot poweroff</list>
</completionHelp>
<valueHelp>
<format>ignore</format>
- <description>Ignore Ctrl-Alt-Delete</description>
+ <description>Ignore key sequence</description>
</valueHelp>
<valueHelp>
<format>reboot</format>
- <description>Reboot VyOS</description>
+ <description>Reboot system</description>
</valueHelp>
<valueHelp>
<format>poweroff</format>
- <description>Poweroff VyOS</description>
+ <description>Poweroff system</description>
</valueHelp>
<constraint>
<regex>^(ignore|reboot|poweroff)$</regex>
@@ -38,7 +32,40 @@
<constraintErrorMessage>Must be ignore, reboot, or poweroff</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="performance">
+ <leafNode name="keyboard-layout">
+ <properties>
+ <help>System keyboard layout, type ISO2</help>
+ <completionHelp>
+ <list>us fr de fi no dk</list>
+ </completionHelp>
+ <valueHelp>
+ <format>us</format>
+ <description>United States of America</description>
+ </valueHelp>
+ <valueHelp>
+ <format>fr</format>
+ <description>France</description>
+ </valueHelp>
+ <valueHelp>
+ <format>de</format>
+ <description>Germany</description>
+ </valueHelp>
+ <valueHelp>
+ <format>fi</format>
+ <description>Finland</description>
+ </valueHelp>
+ <valueHelp>
+ <format>no</format>
+ <description>Norway</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dk</format>
+ <description>Denmark</description>
+ </valueHelp>
+ </properties>
+ <defaultValue>us</defaultValue>
+ </leafNode>
+ <leafNode name="performance">
<properties>
<help>Tune system performance</help>
<completionHelp>
@@ -80,38 +107,11 @@
#include <include/source-address-ipv4-ipv6.xml.i>
</children>
</node>
- <leafNode name="keyboard-layout">
+ <leafNode name="startup-beep">
<properties>
- <help>System keyboard layout, type ISO2</help>
- <completionHelp>
- <list>us fr de fi no dk</list>
- </completionHelp>
- <valueHelp>
- <format>us</format>
- <description>United States of America</description>
- </valueHelp>
- <valueHelp>
- <format>fr</format>
- <description>France</description>
- </valueHelp>
- <valueHelp>
- <format>de</format>
- <description>Germany</description>
- </valueHelp>
- <valueHelp>
- <format>fi</format>
- <description>Finland</description>
- </valueHelp>
- <valueHelp>
- <format>no</format>
- <description>Norway</description>
- </valueHelp>
- <valueHelp>
- <format>dk</format>
- <description>Denmark</description>
- </valueHelp>
+ <help>plays sound via system speaker when you can login</help>
+ <valueless/>
</properties>
- <defaultValue>us</defaultValue>
</leafNode>
</children>
</node>
diff --git a/op-mode-definitions/force-arp.xml b/op-mode-definitions/force-arp.xml
index c7bcad413..f9f7c7643 100644
--- a/op-mode-definitions/force-arp.xml
+++ b/op-mode-definitions/force-arp.xml
@@ -72,6 +72,30 @@
</tagNode>
</children>
</node>
+ <node name="duplicate">
+ <properties>
+ <help>Send ARP for DAD detection</help>
+ </properties>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Send ARP for DAD detection on specified interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py --broadcast</script>
+ </completionHelp>
+ </properties>
+ <children>
+ <tagNode name="address">
+ <properties>
+ <help>Send ARP for DAD detection for specified address</help>
+ </properties>
+ <command>sudo /usr/bin/arping -I $5 -c 1 -D $7</command>
+ </tagNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+
</children>
</node>
</children>
diff --git a/op-mode-definitions/restart.xml b/op-mode-definitions/restart.xml
new file mode 100644
index 000000000..c74ec9013
--- /dev/null
+++ b/op-mode-definitions/restart.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interfaceDefinition>
+ <node name="restart">
+ <properties>
+ <help>Restart individual service</help>
+ </properties>
+ </node>
+</interfaceDefinition>
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index b14f96364..cdcd3f9ea 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -439,13 +439,13 @@ def get_accel_dict(config, base, chap_secrets):
# options which we need to update into the dictionary retrived.
default_values = defaults(base)
- # defaults include RADIUS server specifics per TAG node which need to be
- # added to individual RADIUS servers instead - so we can simply delete them
+ # T2665: defaults include RADIUS server specifics per TAG node which need to
+ # be added to individual RADIUS servers instead - so we can simply delete them
if dict_search('authentication.radius.server', default_values):
del default_values['authentication']['radius']['server']
- # defaults include static-ip address per TAG node which need to be added to
- # individual local users instead - so we can simply delete them
+ # T2665: defaults include static-ip address per TAG node which need to be
+ # added to individual local users instead - so we can simply delete them
if dict_search('authentication.local_users.username', default_values):
del default_values['authentication']['local_users']['username']
@@ -471,6 +471,7 @@ def get_accel_dict(config, base, chap_secrets):
# Add individual RADIUS server default values
if dict_search('authentication.radius.server', dict):
+ # T2665
default_values = defaults(base + ['authentication', 'radius', 'server'])
for server in dict_search('authentication.radius.server', dict):
@@ -484,6 +485,7 @@ def get_accel_dict(config, base, chap_secrets):
# Add individual local-user default values
if dict_search('authentication.local_users.username', dict):
+ # T2665
default_values = defaults(base + ['authentication', 'local-users', 'username'])
for username in dict_search('authentication.local_users.username', dict):
diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py
index 7c77e050a..d0d5da881 100644
--- a/python/vyos/ifconfig/bridge.py
+++ b/python/vyos/ifconfig/bridge.py
@@ -157,7 +157,7 @@ class BridgeIf(Interface):
>>> BridgeIf('br0').set_stp(1)
"""
self.set_interface('stp', state)
-
+
def set_vlan_filter(self, state):
"""
Set bridge Vlan Filter state. 0 -> Vlan Filter disabled, 1 -> Vlan Filter enabled
@@ -192,12 +192,11 @@ class BridgeIf(Interface):
>>> BridgeIf('br0').add_port('eth0')
>>> BridgeIf('br0').add_port('eth1')
"""
- # Bridge port handling of wireless interfaces is done by hostapd.
- if 'wlan' in interface:
- return
-
- return self.set_interface('add_port', interface)
-
+ try:
+ return self.set_interface('add_port', interface)
+ except:
+ from vyos import ConfigError
+ raise ConfigError('Error: Device does not allow enslaving to a bridge.')
def del_port(self, interface):
"""
@@ -217,7 +216,7 @@ class BridgeIf(Interface):
# call base class first
super().update(config)
-
+
ifname = config['ifname']
# Set ageing time
@@ -255,7 +254,7 @@ class BridgeIf(Interface):
if member in interfaces():
self.del_port(member)
vlan_filter = 0
-
+
vlan_del = set()
vlan_add = set()
@@ -286,9 +285,9 @@ class BridgeIf(Interface):
if 'priority' in interface_config:
value = interface_config.get('priority')
lower.set_path_priority(value)
-
+
tmp = dict_search('native_vlan_removed', interface_config)
-
+
for vlan_id in (tmp or []):
cmd = f'bridge vlan del dev {interface} vid {vlan_id}'
self._cmd(cmd)
@@ -296,37 +295,43 @@ class BridgeIf(Interface):
self._cmd(cmd)
vlan_del.add(vlan_id)
vlan_add.add(1)
-
+
tmp = dict_search('allowed_vlan_removed', interface_config)
-
-
+
+
for vlan_id in (tmp or []):
cmd = f'bridge vlan del dev {interface} vid {vlan_id}'
self._cmd(cmd)
vlan_del.add(vlan_id)
-
+
if 'native_vlan' in interface_config:
vlan_filter = 1
cmd = f'bridge vlan del dev {interface} vid 1'
self._cmd(cmd)
- vlan_del.add(1)
vlan_id = interface_config['native_vlan']
+ if vlan_id != 1:
+ vlan_del.add(1)
cmd = f'bridge vlan add dev {interface} vid {vlan_id} pvid untagged master'
self._cmd(cmd)
vlan_add.add(vlan_id)
- else:
- cmd = f'bridge vlan del dev {interface} vid 1'
- self._cmd(cmd)
- vlan_del.add(1)
-
+
if 'allowed_vlan' in interface_config:
vlan_filter = 1
+
+ if vlan_filter:
+ if 'native_vlan' not in interface_config:
+ cmd = f'bridge vlan del dev {interface} vid 1'
+ self._cmd(cmd)
+
+ if 'allowed_vlan' in interface_config:
for vlan in interface_config['allowed_vlan']:
cmd = f'bridge vlan add dev {interface} vid {vlan} master'
self._cmd(cmd)
vlan_add.add(vlan)
-
-
+
+
+
+
for vlan in vlan_del:
if isinstance(vlan,str) and vlan.isnumeric():
if int(vlan) == 1:
@@ -340,15 +345,15 @@ class BridgeIf(Interface):
else:
cmd = f'bridge vlan del dev {ifname} vid {vlan} self'
self._cmd(cmd)
-
+
for vlan in vlan_add:
cmd = f'bridge vlan add dev {ifname} vid {vlan} self'
self._cmd(cmd)
-
+
# enable/disable Vlan Filter
self.set_vlan_filter(vlan_filter)
-
-
+
+
# Enable/Disable of an interface must always be done at the end of the
# derived class to make use of the ref-counting set_admin_state()
# function. We will only enable the interface if 'up' was called as
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 893623284..43cd7220a 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -890,9 +890,9 @@ class Interface(Control):
self._config = dict_merge(tmp, self._config)
render(options_file, 'dhcp-client/daemon-options.tmpl',
- self._config, trim_blocks=True)
+ self._config)
render(config_file, 'dhcp-client/ipv4.tmpl',
- self._config, trim_blocks=True)
+ self._config)
# 'up' check is mandatory b/c even if the interface is A/D, as soon as
# the DHCP client is started the interface will be placed in u/u state.
@@ -919,7 +919,7 @@ class Interface(Control):
if enable and 'disable' not in self._config:
render(config_file, 'dhcp-client/ipv6.tmpl',
- self._config, trim_blocks=True)
+ self._config)
# We must ignore any return codes. This is required to enable DHCPv6-PD
# for interfaces which are yet not up and running.
@@ -942,6 +942,15 @@ class Interface(Control):
# method to apply()?
self._config = config
+ # Change interface MAC address - re-set to real hardware address (hw-id)
+ # if custom mac is removed. Skip if bond member.
+ if 'is_bond_member' not in config:
+ mac = config.get('hw_id')
+ if 'mac' in config:
+ mac = config.get('mac')
+ if mac:
+ self.set_mac(mac)
+
# Update interface description
self.set_alias(config.get('description', ''))
@@ -1058,15 +1067,6 @@ class Interface(Control):
for addr in tmp:
self.del_ipv6_eui64_address(addr)
- # Change interface MAC address - re-set to real hardware address (hw-id)
- # if custom mac is removed. Skip if bond member.
- if 'is_bond_member' not in config:
- mac = config.get('hw_id')
- if 'mac' in config:
- mac = config.get('mac')
- if mac:
- self.set_mac(mac)
-
# Manage IPv6 link-local addresses
tmp = dict_search('ipv6.address.no_default_link_local', config)
# we must check explicitly for None type as if the key is set we will
diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py
index 4122d1a2f..00dc36420 100644
--- a/python/vyos/ifconfig/tunnel.py
+++ b/python/vyos/ifconfig/tunnel.py
@@ -1,4 +1,4 @@
-# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2020 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -18,8 +18,11 @@
from copy import deepcopy
+from netaddr import EUI
+from netaddr import mac_unix_expanded
+from random import getrandbits
+
from vyos.ifconfig.interface import Interface
-from vyos.ifconfig.afi import IP4, IP6
from vyos.validate import assert_list
def enable_to_on(value):
@@ -42,7 +45,6 @@ class _Tunnel(Interface):
**{
'section': 'tunnel',
'prefixes': ['tun',],
- 'bridgeable': False,
},
}
@@ -61,68 +63,73 @@ class _Tunnel(Interface):
},
}}
- # use for "options" and "updates"
- # If an key is only in the options list, it can only be set at creation time
- # the create comand will only be make using the key in options
-
- # If an option is in the updates list, it can be updated
- # upon, the creation, all key not yet applied will be updated
-
- # multicast/allmulticast can not be part of the create command
-
- # options matrix:
- # with ip = 4, we have multicast
- # wiht ip = 6, nothing
- # with tunnel = 4, we have tos, ttl, key
- # with tunnel = 6, we have encaplimit, hoplimit, tclass, flowlabel
-
- # TODO: For multicast, it is allowed on IP6IP6 and Sit6RD
- # TODO: to match vyatta but it should be checked for correctness
-
- updates = []
-
- create = ''
- change = ''
- delete = ''
-
- ip = [] # AFI of the families which can be used in the tunnel
- tunnel = 0 # invalid - need to be set by subclasses
-
def __init__(self, ifname, **config):
self.config = deepcopy(config) if config else {}
super().__init__(ifname, **config)
def _create(self):
+ create = 'ip tunnel add {ifname} mode {type}'
+
# add " option-name option-name-value ..." for all options set
options = " ".join(["{} {}".format(k, self.config[k])
for k in self.options if k in self.config and self.config[k]])
- self._cmd('{} {}'.format(self.create.format(**self.config), options))
+ self._cmd('{} {}'.format(create.format(**self.config), options))
self.set_admin_state('down')
- def _delete(self):
- self.set_admin_state('down')
- cmd = self.delete.format(**self.config)
- return self._cmd(cmd)
+ def change_options(self):
+ change = 'ip tunnel cha {ifname} mode {type}'
- def set_interface(self, option, value):
- try:
- return Interface.set_interface(self, option, value)
- except Exception:
- pass
-
- if value == '':
- # remove the value so that it is not used
- self.config.pop(option, '')
-
- if self.change:
- self._cmd('{} {} {}'.format(
- self.change.format(**self.config), option, value))
- return True
+ # add " option-name option-name-value ..." for all options set
+ options = " ".join(["{} {}".format(k, self.config[k])
+ for k in self.options if k in self.config and self.config[k]])
+ self._cmd('{} {}'.format(change.format(**self.config), options))
@classmethod
def get_config(cls):
return dict(zip(cls.options, ['']*len(cls.options)))
+ def get_mac(self):
+ """
+ Get current interface MAC (Media Access Contrl) address used.
+
+ NOTE: Tunnel interfaces have no "MAC" address by default. The content
+ of the 'address' file in /sys/class/net/device contains the
+ local-ip thus we generate a random MAC address instead
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').get_mac()
+ '00:50:ab:cd:ef:00'
+ """
+ # we choose 40 random bytes for the MAC address, this gives
+ # us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A')
+ tmp = EUI(getrandbits(48)).value
+ # set locally administered bit in MAC address
+ tmp |= 0xf20000000000
+ # convert integer to "real" MAC address representation
+ mac = EUI(hex(tmp).split('x')[-1])
+ # change dialect to use : as delimiter instead of -
+ mac.dialect = mac_unix_expanded
+ return str(mac)
+
+ def update(self, config):
+ """ General helper function which works on a dictionary retrived by
+ get_config_dict(). It's main intention is to consolidate the scattered
+ interface setup code and provide a single point of entry when workin
+ on any interface. """
+
+ # call base class first
+ super().update(config)
+
+ # Enable/Disable of an interface must always be done at the end of the
+ # derived class to make use of the ref-counting set_admin_state()
+ # function. We will only enable the interface if 'up' was called as
+ # often as 'down'. This is required by some interface implementations
+ # as certain parameters can only be changed when the interface is
+ # in admin-down state. This ensures the link does not flap during
+ # reconfiguration.
+ state = 'down' if 'disable' in config else 'up'
+ self.set_admin_state(state)
class GREIf(_Tunnel):
"""
@@ -134,27 +141,8 @@ class GREIf(_Tunnel):
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_gre.c
"""
- definition = {
- **_Tunnel.definition,
- **{
- 'bridgeable': True,
- },
- }
-
- ip = [IP4, IP6]
- tunnel = IP4
-
default = {'type': 'gre'}
- required = ['local', ] # mGRE is a GRE without remote endpoint
-
options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key']
- updates = ['local', 'remote', 'dev', 'ttl', 'tos',
- 'mtu', 'multicast', 'allmulticast']
-
- create = 'ip tunnel add {ifname} mode {type}'
- change = 'ip tunnel cha {ifname}'
- delete = 'ip tunnel del {ifname}'
-
# GreTap also called GRE Bridge
class GRETapIf(_Tunnel):
@@ -173,19 +161,8 @@ class GRETapIf(_Tunnel):
},
}
- ip = [IP4, ]
- tunnel = IP4
-
default = {'type': 'gretap'}
- required = ['local', ]
-
options = ['local', 'remote', 'ttl',]
- updates = ['mtu', ]
-
- create = 'ip link add {ifname} type {type}'
- change = ''
- delete = 'ip link del {ifname}'
-
class IP6GREIf(_Tunnel):
"""
@@ -196,30 +173,9 @@ class IP6GREIf(_Tunnel):
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_gre6.c
"""
- ip = [IP4, IP6]
- tunnel = IP6
-
default = {'type': 'ip6gre'}
- required = ['local', 'remote']
-
options = ['local', 'remote', 'dev', 'encaplimit',
'hoplimit', 'tclass', 'flowlabel']
- updates = ['local', 'remote', 'dev', 'encaplimit',
- 'hoplimit', 'tclass', 'flowlabel',
- 'mtu', 'multicast', 'allmulticast']
-
- create = 'ip tunnel add {ifname} mode {type}'
- change = 'ip tunnel cha {ifname} mode {type}'
- delete = 'ip tunnel del {ifname}'
-
- # using "ip tunnel change" without using "mode" causes errors
- # sudo ip tunnel add tun100 mode ip6gre local ::1 remote 1::1
- # sudo ip tunnel cha tun100 hoplimit 100
- # *** stack smashing detected ** *: < unknown > terminated
- # sudo ip tunnel cha tun100 local: : 2
- # Error: an IP address is expected rather than "::2"
- # works if mode is explicit
-
class IPIPIf(_Tunnel):
"""
@@ -232,20 +188,8 @@ class IPIPIf(_Tunnel):
# IPIP does not allow to pass multicast, unlike GRE
# but the interface itself can be set with multicast
- ip = [IP4,]
- tunnel = IP4
-
default = {'type': 'ipip'}
- required = ['local', 'remote']
-
options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key']
- updates = ['local', 'remote', 'dev', 'ttl', 'tos',
- 'mtu', 'multicast', 'allmulticast']
-
- create = 'ip tunnel add {ifname} mode {type}'
- change = 'ip tunnel cha {ifname}'
- delete = 'ip tunnel del {ifname}'
-
class IPIP6If(_Tunnel):
"""
@@ -255,22 +199,9 @@ class IPIP6If(_Tunnel):
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_ip6tnl.c
"""
- ip = [IP4,]
- tunnel = IP6
-
default = {'type': 'ipip6'}
- required = ['local', 'remote']
-
options = ['local', 'remote', 'dev', 'encaplimit',
'hoplimit', 'tclass', 'flowlabel']
- updates = ['local', 'remote', 'dev', 'encaplimit',
- 'hoplimit', 'tclass', 'flowlabel',
- 'mtu', 'multicast', 'allmulticast']
-
- create = 'ip -6 tunnel add {ifname} mode {type}'
- change = 'ip -6 tunnel cha {ifname}'
- delete = 'ip -6 tunnel del {ifname}'
-
class IP6IP6If(IPIP6If):
"""
@@ -279,9 +210,6 @@ class IP6IP6If(IPIP6If):
For more information please refer to:
https://tools.ietf.org/html/rfc2473
"""
-
- ip = [IP6,]
-
default = {'type': 'ip6ip6'}
@@ -293,20 +221,8 @@ class SitIf(_Tunnel):
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_iptnl.c
"""
- ip = [IP6, IP4]
- tunnel = IP4
-
default = {'type': 'sit'}
- required = ['local', 'remote']
-
options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key']
- updates = ['local', 'remote', 'dev', 'ttl', 'tos',
- 'mtu', 'multicast', 'allmulticast']
-
- create = 'ip tunnel add {ifname} mode {type}'
- change = 'ip tunnel cha {ifname}'
- delete = 'ip tunnel del {ifname}'
-
class Sit6RDIf(SitIf):
"""
@@ -314,15 +230,8 @@ class Sit6RDIf(SitIf):
https://en.wikipedia.org/wiki/IPv6_rapid_deployment
"""
-
- ip = [IP6,]
-
- required = ['remote', '6rd-prefix']
-
# TODO: check if key can really be used with 6RD
options = ['remote', 'ttl', 'tos', 'key', '6rd-prefix', '6rd-relay-prefix']
- updates = ['remote', 'ttl', 'tos',
- 'mtu', 'multicast', 'allmulticast']
def _create(self):
# do not call _Tunnel.create, building fully here
diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py
index da3bd4e89..9ee798ee8 100644
--- a/python/vyos/ifconfig/wireguard.py
+++ b/python/vyos/ifconfig/wireguard.py
@@ -17,6 +17,9 @@ import os
import time
from datetime import timedelta
+from netaddr import EUI
+from netaddr import mac_unix_expanded
+from random import getrandbits
from hurry.filesize import size
from hurry.filesize import alternative
@@ -162,13 +165,37 @@ class WireGuardIf(Interface):
**{
'section': 'wireguard',
'prefixes': ['wg', ],
- 'bridgeable': True,
+ 'bridgeable': False,
}
}
options = Interface.options + \
['port', 'private_key', 'pubkey', 'psk',
'allowed_ips', 'fwmark', 'endpoint', 'keepalive']
+ def get_mac(self):
+ """
+ Get current interface MAC (Media Access Contrl) address used.
+
+ NOTE: Tunnel interfaces have no "MAC" address by default. The content
+ of the 'address' file in /sys/class/net/device contains the
+ local-ip thus we generate a random MAC address instead
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').get_mac()
+ '00:50:ab:cd:ef:00'
+ """
+ # we choose 40 random bytes for the MAC address, this gives
+ # us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A')
+ tmp = EUI(getrandbits(48)).value
+ # set locally administered bit in MAC address
+ tmp |= 0xf20000000000
+ # convert integer to "real" MAC address representation
+ mac = EUI(hex(tmp).split('x')[-1])
+ # change dialect to use : as delimiter instead of -
+ mac.dialect = mac_unix_expanded
+ return str(mac)
+
def update(self, config):
""" General helper function which works on a dictionary retrived by
get_config_dict(). It's main intention is to consolidate the scattered
@@ -221,7 +248,7 @@ class WireGuardIf(Interface):
# Endpoint configuration is optional
if {'address', 'port'} <= set(peer):
- if is_ipv6(config['address']):
+ if is_ipv6(peer['address']):
cmd += ' endpoint [{address}]:{port}'
else:
cmd += ' endpoint {address}:{port}'
diff --git a/python/vyos/ifconfig/wireless.py b/python/vyos/ifconfig/wireless.py
index deca68bf0..37703d242 100644
--- a/python/vyos/ifconfig/wireless.py
+++ b/python/vyos/ifconfig/wireless.py
@@ -23,10 +23,8 @@ class WiFiIf(Interface):
default = {
'type': 'wifi',
- 'phy': '',
- 'wds': 'off',
+ 'phy': 'phy0'
}
-
definition = {
**Interface.definition,
**{
@@ -35,19 +33,12 @@ class WiFiIf(Interface):
'bridgeable': True,
}
}
-
options = Interface.options + \
['phy', 'op_mode']
- _command_set = {**Interface._command_set, **{
- '4addr': {
- 'shellcmd': 'iw dev {ifname} set 4addr {value}',
- },
- }}
-
def _create(self):
# all interfaces will be added in monitor mode
- cmd = 'iw phy {phy} interface add {ifname} type monitor 4addr {wds}' \
+ cmd = 'iw phy {phy} interface add {ifname} type monitor' \
.format(**self.config)
self._cmd(cmd)
@@ -59,20 +50,28 @@ class WiFiIf(Interface):
.format(**self.config)
self._cmd(cmd)
- def set_4aadr_mode(self, state):
- return self.set_interface('4addr', state)
-
def update(self, config):
""" General helper function which works on a dictionary retrived by
get_config_dict(). It's main intention is to consolidate the scattered
interface setup code and provide a single point of entry when workin
on any interface. """
- self.set_4aadr_mode('on' if 'wds' in config else 'off')
+ # We can not call add_to_bridge() until wpa_supplicant is running, thus
+ # we will remove the key from the config dict and react to this specal
+ # case in thie derived class.
+ # re-add ourselves to any bridge we might have fallen out of
+ bridge_member = ''
+ if 'is_bridge_member' in config:
+ bridge_member = config['is_bridge_member']
+ del config['is_bridge_member']
# call base class first
super().update(config)
+ # re-add ourselves to any bridge we might have fallen out of
+ if bridge_member:
+ self.add_to_bridge(bridge_member)
+
# Enable/Disable of an interface must always be done at the end of the
# derived class to make use of the ref-counting set_admin_state()
# function. We will only enable the interface if 'up' was called as
diff --git a/python/vyos/limericks.py b/python/vyos/limericks.py
index e03ccd32b..3c6744816 100644
--- a/python/vyos/limericks.py
+++ b/python/vyos/limericks.py
@@ -18,18 +18,18 @@ import random
limericks = [
"""
-A programmer who's name was Searle
-Once wrote a long program in Perl.
-Despite very few quirks
-No one got how it works,
-Not even the interpreter.
+A programmer whose name was Searle
+once wrote a long program in Perl.
+Despite very few quirks,
+no one got how it works.
+Not even the interpreter perl(1).
""",
"""
There was a young lady of Maine
-Who set up IPsec VPN.
+who set up IPsec VPN.
Problems didn't arise
-'til other vendors' device
+till other vendors' device
had to add she to that VPN.
""",
@@ -45,7 +45,7 @@ to get the damn build scripts to work.
A network admin from Hong Kong
knew MPPE cipher's not strong.
But he was behind NAT,
-so he put up we that,
+so he put up with that,
sad network admin from Hong Kong.
""",
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 58ba75972..b31f5bea2 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -18,24 +18,25 @@ import os
from jinja2 import Environment
from jinja2 import FileSystemLoader
-from vyos.defaults import directories
-from vyos.util import chmod, chown, makedir
+from vyos.defaults import directories
+from vyos.util import chmod
+from vyos.util import chown
+from vyos.util import makedir
# Holds template filters registered via register_filter()
_FILTERS = {}
-
-# reuse Environments with identical trim_blocks setting to improve performance
+# reuse Environments with identical settings to improve performance
@functools.lru_cache(maxsize=2)
-def _get_environment(trim_blocks):
+def _get_environment():
env = Environment(
# Don't check if template files were modified upon re-rendering
auto_reload=False,
# Cache up to this number of templates for quick re-rendering
cache_size=100,
loader=FileSystemLoader(directories["templates"]),
- trim_blocks=trim_blocks,
+ trim_blocks=True,
)
env.filters.update(_FILTERS)
return env
@@ -62,12 +63,11 @@ def register_filter(name, func=None):
return func
-def render_to_string(template, content, trim_blocks=False, formater=None):
+def render_to_string(template, content, formater=None):
"""Render a template from the template directory, raise on any errors.
:param template: the path to the template relative to the template folder
:param content: the dictionary of variables to put into rendering context
- :param trim_blocks: controls the trim_blocks jinja2 feature
:param formater:
if given, it has to be a callable the rendered string is passed through
@@ -78,7 +78,7 @@ def render_to_string(template, content, trim_blocks=False, formater=None):
package is build (recovering the load time and overhead caused by having the
file out of the code).
"""
- template = _get_environment(bool(trim_blocks)).get_template(template)
+ template = _get_environment().get_template(template)
rendered = template.render(content)
if formater is not None:
rendered = formater(rendered)
@@ -89,7 +89,6 @@ def render(
destination,
template,
content,
- trim_blocks=False,
formater=None,
permission=None,
user=None,
@@ -110,7 +109,7 @@ def render(
# As we are opening the file with 'w', we are performing the rendering before
# calling open() to not accidentally erase the file if rendering fails
- rendered = render_to_string(template, content, trim_blocks, formater)
+ rendered = render_to_string(template, content, formater)
# Write to file
with open(destination, "w") as file:
@@ -154,7 +153,7 @@ def is_ipv4(text):
try: return ip_interface(text).version == 4
except: return False
-@register_filter('ipv6')
+@register_filter('is_ipv6')
def is_ipv6(text):
""" Filter IP address, return True on IPv6 address, False otherwise """
from ipaddress import ip_interface
diff --git a/python/vyos/xml/definition.py b/python/vyos/xml/definition.py
index 7831af4d2..f556c5ced 100644
--- a/python/vyos/xml/definition.py
+++ b/python/vyos/xml/definition.py
@@ -255,7 +255,7 @@ class XML(dict):
if not flat:
# _flatten will make this conversion
- d = self.multi_to_list(lpath, d)
+ d = self.multi_to_list(lpath, d, defaults=True)
r = {}
for k in d:
@@ -284,7 +284,7 @@ class XML(dict):
return _flatten(lpath, len(lpath), d)
- def multi_to_list(self, lpath, conf):
+ def multi_to_list(self, lpath, conf, defaults=False):
r = {}
for k in conf:
# key mangling could also be done here
@@ -293,11 +293,14 @@ class XML(dict):
under = k
fpath = lpath + [k]
if isinstance(conf[k],dict):
- r[under] = self.multi_to_list(fpath, conf[k])
+ r[under] = self.multi_to_list(fpath, conf[k], defaults)
continue
value = conf[k]
if self.is_multi(fpath) and not isinstance(value, list):
- value = value.split(' ')
+ if not defaults:
+ value = [value]
+ else:
+ value = value.split(' ')
r[under] = value
return r
diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py
index 41e48c2f8..e636e107d 100755
--- a/smoketest/scripts/cli/test_interfaces_openvpn.py
+++ b/smoketest/scripts/cli/test_interfaces_openvpn.py
@@ -131,6 +131,12 @@ class TestInterfacesOpenVPN(unittest.TestCase):
self.session.set(path + ['tls', 'cert-file', ssl_cert])
self.session.set(path + ['tls', 'key-file', ssl_key])
+ # check validate() - can not have auth username without a password
+ self.session.set(path + ['authentication', 'username', 'vyos'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(path + ['authentication', 'password', 'vyos'])
+
# client commit must pass
self.session.commit()
@@ -162,6 +168,8 @@ class TestInterfacesOpenVPN(unittest.TestCase):
self.session.set(path + ['tls', 'cert-file', ssl_cert])
self.session.set(path + ['tls', 'key-file', ssl_key])
self.session.set(path + ['vrf', vrf_name])
+ self.session.set(path + ['authentication', 'username', interface+'user'])
+ self.session.set(path + ['authentication', 'password', interface+'secretpw'])
self.session.commit()
@@ -169,6 +177,7 @@ class TestInterfacesOpenVPN(unittest.TestCase):
interface = f'vtun{ii}'
remote_host = f'192.0.2.{ii}'
config_file = f'/run/openvpn/{interface}.conf'
+ pw_file = f'/run/openvpn/{interface}.pw'
config = read_file(config_file)
self.assertIn(f'dev {interface}', config)
@@ -180,6 +189,7 @@ class TestInterfacesOpenVPN(unittest.TestCase):
self.assertIn(f'persist-tun', config)
self.assertIn(f'auth {auth_hash}', config)
self.assertIn(f'cipher aes-256-cbc', config)
+
# TLS options
self.assertIn(f'ca {ca_cert}', config)
self.assertIn(f'cert {ssl_cert}', config)
@@ -189,6 +199,10 @@ class TestInterfacesOpenVPN(unittest.TestCase):
self.assertEqual(get_vrf(interface), vrf_name)
self.assertIn(interface, interfaces())
+ pw = cmd(f'sudo cat {pw_file}')
+ self.assertIn(f'{interface}user', pw)
+ self.assertIn(f'{interface}secretpw', pw)
+
# check that no interface remained after deleting them
self.session.delete(base_path)
self.session.commit()
@@ -330,6 +344,8 @@ class TestInterfacesOpenVPN(unittest.TestCase):
self.session.set(path + ['local-port', port])
self.session.set(path + ['server', 'subnet', subnet])
self.session.set(path + ['server', 'topology', 'subnet'])
+ self.session.set(path + ['keep-alive', 'failure-count', '5'])
+ self.session.set(path + ['keep-alive', 'interval', '5'])
# clients
self.session.set(path + ['server', 'client', 'client1', 'ip', client_ip])
@@ -370,6 +386,7 @@ class TestInterfacesOpenVPN(unittest.TestCase):
self.assertIn(f'topology subnet', config)
self.assertIn(f'lport {port}', config)
self.assertIn(f'push "redirect-gateway def1"', config)
+ self.assertIn(f'keepalive 5 25', config)
# TLS options
self.assertIn(f'ca {ca_cert}', config)
@@ -423,6 +440,8 @@ class TestInterfacesOpenVPN(unittest.TestCase):
self.session.set(path + ['server', 'subnet', subnet])
self.session.set(path + ['server', 'topology', 'net30'])
self.session.set(path + ['replace-default-route'])
+ self.session.set(path + ['keep-alive', 'failure-count', '10'])
+ self.session.set(path + ['keep-alive', 'interval', '5'])
self.session.set(path + ['tls', 'ca-cert-file', ca_cert])
self.session.set(path + ['tls', 'cert-file', ssl_cert])
self.session.set(path + ['tls', 'key-file', ssl_key])
@@ -450,6 +469,7 @@ class TestInterfacesOpenVPN(unittest.TestCase):
self.assertIn(f'topology net30', config)
self.assertIn(f'lport {port}', config)
self.assertIn(f'push "redirect-gateway def1"', config)
+ self.assertIn(f'keepalive 5 50', config)
# TLS options
self.assertIn(f'ca {ca_cert}', config)
@@ -534,7 +554,7 @@ class TestInterfacesOpenVPN(unittest.TestCase):
self.session.commit()
- def test_site2site_interfaces(self):
+ def test_site2site_interfaces_tun(self):
"""
Create two OpenVPN site-to-site interfaces
"""
@@ -546,13 +566,22 @@ class TestInterfacesOpenVPN(unittest.TestCase):
for ii in num_range:
interface = f'vtun{ii}'
local_address = f'192.0.{ii}.1'
+ local_address_subnet = '255.255.255.252'
remote_address = f'172.16.{ii}.1'
path = base_path + [interface]
port = str(3000 + ii)
+ self.session.set(path + ['local-address', local_address])
+
+ # even numbers use tun type, odd numbers use tap type
+ if ii % 2 == 0:
+ self.session.set(path + ['device-type', 'tun'])
+ else:
+ self.session.set(path + ['device-type', 'tap'])
+ self.session.set(path + ['local-address', local_address, 'subnet-mask', local_address_subnet])
+
self.session.set(path + ['mode', 'site-to-site'])
self.session.set(path + ['local-port', port])
- self.session.set(path + ['local-address', local_address])
self.session.set(path + ['remote-port', port])
self.session.set(path + ['shared-secret-key-file', s2s_key])
self.session.set(path + ['remote-address', remote_address])
@@ -569,12 +598,19 @@ class TestInterfacesOpenVPN(unittest.TestCase):
config_file = f'/run/openvpn/{interface}.conf'
config = read_file(config_file)
+ # even numbers use tun type, odd numbers use tap type
+ if ii % 2 == 0:
+ self.assertIn(f'dev-type tun', config)
+ self.assertIn(f'ifconfig {local_address} {remote_address}', config)
+ else:
+ self.assertIn(f'dev-type tap', config)
+ self.assertIn(f'ifconfig {local_address} {local_address_subnet}', config)
+
self.assertIn(f'dev {interface}', config)
- self.assertIn(f'dev-type tun', config)
self.assertIn(f'secret {s2s_key}', config)
self.assertIn(f'lport {port}', config)
self.assertIn(f'rport {port}', config)
- self.assertIn(f'ifconfig {local_address} {remote_address}', config)
+
self.assertTrue(process_named_running(PROCESS_NAME))
self.assertEqual(get_vrf(interface), vrf_name)
diff --git a/smoketest/scripts/cli/test_interfaces_tunnel.py b/smoketest/scripts/cli/test_interfaces_tunnel.py
index 4817321cf..aaff92dea 100755
--- a/smoketest/scripts/cli/test_interfaces_tunnel.py
+++ b/smoketest/scripts/cli/test_interfaces_tunnel.py
@@ -168,12 +168,12 @@ class TunnelInterfaceTest(BasicInterfaceTest.BaseTest):
self.session.set(self._base_path + [interface, 'local-ip', self.local_v6])
self.session.set(self._base_path + [interface, 'remote-ip', remote_ip6])
- # Encapsulation mode requires IPv6 local-ip
+ # Encapsulation mode requires IPv4 local-ip
with self.assertRaises(ConfigSessionError):
self.session.commit()
self.session.set(self._base_path + [interface, 'local-ip', self.local_v4])
- # Encapsulation mode requires IPv6 local-ip
+ # Encapsulation mode requires IPv4 local-ip
with self.assertRaises(ConfigSessionError):
self.session.commit()
self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4])
@@ -360,7 +360,7 @@ class TunnelInterfaceTest(BasicInterfaceTest.BaseTest):
# No assertion is raised for GRE remote-ip when missing
self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4])
- # Source interface can not be used with si
+ # Source interface can not be used with sit
self.session.set(self._base_path + [interface, 'source-interface', source_if])
with self.assertRaises(ConfigSessionError):
self.session.commit()
diff --git a/smoketest/scripts/cli/test_policy_local-route.py b/smoketest/scripts/cli/test_policy_local-route.py
new file mode 100755
index 000000000..490bf6b47
--- /dev/null
+++ b/smoketest/scripts/cli/test_policy_local-route.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import unittest
+
+from vyos.configsession import ConfigSession
+from vyos.configsession import ConfigSessionError
+from vyos.util import cmd
+from vyos.util import process_named_running
+
+class PolicyLocalRouteTest(unittest.TestCase):
+ def setUp(self):
+ self.session = ConfigSession(os.getpid())
+ self._sources = ['203.0.113.1', '203.0.113.2']
+
+ def tearDown(self):
+ # Delete all policies
+ self.session.delete(['policy', 'local-route'])
+ self.session.commit()
+ del self.session
+
+ # Test set table for some sources
+ def test_table_id(self):
+ base = ['policy', 'local-route']
+ rule = '50'
+ table = '23'
+ for src in self._sources:
+ self.session.set(base + ['rule', rule, 'set', 'table', table])
+ self.session.set(base + ['rule', rule, 'source', src])
+
+ self.session.commit()
+
+ # Check generated configuration
+
+ # Expected values
+ original = """
+ 50: from 203.0.113.1 lookup 23
+ 50: from 203.0.113.2 lookup 23
+ """
+ tmp = cmd('ip rule show prio 50')
+ original = original.split()
+ tmp = tmp.split()
+
+ self.assertEqual(tmp, original)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/smoketest/scripts/cli/test_protocols_igmp-proxy.py b/smoketest/scripts/cli/test_protocols_igmp-proxy.py
new file mode 100755
index 000000000..f78581fea
--- /dev/null
+++ b/smoketest/scripts/cli/test_protocols_igmp-proxy.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019-2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import unittest
+
+from vyos.configsession import ConfigSession
+from vyos.configsession import ConfigSessionError
+from vyos.util import read_file
+from vyos.util import process_named_running
+
+PROCESS_NAME = 'igmpproxy'
+IGMP_PROXY_CONF = '/etc/igmpproxy.conf'
+base_path = ['protocols', 'igmp-proxy']
+upstream_if = 'eth1'
+downstream_if = 'eth2'
+
+class TestProtocolsIGMPProxy(unittest.TestCase):
+ def setUp(self):
+ self.session = ConfigSession(os.getpid())
+ self.session.set(['interfaces', 'ethernet', upstream_if, 'address', '172.16.1.1/24'])
+
+ def tearDown(self):
+ self.session.delete(['interfaces', 'ethernet', upstream_if, 'address'])
+ self.session.delete(base_path)
+ self.session.commit()
+ del self.session
+
+ def test_igmpproxy(self):
+ threshold = '20'
+ altnet = '192.0.2.0/24'
+ whitelist = '10.0.0.0/8'
+
+ self.session.set(base_path + ['disable-quickleave'])
+ self.session.set(base_path + ['interface', upstream_if, 'threshold', threshold])
+ self.session.set(base_path + ['interface', upstream_if, 'alt-subnet', altnet])
+ self.session.set(base_path + ['interface', upstream_if, 'whitelist', whitelist])
+
+ # Must define an upstream and at least 1 downstream interface!
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(base_path + ['interface', upstream_if, 'role', 'upstream'])
+
+ # Interface does not exist
+ self.session.set(base_path + ['interface', 'eth20', 'role', 'upstream'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(base_path + ['interface', 'eth20'])
+
+ # Only 1 upstream interface allowed
+ self.session.set(base_path + ['interface', downstream_if, 'role', 'upstream'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(base_path + ['interface', downstream_if, 'role', 'downstream'])
+
+ # commit changes
+ self.session.commit()
+
+ # Check generated configuration
+ config = read_file(IGMP_PROXY_CONF)
+ self.assertIn(f'phyint {upstream_if} upstream ratelimit 0 threshold {threshold}', config)
+ self.assertIn(f'altnet {altnet}', config)
+ self.assertIn(f'whitelist {whitelist}', config)
+ self.assertIn(f'phyint {downstream_if} downstream ratelimit 0 threshold 1', config)
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/smoketest/scripts/cli/test_service_dhcp-relay.py b/smoketest/scripts/cli/test_service_dhcp-relay.py
new file mode 100755
index 000000000..4b020db72
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_dhcp-relay.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import os
+import unittest
+
+from vyos.configsession import ConfigSession
+from vyos.configsession import ConfigSessionError
+from vyos.ifconfig import Section
+from vyos.util import cmd
+from vyos.util import process_named_running
+from vyos.util import read_file
+
+PROCESS_NAME = 'dhcrelay'
+RELAY_CONF = '/run/dhcp-relay/dhcrelay.conf'
+base_path = ['service', 'dhcp-relay']
+
+class TestServiceDHCPRelay(unittest.TestCase):
+ def setUp(self):
+ self.session = ConfigSession(os.getpid())
+
+ def tearDown(self):
+ self.session.delete(base_path)
+ self.session.commit()
+ del self.session
+
+ def test_relay_default(self):
+ max_size = '800'
+ hop_count = '20'
+ agents_packets = 'append'
+ servers = ['192.0.2.1', '192.0.2.2']
+
+ self.session.set(base_path + ['interface', 'lo'])
+ # check validate() - DHCP relay does not support the loopback interface
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(base_path + ['interface', 'lo'])
+
+ # activate DHCP relay on all ethernet interfaces
+ for tmp in Section.interfaces("ethernet"):
+ self.session.set(base_path + ['interface', tmp])
+
+ # check validate() - No DHCP relay server(s) configured
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ for server in servers:
+ self.session.set(base_path + ['server', server])
+
+ self.session.set(base_path + ['relay-options', 'max-size', max_size])
+ self.session.set(base_path + ['relay-options', 'hop-count', hop_count])
+ self.session.set(base_path + ['relay-options', 'relay-agents-packets', agents_packets])
+
+ # commit changes
+ self.session.commit()
+
+ # Check configured port
+ config = read_file(RELAY_CONF)
+
+ # Test configured relay interfaces
+ for tmp in Section.interfaces("ethernet"):
+ self.assertIn(f'-i {tmp}', config)
+
+ # Test relay servers
+ for server in servers:
+ self.assertIn(f' {server}', config)
+
+ # Test max-size
+ self.assertIn(f'-A {max_size}', config)
+ # Hop count
+ self.assertIn(f'-c {hop_count}', config)
+ # relay-agents-packets
+ self.assertIn(f'-a -m {agents_packets}', config)
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+if __name__ == '__main__':
+ unittest.main()
+
diff --git a/smoketest/scripts/cli/test_service_dhcpv6-relay.py b/smoketest/scripts/cli/test_service_dhcpv6-relay.py
new file mode 100755
index 000000000..ccc849a4f
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_dhcpv6-relay.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import os
+import unittest
+
+from vyos.configsession import ConfigSession
+from vyos.configsession import ConfigSessionError
+from vyos.ifconfig import Section
+from vyos.template import address_from_cidr
+from vyos.util import cmd
+from vyos.util import process_named_running
+from vyos.util import read_file
+
+PROCESS_NAME = 'dhcrelay'
+RELAY_CONF = '/run/dhcp-relay/dhcrelay6.conf'
+base_path = ['service', 'dhcpv6-relay']
+
+upstream_if = 'eth0'
+upstream_if_addr = '2001:db8::1/64'
+listen_addr = '2001:db8:ffff::1/64'
+interfaces = []
+
+class TestServiceDHCPv6Relay(unittest.TestCase):
+ def setUp(self):
+ self.session = ConfigSession(os.getpid())
+ for tmp in interfaces:
+ listen = listen_addr
+ if tmp == upstream_if:
+ listen = upstream_if_addr
+ self.session.set(['interfaces', 'ethernet', tmp, 'address', listen])
+
+ def tearDown(self):
+ self.session.delete(base_path)
+ for tmp in interfaces:
+ listen = listen_addr
+ if tmp == upstream_if:
+ listen = upstream_if_addr
+ self.session.delete(['interfaces', 'ethernet', tmp, 'address', listen])
+
+ self.session.commit()
+ del self.session
+
+ def test_relay_default(self):
+ dhcpv6_server = '2001:db8::ffff'
+ hop_count = '20'
+
+ self.session.set(base_path + ['use-interface-id-option'])
+ self.session.set(base_path + ['max-hop-count', hop_count])
+
+ # check validate() - Must set at least one listen and upstream
+ # interface addresses.
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(base_path + ['upstream-interface', upstream_if, 'address', dhcpv6_server])
+
+ # check validate() - Must set at least one listen and upstream
+ # interface addresses.
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+
+ # add listener on all ethernet interfaces except the upstream interface
+ for tmp in interfaces:
+ if tmp == upstream_if:
+ continue
+ self.session.set(base_path + ['listen-interface', tmp, 'address', listen_addr.split('/')[0]])
+
+ # commit changes
+ self.session.commit()
+
+ # Check configured port
+ config = read_file(RELAY_CONF)
+
+ # Test configured upstream interfaces
+ self.assertIn(f'-u {dhcpv6_server}%{upstream_if}', config)
+
+ # Check listener on all ethernet interfaces
+ for tmp in interfaces:
+ if tmp == upstream_if:
+ continue
+ addr = listen_addr.split('/')[0]
+ self.assertIn(f'-l {addr}%{tmp}', config)
+
+ # Check hop count
+ self.assertIn(f'-c {hop_count}', config)
+ # Check Interface ID option
+ self.assertIn('-I', config)
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+if __name__ == '__main__':
+ for tmp in Section.interfaces('ethernet'):
+ if '.' not in tmp:
+ interfaces.append(tmp)
+
+ unittest.main()
+
diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py
index f0c71e2de..ed006f16c 100755
--- a/smoketest/scripts/cli/test_service_pppoe-server.py
+++ b/smoketest/scripts/cli/test_service_pppoe-server.py
@@ -22,12 +22,9 @@ from base_accel_ppp_test import BasicAccelPPPTest
from configparser import ConfigParser
from vyos.configsession import ConfigSessionError
from vyos.util import process_named_running
-from vyos.util import cmd
local_if = ['interfaces', 'dummy', 'dum667']
-
ac_name = 'ACN'
-
interface = 'eth0'
class TestServicePPPoEServer(BasicAccelPPPTest.BaseTest):
diff --git a/smoketest/scripts/cli/test_system_ntp.py b/smoketest/scripts/cli/test_system_ntp.py
index 822a9aff2..e2744c936 100755
--- a/smoketest/scripts/cli/test_system_ntp.py
+++ b/smoketest/scripts/cli/test_system_ntp.py
@@ -51,11 +51,15 @@ class TestSystemNTP(unittest.TestCase):
""" Test basic NTP support with multiple servers and their options """
servers = ['192.0.2.1', '192.0.2.2']
options = ['noselect', 'preempt', 'prefer']
+ ntp_pool = 'pool.vyos.io'
for server in servers:
for option in options:
self.session.set(base_path + ['server', server, option])
+ # Test NTP pool
+ self.session.set(base_path + ['server', ntp_pool, 'pool'])
+
# commit changes
self.session.commit()
@@ -65,6 +69,9 @@ class TestSystemNTP(unittest.TestCase):
test = f'{server} iburst ' + ' '.join(options)
self.assertTrue(test in tmp)
+ tmp = get_config_value('pool')
+ self.assertTrue(f'{ntp_pool} iburst' in tmp)
+
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
diff --git a/src/conf_mode/bcast_relay.py b/src/conf_mode/bcast_relay.py
index 78daeb6be..d93a2a8f4 100755
--- a/src/conf_mode/bcast_relay.py
+++ b/src/conf_mode/bcast_relay.py
@@ -79,7 +79,7 @@ def generate(relay):
config['instance'] = instance
render(config_file_base + instance, 'bcast-relay/udp-broadcast-relay.tmpl',
- config, trim_blocks=True)
+ config)
return None
diff --git a/src/conf_mode/dhcp_relay.py b/src/conf_mode/dhcp_relay.py
index 352865b9d..6352e0b4a 100755
--- a/src/conf_mode/dhcp_relay.py
+++ b/src/conf_mode/dhcp_relay.py
@@ -19,81 +19,43 @@ import os
from sys import exit
from vyos.config import Config
+from vyos.configdict import dict_merge
from vyos.template import render
from vyos.util import call
+from vyos.util import dict_search
+from vyos.xml import defaults
from vyos import ConfigError
-
from vyos import airbag
airbag.enable()
-config_file = r'/run/dhcp-relay/dhcp.conf'
-
-default_config_data = {
- 'interface': [],
- 'server': [],
- 'options': [],
- 'hop_count': '10',
- 'relay_agent_packets': 'forward'
-}
+config_file = r'/run/dhcp-relay/dhcrelay.conf'
def get_config(config=None):
- relay = default_config_data
if config:
conf = config
else:
conf = Config()
- if not conf.exists(['service', 'dhcp-relay']):
+ base = ['service', 'dhcp-relay']
+ if not conf.exists(base):
return None
- else:
- conf.set_level(['service', 'dhcp-relay'])
-
- # Network interfaces to listen on
- 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'])
-
- conf.set_level(['service', 'dhcp-relay', 'relay-options'])
-
- 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.
- # This might be done to allow sufficient space for addition of relay agent
- # 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'])
- relay['options'].append(size)
-
- # Control the handling of incoming DHCPv4 packets which already contain
- # relay agent options. If such a packet does not have giaddr set in its
- # header, the DHCP standard requires that the packet be discarded. However,
- # if giaddr is set, the relay agent may handle the situation in four ways:
- # It may append its own set of relay options to the packet, leaving the
- # supplied option field intact; it may replace the existing agent option
- # 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'])
- relay['options'].append(pkt)
+
+ relay = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ default_values = defaults(base)
+ relay = dict_merge(default_values, relay)
return relay
def verify(relay):
# bail out early - looks like removal from running config
- if relay is None:
+ if not relay:
return None
- if 'lo' in relay['interface']:
+ if 'lo' in (dict_search('interface', relay) or []):
raise ConfigError('DHCP relay does not support the loopback interface.')
- if len(relay['server']) == 0:
+ if 'server' not in relay :
raise ConfigError('No DHCP relay server(s) configured.\n' \
'At least one DHCP relay server required.')
@@ -104,17 +66,18 @@ def generate(relay):
if not relay:
return None
- render(config_file, 'dhcp-relay/config.tmpl', relay)
+ render(config_file, 'dhcp-relay/dhcrelay.conf.tmpl', relay)
return None
def apply(relay):
- if relay:
- call('systemctl restart isc-dhcp-relay.service')
- else:
- # DHCP relay support is removed in the commit
+ # bail out early - looks like removal from running config
+ if not relay:
call('systemctl stop isc-dhcp-relay.service')
if os.path.exists(config_file):
os.unlink(config_file)
+ return None
+
+ call('systemctl restart isc-dhcp-relay.service')
return None
diff --git a/src/conf_mode/dhcpv6_relay.py b/src/conf_mode/dhcpv6_relay.py
index d4212b8be..cf8a26674 100755
--- a/src/conf_mode/dhcpv6_relay.py
+++ b/src/conf_mode/dhcpv6_relay.py
@@ -17,90 +17,84 @@
import os
from sys import exit
-from copy import deepcopy
from vyos.config import Config
-from vyos import ConfigError
-from vyos.util import call
+from vyos.configdict import dict_merge
+from vyos.ifconfig import Interface
from vyos.template import render
-
+from vyos.util import call
+from vyos.util import dict_search
+from vyos.validate import is_ipv6_link_local
+from vyos.xml import defaults
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
-config_file = r'/run/dhcp-relay/dhcpv6.conf'
-
-default_config_data = {
- 'listen_addr': [],
- 'upstream_addr': [],
- 'options': [],
-}
+config_file = '/run/dhcp-relay/dhcrelay6.conf'
def get_config(config=None):
- relay = deepcopy(default_config_data)
if config:
conf = config
else:
conf = Config()
- if not conf.exists('service dhcpv6-relay'):
+ base = ['service', 'dhcpv6-relay']
+ if not conf.exists(base):
return None
- else:
- conf.set_level('service dhcpv6-relay')
-
- # Network interfaces/address to listen on for DHCPv6 query(s)
- if conf.exists('listen-interface'):
- interfaces = conf.list_nodes('listen-interface')
- for intf in interfaces:
- if conf.exists('listen-interface {0} address'.format(intf)):
- addr = conf.return_value('listen-interface {0} address'.format(intf))
- listen = addr + '%' + intf
- relay['listen_addr'].append(listen)
-
- # Upstream interface/address for remote DHCPv6 server
- if conf.exists('upstream-interface'):
- interfaces = conf.list_nodes('upstream-interface')
- for intf in interfaces:
- addresses = conf.return_values('upstream-interface {0} address'.format(intf))
- for addr in addresses:
- server = addr + '%' + intf
- relay['upstream_addr'].append(server)
-
- # Maximum hop count. When forwarding packets, dhcrelay discards packets
- # which have reached a hop count of COUNT. Default is 10. Maximum is 255.
- if conf.exists('max-hop-count'):
- count = '-c ' + conf.return_value('max-hop-count')
- relay['options'].append(count)
-
- if conf.exists('use-interface-id-option'):
- relay['options'].append('-I')
+
+ relay = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ default_values = defaults(base)
+ relay = dict_merge(default_values, relay)
return relay
def verify(relay):
# bail out early - looks like removal from running config
- if relay is None:
+ if not relay:
return None
- if len(relay['listen_addr']) == 0 or len(relay['upstream_addr']) == 0:
- raise ConfigError('Must set at least one listen and upstream interface addresses.')
+ if 'upstream_interface' not in relay:
+ raise ConfigError('At least one upstream interface required!')
+ for interface, config in relay['upstream_interface'].items():
+ if 'address' not in config:
+ raise ConfigError('DHCPv6 server required for upstream ' \
+ f'interface {interface}!')
+
+ if 'listen_interface' not in relay:
+ raise ConfigError('At least one listen interface required!')
+
+ # DHCPv6 relay requires at least one global unicat address assigned to the
+ # interface
+ for interface in relay['listen_interface']:
+ has_global = False
+ for addr in Interface(interface).get_addr():
+ if not is_ipv6_link_local(addr.split('/')[0]):
+ has_global = True
+ if not has_global:
+ raise ConfigError(f'Interface {interface} does not have global '\
+ 'IPv6 address assigned!')
return None
def generate(relay):
# bail out early - looks like removal from running config
- if relay is None:
+ if not relay:
return None
- render(config_file, 'dhcpv6-relay/config.tmpl', relay)
+ render(config_file, 'dhcp-relay/dhcrelay6.conf.tmpl', relay)
return None
def apply(relay):
- if relay is not None:
- call('systemctl restart isc-dhcp-relay6.service')
- else:
+ # bail out early - looks like removal from running config
+ if not relay:
# DHCPv6 relay support is removed in the commit
call('systemctl stop isc-dhcp-relay6.service')
if os.path.exists(config_file):
os.unlink(config_file)
+ return None
+
+ call('systemctl restart isc-dhcp-relay6.service')
return None
diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py
index ef52cbfd3..c44e6c974 100755
--- a/src/conf_mode/dns_forwarding.py
+++ b/src/conf_mode/dns_forwarding.py
@@ -114,10 +114,10 @@ def generate(dns):
return None
render(pdns_rec_config_file, 'dns-forwarding/recursor.conf.tmpl',
- dns, trim_blocks=True, user=pdns_rec_user, group=pdns_rec_group)
+ dns, user=pdns_rec_user, group=pdns_rec_group)
render(pdns_rec_lua_conf_file, 'dns-forwarding/recursor.conf.lua.tmpl',
- dns, trim_blocks=True, user=pdns_rec_user, group=pdns_rec_group)
+ dns, user=pdns_rec_user, group=pdns_rec_group)
# if vyos-hostsd didn't create its files yet, create them (empty)
for file in [pdns_rec_hostsd_lua_conf_file, pdns_rec_hostsd_zones_file]:
diff --git a/src/conf_mode/dynamic_dns.py b/src/conf_mode/dynamic_dns.py
index 93e995b78..6d39c6644 100755
--- a/src/conf_mode/dynamic_dns.py
+++ b/src/conf_mode/dynamic_dns.py
@@ -131,7 +131,9 @@ def generate(dyndns):
if not dyndns:
return None
- render(config_file, 'dynamic-dns/ddclient.conf.tmpl', dyndns, trim_blocks=True, permission=0o600)
+ render(config_file, 'dynamic-dns/ddclient.conf.tmpl', dyndns,
+ permission=0o600)
+
return None
def apply(dyndns):
diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py
index de228f0f8..a6e2d9c8c 100755
--- a/src/conf_mode/https.py
+++ b/src/conf_mode/https.py
@@ -159,7 +159,7 @@ def generate(https):
if 'server_block_list' not in https or not https['server_block_list']:
https['server_block_list'] = [default_server_block]
- render(config_file, 'https/nginx.default.tmpl', https, trim_blocks=True)
+ render(config_file, 'https/nginx.default.tmpl', https)
return None
diff --git a/src/conf_mode/igmp_proxy.py b/src/conf_mode/igmp_proxy.py
index 754f46566..fb030c9f3 100755
--- a/src/conf_mode/igmp_proxy.py
+++ b/src/conf_mode/igmp_proxy.py
@@ -17,90 +17,65 @@
import os
from sys import exit
-from copy import deepcopy
-
from netifaces import interfaces
+
from vyos.config import Config
-from vyos import ConfigError
-from vyos.util import call
+from vyos.configdict import dict_merge
from vyos.template import render
-
+from vyos.util import call
+from vyos.util import dict_search
+from vyos.xml import defaults
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
config_file = r'/etc/igmpproxy.conf'
-default_config_data = {
- 'disable': False,
- 'disable_quickleave': False,
- 'interfaces': [],
-}
-
def get_config(config=None):
- igmp_proxy = deepcopy(default_config_data)
if config:
conf = config
else:
conf = Config()
- base = ['protocols', 'igmp-proxy']
- if not conf.exists(base):
- return None
- else:
- conf.set_level(base)
-
- # Network interfaces to listen on
- if conf.exists(['disable']):
- igmp_proxy['disable'] = True
-
- # Option to disable "quickleave"
- if conf.exists(['disable-quickleave']):
- igmp_proxy['disable_quickleave'] = True
- for intf in conf.list_nodes(['interface']):
- conf.set_level(base + ['interface', intf])
- interface = {
- 'name': intf,
- 'alt_subnet': [],
- 'role': 'downstream',
- 'threshold': '1',
- 'whitelist': []
- }
-
- if conf.exists(['alt-subnet']):
- interface['alt_subnet'] = conf.return_values(['alt-subnet'])
+ base = ['protocols', 'igmp-proxy']
+ igmp_proxy = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- if conf.exists(['role']):
- interface['role'] = conf.return_value(['role'])
+ if 'interface' in igmp_proxy:
+ # T2665: we must add the tagNode defaults individually until this is
+ # moved to the base class
+ default_values = defaults(base + ['interface'])
+ for interface in igmp_proxy['interface']:
+ igmp_proxy['interface'][interface] = dict_merge(default_values,
+ igmp_proxy['interface'][interface])
- if conf.exists(['threshold']):
- interface['threshold'] = conf.return_value(['threshold'])
- if conf.exists(['whitelist']):
- interface['whitelist'] = conf.return_values(['whitelist'])
+ if conf.exists(['protocols', 'igmp']):
+ igmp_proxy.update({'igmp_configured': ''})
- # Append interface configuration to global configuration list
- igmp_proxy['interfaces'].append(interface)
+ if conf.exists(['protocols', 'pim']):
+ igmp_proxy.update({'pim_configured': ''})
return igmp_proxy
def verify(igmp_proxy):
# bail out early - looks like removal from running config
- if igmp_proxy is None:
+ if not igmp_proxy or 'disable' in igmp_proxy:
return None
- # bail out early - service is disabled
- if igmp_proxy['disable']:
- return None
+ if 'igmp_configured' in igmp_proxy or 'pim_configured' in igmp_proxy:
+ raise ConfigError('Can not configure both IGMP proxy and PIM '\
+ 'at the same time')
# at least two interfaces are required, one upstream and one downstream
- if len(igmp_proxy['interfaces']) < 2:
- raise ConfigError('Must define an upstream and at least 1 downstream interface!')
+ if 'interface' not in igmp_proxy or len(igmp_proxy['interface']) < 2:
+ raise ConfigError('Must define exactly one upstream and at least one ' \
+ 'downstream interface!')
upstream = 0
- for interface in igmp_proxy['interfaces']:
- if interface['name'] not in interfaces():
- raise ConfigError('Interface "{}" does not exist'.format(interface['name']))
- if "upstream" == interface['role']:
+ for interface, config in igmp_proxy['interface'].items():
+ if interface not in interfaces():
+ raise ConfigError(f'Interface "{interface}" does not exist')
+ if dict_search('role', config) == 'upstream':
upstream += 1
if upstream == 0:
@@ -112,19 +87,20 @@ def verify(igmp_proxy):
def generate(igmp_proxy):
# bail out early - looks like removal from running config
- if igmp_proxy is None:
+ if not igmp_proxy:
return None
# bail out early - service is disabled, but inform user
- if igmp_proxy['disable']:
- print('Warning: IGMP Proxy will be deactivated because it is disabled')
+ if 'disable' in igmp_proxy:
+ print('WARNING: IGMP Proxy will be deactivated because it is disabled')
return None
render(config_file, 'igmp-proxy/igmpproxy.conf.tmpl', igmp_proxy)
+
return None
def apply(igmp_proxy):
- if igmp_proxy is None or igmp_proxy['disable']:
+ if not igmp_proxy or 'disable' in igmp_proxy:
# IGMP Proxy support is removed in the commit
call('systemctl stop igmpproxy.service')
if os.path.exists(config_file):
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index 076bdb63e..7af3e3d7c 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -123,12 +123,12 @@ def get_config(config=None):
# VLAN-aware bridge members must not have VLAN interface configuration
if 'native_vlan' in interface_config:
- if 'disable' not in interface_config['native_vlan']:
- vlan_aware = True
+ vlan_aware = True
if 'allowed_vlan' in interface_config:
vlan_aware = True
+
if vlan_aware:
tmp = has_vlan_subinterface_configured(conf,interface)
if tmp:
@@ -142,6 +142,8 @@ def verify(bridge):
verify_dhcpv6(bridge)
verify_vrf(bridge)
+
+ vlan_aware = False
if dict_search('member.interface', bridge):
for interface, interface_config in bridge['member']['interface'].items():
@@ -168,6 +170,16 @@ def verify(bridge):
if 'has_vlan' in interface_config:
raise ConfigError(error_msg + 'it has an VLAN subinterface assigned!')
+ # VLAN-aware bridge members must not have VLAN interface configuration
+ if 'native_vlan' in interface_config:
+ vlan_aware = True
+
+ if 'allowed_vlan' in interface_config:
+ vlan_aware = True
+
+ if vlan_aware and 'wlan' in interface:
+ raise ConfigError(error_msg + 'VLAN aware cannot be set!')
+
if 'allowed_vlan' in interface_config:
for vlan in interface_config['allowed_vlan']:
if re.search('[0-9]{1,4}-[0-9]{1,4}', vlan):
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index c23e79948..25920f893 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -120,7 +120,7 @@ def verify(openvpn):
# OpenVPN site-to-site - VERIFY
#
elif openvpn['mode'] == 'site-to-site':
- if not 'local_address' in openvpn:
+ if 'local_address' not in openvpn and 'is_bridge_member' not in openvpn:
raise ConfigError('Must specify "local-address" or add interface to bridge')
if len([addr for addr in openvpn['local_address'] if is_ipv4(addr)]) > 1:
@@ -166,15 +166,16 @@ def verify(openvpn):
if dict_search('remote_host', openvpn) in dict_search('remote_address', openvpn):
raise ConfigError('"remote-address" and "remote-host" can not be the same')
-
- if 'local_address' in openvpn:
+ if openvpn['device_type'] == 'tap':
# we can only have one local_address, this is ensured above
v4addr = None
for laddr in openvpn['local_address']:
- if is_ipv4(laddr): v4addr = laddr
+ if is_ipv4(laddr):
+ v4addr = laddr
+ break
- if 'remote_address' not in openvpn and (v4addr not in openvpn['local_address'] or 'subnet_mask' not in openvpn['local_address'][v4addr]):
- raise ConfigError('IPv4 "local-address" requires IPv4 "remote-address" or IPv4 "local-address subnet"')
+ if v4addr in openvpn['local_address'] and 'subnet_mask' not in openvpn['local_address'][v4addr]:
+ raise ConfigError('Must specify IPv4 "subnet-mask" for local-address')
if dict_search('encryption.ncp_ciphers', openvpn):
raise ConfigError('NCP ciphers can only be used in client or server mode')
@@ -464,12 +465,9 @@ def generate(openvpn):
if tmp: fix_permissions.append(tmp)
# Generate User/Password authentication file
- if 'auth' in openvpn:
- with open(openvpn['auth_user_pass_file'], 'w') as f:
- f.write('{}\n{}'.format(openvpn['auth_user'], openvpn['auth_pass']))
- # also change permission on auth file
- fix_permissions.append(openvpn['auth_user_pass_file'])
-
+ if 'authentication' in openvpn:
+ render(openvpn['auth_user_pass_file'], 'openvpn/auth.pw.tmpl', openvpn,
+ user=user, group=group, permission=0o600)
else:
# delete old auth file if present
if os.path.isfile(openvpn['auth_user_pass_file']):
@@ -483,17 +481,13 @@ def generate(openvpn):
# Our client need's to know its subnet mask ...
client_config['server_subnet'] = dict_search('server.subnet', openvpn)
- import pprint
- pprint.pprint(client_config)
-
render(client_file, 'openvpn/client.conf.tmpl', client_config,
- trim_blocks=True, user=user, group=group)
+ user=user, group=group)
# we need to support quoting of raw parameters from OpenVPN CLI
# see https://phabricator.vyos.net/T1632
render(cfg_file.format(**openvpn), 'openvpn/server.conf.tmpl', openvpn,
- trim_blocks=True, formater=lambda _: _.replace("&quot;", '"'),
- user=user, group=group)
+ formater=lambda _: _.replace("&quot;", '"'), user=user, group=group)
# Fixup file permissions
for file in fix_permissions:
diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py
index ee3b142c8..c31e49574 100755
--- a/src/conf_mode/interfaces-pppoe.py
+++ b/src/conf_mode/interfaces-pppoe.py
@@ -93,25 +93,25 @@ def generate(pppoe):
return None
# Create PPP configuration files
- render(config_pppoe, 'pppoe/peer.tmpl',
- pppoe, trim_blocks=True, permission=0o755)
+ render(config_pppoe, 'pppoe/peer.tmpl', pppoe, permission=0o755)
+
# Create script for ip-pre-up.d
- render(script_pppoe_pre_up, 'pppoe/ip-pre-up.script.tmpl',
- pppoe, trim_blocks=True, permission=0o755)
+ render(script_pppoe_pre_up, 'pppoe/ip-pre-up.script.tmpl', pppoe,
+ permission=0o755)
# Create script for ip-up.d
- render(script_pppoe_ip_up, 'pppoe/ip-up.script.tmpl',
- pppoe, trim_blocks=True, permission=0o755)
+ render(script_pppoe_ip_up, 'pppoe/ip-up.script.tmpl', pppoe,
+ permission=0o755)
# Create script for ip-down.d
- render(script_pppoe_ip_down, 'pppoe/ip-down.script.tmpl',
- pppoe, trim_blocks=True, permission=0o755)
+ render(script_pppoe_ip_down, 'pppoe/ip-down.script.tmpl', pppoe,
+ permission=0o755)
# Create script for ipv6-up.d
- render(script_pppoe_ipv6_up, 'pppoe/ipv6-up.script.tmpl',
- pppoe, trim_blocks=True, permission=0o755)
+ render(script_pppoe_ipv6_up, 'pppoe/ipv6-up.script.tmpl', pppoe,
+ permission=0o755)
if 'dhcpv6_options' in pppoe and 'pd' in pppoe['dhcpv6_options']:
# ipv6.tmpl relies on ifname - this should be made consitent in the
# future better then double key-ing the same value
- render(config_wide_dhcp6c, 'dhcp-client/ipv6.tmpl', pppoe, trim_blocks=True)
+ render(config_wide_dhcp6c, 'dhcp-client/ipv6.tmpl', pppoe)
return None
diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py
index f1217b62d..1a7e9a96d 100755
--- a/src/conf_mode/interfaces-tunnel.py
+++ b/src/conf_mode/interfaces-tunnel.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 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,354 +15,124 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import netifaces
from sys import exit
-from copy import deepcopy
from netifaces import interfaces
from vyos.config import Config
-from vyos.configdict import is_member
-from vyos.configdict import list_diff
-from vyos.dicts import FixedDict
-from vyos.ifconfig import Interface, GREIf, GRETapIf, IPIPIf, IP6GREIf, IPIP6If, IP6IP6If, SitIf, Sit6RDIf
-from vyos.ifconfig.afi import IP4, IP6
+from vyos.configdict import dict_merge
+from vyos.configdict import get_interface_dict
+from vyos.configdict import node_changed
+from vyos.configdict import leaf_node_changed
+from vyos.configverify import verify_vrf
+from vyos.configverify import verify_address
+from vyos.configverify import verify_bridge_delete
+from vyos.configverify import verify_mtu_ipv6
+from vyos.ifconfig import Interface
+from vyos.ifconfig import GREIf
+from vyos.ifconfig import GRETapIf
+from vyos.ifconfig import IPIPIf
+from vyos.ifconfig import IP6GREIf
+from vyos.ifconfig import IPIP6If
+from vyos.ifconfig import IP6IP6If
+from vyos.ifconfig import SitIf
+from vyos.ifconfig import Sit6RDIf
from vyos.template import is_ipv4
from vyos.template import is_ipv6
+from vyos.util import dict_search
from vyos import ConfigError
-
-
from vyos import airbag
airbag.enable()
-
-class ConfigurationState(object):
+def get_config(config=None):
"""
- The current API require a dict to be generated by get_config()
- which is then consumed by verify(), generate() and apply()
-
- ConfiguartionState is an helper class wrapping Config and providing
- an common API to this dictionary structure
-
- Its to_api() function return a dictionary containing three fields,
- each a dict, called options, changes, actions.
-
- options:
-
- contains the configuration options for the dict and its value
- {'options': {'commment': 'test'}} will be set if
- 'set interface dummy dum1 description test' was used and
- the key 'commment' is used to index the description info.
-
- changes:
-
- per key, let us know how the data was modified using one of the action
- a special key called 'section' is used to indicate what happened to the
- section. for example:
-
- 'set interface dummy dum1 description test' when no interface was setup
- will result in the following changes
- {'changes': {'section': 'create', 'comment': 'create'}}
-
- on an existing interface, depending if there was a description
- 'set interface dummy dum1 description test' will result in one of
- {'changes': {'comment': 'create'}} (not present before)
- {'changes': {'comment': 'static'}} (unchanged)
- {'changes': {'comment': 'modify'}} (changed from half)
-
- and 'delete interface dummy dummy1 description' will result in:
- {'changes': {'comment': 'delete'}}
-
- actions:
-
- for each action list the configuration key which were changes
- in our example if we added the 'description' and added an IP we would have
- {'actions': { 'create': ['comment'], 'modify': ['addresses-add']}}
-
- the actions are:
- 'create': it did not exist previously and was created
- 'modify': it did exist previously but its content changed
- 'static': it did exist and did not change
- 'delete': it was present but was removed from the configuration
- 'absent': it was not and is not present
- which for each field represent how it was modified since the last commit
+ Retrive CLI config as dictionary. Dictionary can never be empty, as at least
+ the interface name will be added or a deleted flag
"""
+ if config:
+ conf = config
+ else:
+ conf = Config()
+ base = ['interfaces', 'tunnel']
+ tunnel = get_interface_dict(conf, base)
- def __init__(self, configuration, section, default):
- """
- initialise the class for a given configuration path:
-
- >>> conf = ConfigurationState(conf, 'interfaces ethernet eth1')
- all further references to get_value(s) and get_effective(s)
- will be for this part of the configuration (eth1)
- """
- self._conf = configuration
-
- self.default = deepcopy(default)
- self.options = FixedDict(**default)
- self.actions = {
- 'create': [], # the key did not exist and was added
- 'static': [], # the key exists and its value was not modfied
- 'modify': [], # the key exists and its value was modified
- 'absent': [], # the key is not present
- 'delete': [], # the key was present and was deleted
- }
- self.changes = {}
- if not self._conf.exists(section):
- self.changes['section'] = 'delete'
- elif self._conf.exists_effective(section):
- self.changes['section'] = 'modify'
- else:
- self.changes['section'] = 'create'
-
- self.set_level(section)
-
- def set_level(self, lpath):
- self.section = lpath
- self._conf.set_level(lpath)
-
- def _act(self, section):
- """
- Returns for a given configuration field determine what happened to it
-
- 'create': it did not exist previously and was created
- 'modify': it did exist previously but its content changed
- 'static': it did exist and did not change
- 'delete': it was present but was removed from the configuration
- 'absent': it was not and is not present
- """
- if self._conf.exists(section):
- if self._conf.exists_effective(section):
- if self._conf.return_value(section) != self._conf.return_effective_value(section):
- return 'modify'
- return 'static'
- return 'create'
- else:
- if self._conf.exists_effective(section):
- return 'delete'
- return 'absent'
-
- def _action(self, name, key):
- action = self._act(key)
- self.changes[name] = action
- self.actions[action].append(name)
- return action
-
- def _get(self, name, key, default, getter):
- value = getter(key)
- if not value:
- if default:
- self.options[name] = default
- return
- self.options[name] = self.default[name]
- return
- self.options[name] = value
-
- def get_value(self, name, key, default=None):
- """
- >>> conf.get_value('comment', 'description')
- will place the string of 'interface dummy description test'
- into the dictionnary entry 'comment' using Config.return_value
- (the data in the configuration to apply)
- """
- if self._action(name, key) in ('delete', 'absent'):
- return
- return self._get(name, key, default, self._conf.return_value)
-
- def get_values(self, name, key, default=None):
- """
- >>> conf.get_values('addresses', 'address')
- will place a list of the new IP present in 'interface dummy dum1 address'
- into the dictionnary entry "-add" (here 'addresses-add') using
- Config.return_values and will add the the one which were removed in into
- the entry "-del" (here addresses-del')
- """
- add_name = f'{name}-add'
-
- if self._action(add_name, key) in ('delete', 'absent'):
- return
-
- self._get(add_name, key, default, self._conf.return_values)
-
- # get the effective values to determine which data is no longer valid
- self.options['addresses-del'] = list_diff(
- self._conf.return_effective_values('address'),
- self.options['addresses-add']
- )
-
- def get_effective(self, name, key, default=None):
- """
- >>> conf.get_value('comment', 'description')
- will place the string of 'interface dummy description test'
- into the dictionnary entry 'comment' using Config.return_effective_value
- (the data in the configuration to apply)
- """
- self._action(name, key)
- return self._get(name, key, default, self._conf.return_effective_value)
-
- def get_effectives(self, name, key, default=None):
- """
- >>> conf.get_effectives('addresses-add', 'address')
- will place a list made of the IP present in 'interface ethernet eth1 address'
- into the dictionnary entry 'addresses-add' using Config.return_effectives_value
- (the data in the un-modified configuration)
- """
- self._action(name, key)
- return self._get(name, key, default, self._conf.return_effectives_value)
+ # Wireguard is "special" the default MTU is 1420 - update accordingly
+ # as the config_level is already st in get_interface_dict() - we can use []
+ tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True)
+ if 'mtu' not in tmp:
+ tunnel['mtu'] = '1476'
- def load(self, mapping):
- """
- load will take a dictionary defining how we wish the configuration
- to be parsed and apply this definition to set the data.
+ tmp = leaf_node_changed(conf, ['encapsulation'])
+ if tmp: tunnel.update({'encapsulation_changed': {}})
- >>> mapping = {
- 'addresses-add' : ('address', True, None),
- 'comment' : ('description', False, 'auto'),
- }
- >>> conf.load(mapping)
+ # We must check if our interface is configured to be a DMVPN member
+ nhrp_base = ['protocols', 'nhrp', 'tunnel']
+ conf.set_level(nhrp_base)
+ nhrp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True)
+ if nhrp: tunnel.update({'nhrp' : list(nhrp.keys())})
- mapping is a dictionary where each key represents the name we wish
- to have (such as 'addresses-add'), with a list a content representing
- how the data should be parsed:
- - the configuration section name
- such as 'address' under 'interface ethernet eth1'
- - boolean indicating if this data can have multiple values
- for 'address', True, as multiple IPs can be set
- for 'description', False, as it is a single string
- - default represent the default value if absent from the configuration
- 'None' indicate that no default should be set if the configuration
- does not have the configuration section
+ return tunnel
- """
- for local_name, (config_name, multiple, default) in mapping.items():
- if multiple:
- self.get_values(local_name, config_name, default)
- else:
- self.get_value(local_name, config_name, default)
+def verify(tunnel):
+ if 'deleted' in tunnel:
+ verify_bridge_delete(tunnel)
- def remove_default(self,*options):
- """
- remove all the values which were not changed from the default
- """
- for option in options:
- if not self._conf.exists(option):
- del self.options[option]
- continue
+ if 'nhrp' in tunnel and tunnel['ifname'] in tunnel['nhrp']:
+ raise ConfigError('Tunnel used for NHRP, it can not be deleted!')
- if self._conf.return_value(option) == self.default[option]:
- del self.options[option]
- continue
+ return None
- if self._conf.return_values(option) == self.default[option]:
- del self.options[option]
- continue
+ if 'encapsulation' not in tunnel:
+ raise ConfigError('Must configure the tunnel encapsulation for '\
+ '{ifname}!'.format(**tunnel))
- def as_dict(self, lpath):
- l = self._conf.get_level()
- self._conf.set_level([])
- d = self._conf.get_config_dict(lpath)
- # XXX: that not what I would have expected from get_config_dict
- if lpath:
- d = d[lpath[-1]]
- # XXX: it should have provided me the content and not the key
- self._conf.set_level(l)
- return d
+ verify_mtu_ipv6(tunnel)
+ verify_address(tunnel)
+ verify_vrf(tunnel)
- def to_api(self):
- """
- provide a dictionary with the generated data for the configuration
- options: the configuration value for the key
- changes: per key how they changed from the previous configuration
- actions: per changes all the options which were changed
- """
- # as we have to use a dict() for the API for verify and apply the options
- return {
- 'options': self.options,
- 'changes': self.changes,
- 'actions': self.actions,
- }
+ if 'local_ip' not in tunnel and 'dhcp_interface' not in tunnel:
+ raise ConfigError('local-ip is mandatory for tunnel')
+ if 'remote_ip' not in tunnel and tunnel['encapsulation'] != 'gre':
+ raise ConfigError('remote-ip is mandatory for tunnel')
-default_config_data = {
- # interface definition
- 'vrf': '',
- 'addresses-add': [],
- 'addresses-del': [],
- 'state': 'up',
- 'dhcp-interface': '',
- 'link_detect': 1,
- 'ip': False,
- 'ipv6': False,
- 'nhrp': [],
- 'arp_filter': 1,
- 'arp_accept': 0,
- 'arp_announce': 0,
- 'arp_ignore': 0,
- 'ipv6_accept_ra': 1,
- 'ipv6_autoconf': 0,
- 'ipv6_forwarding': 1,
- 'ipv6_dad_transmits': 1,
- # internal
- 'interfaces': [],
- 'tunnel': {},
- 'bridge': '',
- # the following names are exactly matching the name
- # for the ip command and must not be changed
- 'ifname': '',
- 'type': '',
- 'alias': '',
- 'mtu': '1476',
- 'local': '',
- 'remote': '',
- 'dev': '',
- 'multicast': 'disable',
- 'allmulticast': 'disable',
- 'ttl': '255',
- 'tos': 'inherit',
- 'key': '',
- 'encaplimit': '4',
- 'flowlabel': 'inherit',
- 'hoplimit': '64',
- 'tclass': 'inherit',
- '6rd-prefix': '',
- '6rd-relay-prefix': '',
-}
+ if {'local_ip', 'dhcp_interface'} <= set(tunnel):
+ raise ConfigError('Can not use both local-ip and dhcp-interface')
+ if tunnel['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre']:
+ error_ipv6 = 'Encapsulation mode requires IPv6'
+ if 'local_ip' in tunnel and not is_ipv6(tunnel['local_ip']):
+ raise ConfigError(f'{error_ipv6} local-ip')
-# dict name -> config name, multiple values, default
-mapping = {
- 'type': ('encapsulation', False, None),
- 'alias': ('description', False, None),
- 'mtu': ('mtu', False, None),
- 'local': ('local-ip', False, None),
- 'remote': ('remote-ip', False, None),
- 'multicast': ('multicast', False, None),
- 'dev': ('source-interface', False, None),
- 'ttl': ('parameters ip ttl', False, None),
- 'tos': ('parameters ip tos', False, None),
- 'key': ('parameters ip key', False, None),
- 'encaplimit': ('parameters ipv6 encaplimit', False, None),
- 'flowlabel': ('parameters ipv6 flowlabel', False, None),
- 'hoplimit': ('parameters ipv6 hoplimit', False, None),
- 'tclass': ('parameters ipv6 tclass', False, None),
- '6rd-prefix': ('6rd-prefix', False, None),
- '6rd-relay-prefix': ('6rd-relay-prefix', False, None),
- 'dhcp-interface': ('dhcp-interface', False, None),
- 'state': ('disable', False, 'down'),
- 'link_detect': ('disable-link-detect', False, 2),
- 'vrf': ('vrf', False, None),
- 'addresses': ('address', True, None),
- 'arp_filter': ('ip disable-arp-filter', False, 0),
- 'arp_accept': ('ip enable-arp-accept', False, 1),
- 'arp_announce': ('ip enable-arp-announce', False, 1),
- 'arp_ignore': ('ip enable-arp-ignore', False, 1),
- 'ipv6_autoconf': ('ipv6 address autoconf', False, 1),
- 'ipv6_forwarding': ('ipv6 disable-forwarding', False, 0),
- 'ipv6_dad_transmits:': ('ipv6 dup-addr-detect-transmits', False, None)
-}
+ if 'remote_ip' in tunnel and not is_ipv6(tunnel['remote_ip']):
+ raise ConfigError(f'{error_ipv6} remote-ip')
+ else:
+ error_ipv4 = 'Encapsulation mode requires IPv4'
+ if 'local_ip' in tunnel and not is_ipv4(tunnel['local_ip']):
+ raise ConfigError(f'{error_ipv4} local-ip')
+
+ if 'remote_ip' in tunnel and not is_ipv4(tunnel['remote_ip']):
+ raise ConfigError(f'{error_ipv4} remote-ip')
+
+ if tunnel['encapsulation'] in ['sit', 'gre-bridge']:
+ if 'source_interface' in tunnel:
+ raise ConfigError('Option source-interface can not be used with ' \
+ 'encapsulation "sit" or "gre-bridge"')
+ elif tunnel['encapsulation'] == 'gre':
+ if 'local_ip' in tunnel and is_ipv6(tunnel['local_ip']):
+ raise ConfigError('Can not use local IPv6 address is for mGRE tunnels')
+
+def generate(tunnel):
+ return None
+def apply(tunnel):
+ if 'deleted' in tunnel or 'encapsulation_changed' in tunnel:
+ if tunnel['ifname'] in interfaces():
+ tmp = Interface(tunnel['ifname'])
+ tmp.remove()
+ if 'deleted' in tunnel:
+ return None
-def get_class (options):
dispatch = {
'gre': GREIf,
'gre-bridge': GRETapIf,
@@ -373,353 +143,52 @@ def get_class (options):
'sit': SitIf,
}
- kls = dispatch[options['type']]
- if options['type'] == 'gre' and not options['remote'] \
- and not options['key'] and not options['multicast']:
- # will use GreTapIf on GreIf deletion but it does not matter
- return GRETapIf
- elif options['type'] == 'sit' and options['6rd-prefix']:
- # will use SitIf on Sit6RDIf deletion but it does not matter
- return Sit6RDIf
- return kls
-
-def get_interface_ip (ifname):
- if not ifname:
- return ''
- try:
- addrs = Interface(ifname).get_addr()
- if addrs:
- return addrs[0].split('/')[0]
- except Exception:
- return ''
-
-def get_afi (ip):
- return IP6 if is_ipv6(ip) else IP4
-
-def ip_proto (afi):
- return 6 if afi == IP6 else 4
-
-
-def get_config(config=None):
- ifname = os.environ.get('VYOS_TAGNODE_VALUE','')
- if not ifname:
- raise ConfigError('Interface not specified')
-
- if config:
- config = config
- else:
- config = Config()
-
- conf = ConfigurationState(config, ['interfaces', 'tunnel ', ifname], default_config_data)
- options = conf.options
- changes = conf.changes
- options['ifname'] = ifname
-
- if changes['section'] == 'delete':
- conf.get_effective('type', mapping['type'][0])
- config.set_level(['protocols', 'nhrp', 'tunnel'])
- options['nhrp'] = config.list_nodes('')
- return conf.to_api()
-
- # load all the configuration option according to the mapping
- conf.load(mapping)
-
- # remove default value if not set and not required
- afi_local = get_afi(options['local'])
- if afi_local == IP6:
- conf.remove_default('ttl', 'tos', 'key')
- if afi_local == IP4:
- conf.remove_default('encaplimit', 'flowlabel', 'hoplimit', 'tclass')
-
- # if the local-ip is not set, pick one from the interface !
- # hopefully there is only one, otherwise it will not be very deterministic
- # at time of writing the code currently returns ipv4 before ipv6 in the list
-
- # XXX: There is no way to trigger an update of the interface source IP if
- # XXX: the underlying interface IP address does change, I believe this
- # XXX: limit/issue is present in vyatta too
-
- if not options['local'] and options['dhcp-interface']:
- # XXX: This behaviour changes from vyatta which would return 127.0.0.1 if
- # XXX: the interface was not DHCP. As there is no easy way to find if an
- # XXX: interface is using DHCP, and using this feature to get 127.0.0.1
- # XXX: makes little sense, I feel the change in behaviour is acceptable
- picked = get_interface_ip(options['dhcp-interface'])
- if picked == '':
- picked = '127.0.0.1'
- print('Could not get an IP address from {dhcp-interface} using 127.0.0.1 instead')
- options['local'] = picked
- options['dhcp-interface'] = ''
-
- # to make IPv6 SLAAC and DHCPv6 work with forwarding=1,
- # accept_ra must be 2
- if options['ipv6_autoconf'] or 'dhcpv6' in options['addresses-add']:
- options['ipv6_accept_ra'] = 2
-
- # allmulticast fate is linked to multicast
- options['allmulticast'] = options['multicast']
-
- # check that per encapsulation all local-remote pairs are unique
- ct = conf.as_dict(['interfaces', 'tunnel'])
- options['tunnel'] = {}
-
- # check for bridges
- tmp = is_member(config, ifname, 'bridge')
- if tmp: options['bridge'] = next(iter(tmp))
- options['interfaces'] = interfaces()
-
- for name in ct:
- tunnel = ct[name]
- encap = tunnel.get('encapsulation', '')
- local = tunnel.get('local-ip', '')
- if not local:
- local = get_interface_ip(tunnel.get('dhcp-interface', ''))
- remote = tunnel.get('remote-ip', '<unset>')
- pair = f'{local}-{remote}'
- options['tunnel'][encap][pair] = options['tunnel'].setdefault(encap, {}).get(pair, 0) + 1
-
- return conf.to_api()
-
-
-def verify(conf):
- options = conf['options']
- changes = conf['changes']
- actions = conf['actions']
-
- ifname = options['ifname']
- iftype = options['type']
-
- if changes['section'] == 'delete':
- if ifname in options['nhrp']:
- raise ConfigError((
- f'Cannot delete interface tunnel {iftype} {ifname}, '
- 'it is used by NHRP'))
-
- if options['bridge']:
- raise ConfigError((
- f'Cannot delete interface "{options["ifname"]}" as it is a '
- f'member of bridge "{options["bridge"]}"!'))
-
- # done, bail out early
- return None
-
- # tunnel encapsulation checks
-
- if not iftype:
- raise ConfigError(f'Must provide an "encapsulation" for tunnel {iftype} {ifname}')
-
- if changes['type'] in ('modify', 'delete'):
- # TODO: we could now deal with encapsulation modification by deleting / recreating
- raise ConfigError(f'Encapsulation can only be set at tunnel creation for tunnel {iftype} {ifname}')
-
- if iftype != 'sit' and options['6rd-prefix']:
- # XXX: should be able to remove this and let the definition catch it
- print(f'6RD can only be configured for sit interfaces not tunnel {iftype} {ifname}')
-
- # what are the tunnel options we can set / modified / deleted
-
- kls = get_class(options)
- valid = kls.updates + ['alias', 'addresses-add', 'addresses-del', 'vrf', 'state']
- valid += ['arp_filter', 'arp_accept', 'arp_announce', 'arp_ignore']
- valid += ['ipv6_accept_ra', 'ipv6_autoconf', 'ipv6_forwarding', 'ipv6_dad_transmits']
-
- if changes['section'] == 'create':
- valid.extend(['type',])
- valid.extend([o for o in kls.options if o not in kls.updates])
-
- for create in actions['create']:
- if create not in valid:
- raise ConfigError(f'Can not set "{create}" for tunnel {iftype} {ifname} at tunnel creation')
-
- for modify in actions['modify']:
- if modify not in valid:
- raise ConfigError(f'Can not modify "{modify}" for tunnel {iftype} {ifname}. it must be set at tunnel creation')
-
- for delete in actions['delete']:
- if delete in kls.required:
- raise ConfigError(f'Can not remove "{delete}", it is an mandatory option for tunnel {iftype} {ifname}')
-
- # tunnel information
-
- tun_local = options['local']
- afi_local = get_afi(tun_local)
- tun_remote = options['remote'] or tun_local
- afi_remote = get_afi(tun_remote)
- tun_ismgre = iftype == 'gre' and not options['remote']
- tun_is6rd = iftype == 'sit' and options['6rd-prefix']
- tun_dev = options['dev']
-
- # incompatible options
-
- if not tun_local and not options['dhcp-interface'] and not tun_is6rd:
- raise ConfigError(f'Must configure either local-ip or dhcp-interface for tunnel {iftype} {ifname}')
-
- if tun_local and options['dhcp-interface']:
- raise ConfigError(f'Must configure only one of local-ip or dhcp-interface for tunnel {iftype} {ifname}')
-
- if tun_dev and iftype in ('gre-bridge', 'sit'):
- raise ConfigError(f'source interface can not be used with {iftype} {ifname}')
-
- # tunnel endpoint
-
- if afi_local != afi_remote:
- raise ConfigError(f'IPv4/IPv6 mismatch between local-ip and remote-ip for tunnel {iftype} {ifname}')
-
- if afi_local != kls.tunnel:
- version = 4 if tun_local == IP4 else 6
- raise ConfigError(f'Invalid IPv{version} local-ip for tunnel {iftype} {ifname}')
-
- ipv4_count = len([ip for ip in options['addresses-add'] if is_ipv4(ip)])
- ipv6_count = len([ip for ip in options['addresses-add'] if is_ipv6(ip)])
-
- if tun_ismgre and afi_local == IP6:
- raise ConfigError(f'Using an IPv6 address is forbidden for mGRE tunnels such as tunnel {iftype} {ifname}')
-
- # check address family use
- # checks are not enforced (but ip command failing) for backward compatibility
-
- if ipv4_count and not IP4 in kls.ip:
- print(f'Should not use IPv4 addresses on tunnel {iftype} {ifname}')
-
- if ipv6_count and not IP6 in kls.ip:
- print(f'Should not use IPv6 addresses on tunnel {iftype} {ifname}')
-
- # vrf check
- if options['vrf']:
- if options['vrf'] not in options['interfaces']:
- raise ConfigError(f'VRF "{options["vrf"]}" does not exist')
-
- if options['bridge']:
- raise ConfigError((
- f'Interface "{options["ifname"]}" cannot be member of VRF '
- f'"{options["vrf"]}" and bridge {options["bridge"]} '
- f'at the same time!'))
-
- # bridge and address check
- if ( options['bridge']
- and ( options['addresses-add']
- or options['ipv6_autoconf'] ) ):
- raise ConfigError((
- f'Cannot assign address to interface "{options["name"]}" '
- f'as it is a member of bridge "{options["bridge"]}"!'))
-
- # source-interface check
-
- if tun_dev and tun_dev not in options['interfaces']:
- raise ConfigError(f'device "{tun_dev}" does not exist')
-
- # tunnel encapsulation check
-
- convert = {
- (6, 4, 'gre'): 'ip6gre',
- (6, 6, 'gre'): 'ip6gre',
- (4, 6, 'ipip'): 'ipip6',
- (6, 6, 'ipip'): 'ip6ip6',
+ # We need to re-map the tunnel encapsulation proto to a valid interface class
+ encap = tunnel['encapsulation']
+ klass = dispatch[encap]
+
+ # This is a special type of interface which needs additional parameters
+ # when created using iproute2. Instead of passing a ton of arguments,
+ # use a dictionary provided by the interface class which holds all the
+ # options necessary.
+ conf = klass.get_config()
+
+ # Copy/re-assign our dictionary values to values understood by the
+ # derived _Tunnel classes
+ mapping = {
+ # this : get_config()
+ 'local_ip' : 'local',
+ 'remote_ip' : 'remote',
+ 'source_interface' : 'dev',
+ 'parameters.ip.ttl' : 'ttl',
+ 'parameters.ip.tos' : 'tos',
+ 'parameters.ip.key' : 'key',
+ 'parameters.ipv6.encaplimit' : 'encaplimit'
}
- iprotos = []
- if ipv4_count:
- iprotos.append(4)
- if ipv6_count:
- iprotos.append(6)
-
- for iproto in iprotos:
- replace = convert.get((kls.tunnel, iproto, iftype), '')
- if replace:
- raise ConfigError(
- f'Using IPv6 address in local-ip or remote-ip is not possible with "encapsulation {iftype}". ' +
- f'Use "encapsulation {replace}" for tunnel {iftype} {ifname} instead.'
- )
-
- # tunnel options
-
- incompatible = []
- if afi_local == IP6:
- incompatible.extend(['ttl', 'tos', 'key',])
- if afi_local == IP4:
- incompatible.extend(['encaplimit', 'flowlabel', 'hoplimit', 'tclass'])
-
- for option in incompatible:
- if option in options:
- # TODO: raise converted to print as not enforced by vyatta
- # raise ConfigError(f'{option} is not valid for tunnel {iftype} {ifname}')
- print(f'Using "{option}" is invalid for tunnel {iftype} {ifname}')
-
- # duplicate tunnel pairs
-
- pair = '{}-{}'.format(options['local'], options['remote'])
- if options['tunnel'].get(iftype, {}).get(pair, 0) > 1:
- raise ConfigError(f'More than one tunnel configured for with the same encapulation and IPs for tunnel {iftype} {ifname}')
+ # Add additional IPv6 options if tunnel is IPv6 aware
+ if tunnel['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre']:
+ mappingv6 = {
+ # this : get_config()
+ 'parameters.ipv6.encaplimit' : 'encaplimit'
+ }
+ mapping.update(mappingv6)
- return None
+ for our_key, their_key in mapping.items():
+ if dict_search(our_key, tunnel) and their_key in conf:
+ conf[their_key] = dict_search(our_key, tunnel)
+ tun = klass(tunnel['ifname'], **conf)
+ tun.change_options()
+ tun.update(tunnel)
-def generate(gre):
return None
-def apply(conf):
- options = conf['options']
- changes = conf['changes']
- actions = conf['actions']
- kls = get_class(options)
-
- # extract ifname as otherwise it is duplicated on the interface creation
- ifname = options.pop('ifname')
-
- # only the valid keys for creation of a Interface
- config = dict((k, options[k]) for k in kls.options if options[k])
-
- # setup or create the tunnel interface if it does not exist
- tunnel = kls(ifname, **config)
-
- if changes['section'] == 'delete':
- tunnel.remove()
- # The perl code was calling/opt/vyatta/sbin/vyatta-tunnel-cleanup
- # which identified tunnels type which were not used anymore to remove them
- # (ie: gre0, gretap0, etc.) The perl code did however nothing
- # This feature is also not implemented yet
- return
-
- # A GRE interface without remote will be mGRE
- # if the interface does not suppor the option, it skips the change
- for option in tunnel.updates:
- if changes['section'] in 'create' and option in tunnel.options:
- # it was setup at creation
- continue
- if not options[option]:
- # remote can be set to '' and it would generate an invalide command
- continue
- tunnel.set_interface(option, options[option])
-
- # set other interface properties
- for option in ('alias', 'mtu', 'link_detect', 'multicast', 'allmulticast',
- 'arp_accept', 'arp_filter', 'arp_announce', 'arp_ignore',
- 'ipv6_accept_ra', 'ipv6_autoconf', 'ipv6_forwarding', 'ipv6_dad_transmits'):
- if not options[option]:
- # should never happen but better safe
- continue
- tunnel.set_interface(option, options[option])
-
- # assign/remove VRF (ONLY when not a member of a bridge,
- # otherwise 'nomaster' removes it from it)
- if not options['bridge']:
- tunnel.set_vrf(options['vrf'])
-
- # Configure interface address(es)
- for addr in options['addresses-del']:
- tunnel.del_addr(addr)
- for addr in options['addresses-add']:
- tunnel.add_addr(addr)
-
- # now bring it up (or not)
- tunnel.set_admin_state(options['state'])
-
-
if __name__ == '__main__':
try:
c = get_config()
- verify(c)
generate(c)
+ verify(c)
apply(c)
except ConfigError as e:
print(e)
diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py
index 9bda35d0a..7cfc76aa0 100755
--- a/src/conf_mode/interfaces-wireguard.py
+++ b/src/conf_mode/interfaces-wireguard.py
@@ -80,9 +80,6 @@ def verify(wireguard):
raise ConfigError('Wireguard private-key not found! Execute: ' \
'"run generate wireguard [default-keypair|named-keypairs]"')
- if 'address' not in wireguard:
- raise ConfigError('IP address required!')
-
if 'peer' not in wireguard:
raise ConfigError('At least one Wireguard peer is required!')
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index 5d723bbfd..b25fcd4e0 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -241,10 +241,12 @@ def generate(wifi):
# render appropriate new config files depending on access-point or station mode
if wifi['type'] == 'access-point':
- render(hostapd_conf.format(**wifi), 'wifi/hostapd.conf.tmpl', wifi, trim_blocks=True)
+ render(hostapd_conf.format(**wifi), 'wifi/hostapd.conf.tmpl',
+ wifi)
elif wifi['type'] == 'station':
- render(wpa_suppl_conf.format(**wifi), 'wifi/wpa_supplicant.conf.tmpl', wifi, trim_blocks=True)
+ render(wpa_suppl_conf.format(**wifi), 'wifi/wpa_supplicant.conf.tmpl',
+ wifi)
return None
@@ -261,7 +263,6 @@ def apply(wifi):
# Assign WiFi instance configuration parameters to config dict
conf['phy'] = wifi['physical_device']
- conf['wds'] = 'on' if 'wds' in wifi else 'off'
# Finally create the new interface
w = WiFiIf(interface, **conf)
diff --git a/src/conf_mode/interfaces-wirelessmodem.py b/src/conf_mode/interfaces-wirelessmodem.py
index bce3405d0..976953b31 100755
--- a/src/conf_mode/interfaces-wirelessmodem.py
+++ b/src/conf_mode/interfaces-wirelessmodem.py
@@ -91,21 +91,21 @@ def generate(wwan):
wwan['device'] = find_device_file(wwan['device'])
# Create PPP configuration files
- render(config_wwan, 'wwan/peer.tmpl', wwan, trim_blocks=True)
+ render(config_wwan, 'wwan/peer.tmpl', wwan)
# Create PPP chat script
- render(config_wwan_chat, 'wwan/chat.tmpl', wwan, trim_blocks=True)
+ render(config_wwan_chat, 'wwan/chat.tmpl', wwan)
# generated script file must be executable
# Create script for ip-pre-up.d
render(script_wwan_pre_up, 'wwan/ip-pre-up.script.tmpl',
- wwan, trim_blocks=True, permission=0o755)
+ wwan, permission=0o755)
# Create script for ip-up.d
render(script_wwan_ip_up, 'wwan/ip-up.script.tmpl',
- wwan, trim_blocks=True, permission=0o755)
+ wwan, permission=0o755)
# Create script for ip-down.d
render(script_wwan_ip_down, 'wwan/ip-down.script.tmpl',
- wwan, trim_blocks=True, permission=0o755)
+ wwan, permission=0o755)
return None
diff --git a/src/conf_mode/ipsec-settings.py b/src/conf_mode/ipsec-settings.py
index 11a5b7aaa..a65e8b567 100755
--- a/src/conf_mode/ipsec-settings.py
+++ b/src/conf_mode/ipsec-settings.py
@@ -170,12 +170,12 @@ def verify(data):
raise ConfigError("L2TP VPN configuration error: \"vpn ipsec ipsec-interfaces\" must be specified.")
def generate(data):
- render(charon_conf_file, 'ipsec/charon.tmpl', data, trim_blocks=True)
+ render(charon_conf_file, 'ipsec/charon.tmpl', data)
if data["ipsec_l2tp"]:
remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_secrets_file)
# old_umask = os.umask(0o077)
- # render(ipsec_secrets_file, 'ipsec/ipsec.secrets.tmpl', data, trim_blocks=True)
+ # render(ipsec_secrets_file, 'ipsec/ipsec.secrets.tmpl', data)
# os.umask(old_umask)
## Use this method while IPSec CLI handler won't be overwritten to python
write_ipsec_secrets(data)
@@ -186,12 +186,12 @@ def generate(data):
if not os.path.exists(ipsec_ra_conn_dir):
os.makedirs(ipsec_ra_conn_dir)
- render(ipsec_ra_conn_file, 'ipsec/remote-access.tmpl', data, trim_blocks=True)
+ render(ipsec_ra_conn_file, 'ipsec/remote-access.tmpl', data)
os.umask(old_umask)
remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_file)
# old_umask = os.umask(0o077)
- # render(ipsec_conf_file, 'ipsec/ipsec.conf.tmpl', data, trim_blocks=True)
+ # render(ipsec_conf_file, 'ipsec/ipsec.conf.tmpl', data)
# os.umask(old_umask)
## Use this method while IPSec CLI handler won't be overwritten to python
write_ipsec_conf(data)
diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py
index b66cd370a..f5c023b81 100755
--- a/src/conf_mode/nat.py
+++ b/src/conf_mode/nat.py
@@ -18,18 +18,19 @@ import jmespath
import json
import os
-from copy import deepcopy
from distutils.version import LooseVersion
from platform import release as kernel_version
from sys import exit
from netifaces import interfaces
from vyos.config import Config
+from vyos.configdict import dict_merge
from vyos.template import render
-from vyos.util import call
from vyos.util import cmd
from vyos.util import check_kmod
+from vyos.util import dict_search
from vyos.validate import is_addr_assigned
+from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
@@ -40,17 +41,6 @@ if LooseVersion(kernel_version()) > LooseVersion('5.1'):
else:
k_mod = ['nft_nat', 'nft_chain_nat_ipv4']
-default_config_data = {
- 'deleted': False,
- 'destination': [],
- 'helper_functions': None,
- 'pre_ct_helper': '',
- 'pre_ct_conntrack': '',
- 'out_ct_helper': '',
- 'out_ct_conntrack': '',
- 'source': []
-}
-
iptables_nat_config = '/tmp/vyos-nat-rules.nft'
def get_handler(json, chain, target):
@@ -66,114 +56,43 @@ def get_handler(json, chain, target):
return None
-def verify_rule(rule, err_msg):
+def verify_rule(config, err_msg):
""" Common verify steps used for both source and destination NAT """
- if rule['translation_port'] or rule['dest_port'] or rule['source_port']:
- if rule['protocol'] not in ['tcp', 'udp', 'tcp_udp']:
- proto = rule['protocol']
- raise ConfigError(f'{err_msg} ports can only be specified when protocol is "tcp", "udp" or "tcp_udp" (currently "{proto}")')
- if '/' in rule['translation_address']:
+ if (dict_search('translation.port', config) != None or
+ dict_search('destination.port', config) != None or
+ dict_search('source.port', config)):
+
+ if config['protocol'] not in ['tcp', 'udp', 'tcp_udp']:
+ raise ConfigError(f'{err_msg}\n' \
+ 'ports can only be specified when protocol is '\
+ 'either tcp, udp or tcp_udp!')
+
+ if '/' in (dict_search('translation.address', config) or []):
raise ConfigError(f'{err_msg}\n' \
'Cannot use ports with an IPv4net type translation address as it\n' \
'statically maps a whole network of addresses onto another\n' \
'network of addresses')
-
-def parse_configuration(conf, source_dest):
- """ Common wrapper to read in both NAT source and destination CLI """
- ruleset = []
- base_level = ['nat', source_dest]
- conf.set_level(base_level)
- for number in conf.list_nodes(['rule']):
- rule = {
- 'description': '',
- 'dest_address': '',
- 'dest_port': '',
- 'disabled': False,
- 'exclude': False,
- 'interface_in': '',
- 'interface_out': '',
- 'log': False,
- 'protocol': 'all',
- 'number': number,
- 'source_address': '',
- 'source_prefix': '',
- 'source_port': '',
- 'translation_address': '',
- 'translation_prefix': '',
- 'translation_port': ''
- }
- conf.set_level(base_level + ['rule', number])
-
- if conf.exists(['description']):
- rule['description'] = conf.return_value(['description'])
-
- if conf.exists(['destination', 'address']):
- tmp = conf.return_value(['destination', 'address'])
- if tmp.startswith('!'):
- tmp = tmp.replace('!', '!=')
- rule['dest_address'] = tmp
-
- if conf.exists(['destination', 'port']):
- tmp = conf.return_value(['destination', 'port'])
- if tmp.startswith('!'):
- tmp = tmp.replace('!', '!=')
- rule['dest_port'] = tmp
-
- if conf.exists(['disable']):
- rule['disabled'] = True
-
- if conf.exists(['exclude']):
- rule['exclude'] = True
-
- if conf.exists(['inbound-interface']):
- rule['interface_in'] = conf.return_value(['inbound-interface'])
-
- if conf.exists(['outbound-interface']):
- rule['interface_out'] = conf.return_value(['outbound-interface'])
-
- if conf.exists(['log']):
- rule['log'] = True
-
- if conf.exists(['protocol']):
- rule['protocol'] = conf.return_value(['protocol'])
-
- if conf.exists(['source', 'address']):
- tmp = conf.return_value(['source', 'address'])
- if tmp.startswith('!'):
- tmp = tmp.replace('!', '!=')
- rule['source_address'] = tmp
-
- if conf.exists(['source', 'prefix']):
- rule['source_prefix'] = conf.return_value(['source', 'prefix'])
-
- if conf.exists(['source', 'port']):
- tmp = conf.return_value(['source', 'port'])
- if tmp.startswith('!'):
- tmp = tmp.replace('!', '!=')
- rule['source_port'] = tmp
-
- if conf.exists(['translation', 'address']):
- rule['translation_address'] = conf.return_value(['translation', 'address'])
-
- if conf.exists(['translation', 'prefix']):
- rule['translation_prefix'] = conf.return_value(['translation', 'prefix'])
-
- if conf.exists(['translation', 'port']):
- rule['translation_port'] = conf.return_value(['translation', 'port'])
-
- ruleset.append(rule)
-
- return ruleset
-
def get_config(config=None):
- nat = deepcopy(default_config_data)
if config:
conf = config
else:
conf = Config()
+ base = ['nat']
+ nat = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+
+ # T2665: we must add the tagNode defaults individually until this is
+ # moved to the base class
+ for direction in ['source', 'destination']:
+ if direction in nat:
+ default_values = defaults(base + [direction, 'rule'])
+ for rule in nat[direction]['rule']:
+ nat[direction]['rule'][rule] = dict_merge(default_values,
+ nat[direction]['rule'][rule])
+
+
# read in current nftable (once) for further processing
tmp = cmd('nft -j list table raw')
nftable_json = json.loads(tmp)
@@ -182,7 +101,7 @@ def get_config(config=None):
pattern = 'nftables[?rule].rule[?expr[].jump].{chain: chain, handle: handle, target: expr[].jump.target | [0]}'
condensed_json = jmespath.search(pattern, nftable_json)
- if not conf.exists(['nat']):
+ if not conf.exists(base):
nat['helper_functions'] = 'remove'
# Retrieve current table handler positions
@@ -190,9 +109,7 @@ def get_config(config=None):
nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK')
nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYATTA_CT_HELPER')
nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'NAT_CONNTRACK')
-
- nat['deleted'] = True
-
+ nat['deleted'] = ''
return nat
# check if NAT connection tracking helpers need to be set up - this has to
@@ -206,19 +123,10 @@ def get_config(config=None):
nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYATTA_CT_IGNORE')
nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'VYATTA_CT_OUTPUT_HOOK')
- # set config level for parsing in NAT configuration
- conf.set_level(['nat'])
-
- # use a common wrapper function to read in the source / destination
- # tree from the config - thus we do not need to replicate almost the
- # same code :-)
- for tgt in ['source', 'destination', 'nptv6']:
- nat[tgt] = parse_configuration(conf, tgt)
-
return nat
def verify(nat):
- if nat['deleted']:
+ if not nat or 'deleted' in nat:
# no need to verify the CLI as NAT is going to be deactivated
return None
@@ -226,49 +134,55 @@ def verify(nat):
if not (nat['pre_ct_ignore'] or nat['pre_ct_conntrack'] or nat['out_ct_ignore'] or nat['out_ct_conntrack']):
raise Exception('could not determine nftable ruleset handlers')
- for rule in nat['source']:
- interface = rule['interface_out']
- err_msg = f'Source NAT configuration error in rule "{rule["number"]}":'
-
- if interface and interface not in 'any' and interface not in interfaces():
- print(f'Warning: rule "{rule["number"]}" interface "{interface}" does not exist on this system')
+ if dict_search('source.rule', nat):
+ for rule, config in dict_search('source.rule', nat).items():
+ err_msg = f'Source NAT configuration error in rule {rule}:'
+ if 'outbound_interface' not in config:
+ raise ConfigError(f'{err_msg}\n' \
+ 'outbound-interface not specified')
+ else:
+ if config['outbound_interface'] not in 'any' and config['outbound_interface'] not in interfaces():
+ print(f'WARNING: rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system')
- if not rule['interface_out']:
- raise ConfigError(f'{err_msg} outbound-interface not specified')
- if rule['translation_address']:
- addr = rule['translation_address']
- if addr != 'masquerade':
- for ip in addr.split('-'):
- if not is_addr_assigned(ip):
- print(f'Warning: IP address {ip} does not exist on the system!')
+ addr = dict_search('translation.address', config)
+ if addr != None:
+ if addr != 'masquerade':
+ for ip in addr.split('-'):
+ if not is_addr_assigned(ip):
+ print(f'WARNING: IP address {ip} does not exist on the system!')
+ elif 'exclude' not in config:
+ raise ConfigError(f'{err_msg}\n' \
+ 'translation address not specified')
- elif not rule['exclude']:
- raise ConfigError(f'{err_msg} translation address not specified')
+ # common rule verification
+ verify_rule(config, err_msg)
- # common rule verification
- verify_rule(rule, err_msg)
- for rule in nat['destination']:
- interface = rule['interface_in']
- err_msg = f'Destination NAT configuration error in rule "{rule["number"]}":'
+ if dict_search('destination.rule', nat):
+ for rule, config in dict_search('destination.rule', nat).items():
+ err_msg = f'Destination NAT configuration error in rule {rule}:'
- if interface and interface not in 'any' and interface not in interfaces():
- print(f'Warning: rule "{rule["number"]}" interface "{interface}" does not exist on this system')
+ if 'inbound_interface' not in config:
+ raise ConfigError(f'{err_msg}\n' \
+ 'inbound-interface not specified')
+ else:
+ if config['inbound_interface'] not in 'any' and config['inbound_interface'] not in interfaces():
+ print(f'WARNING: rule "{rule}" interface "{config["inbound_interface"]}" does not exist on this system')
- if not rule['interface_in']:
- raise ConfigError(f'{err_msg} inbound-interface not specified')
- if not rule['translation_address'] and not rule['exclude']:
- raise ConfigError(f'{err_msg} translation address not specified')
+ if dict_search('translation.address', config) == None and 'exclude' not in config:
+ raise ConfigError(f'{err_msg}\n' \
+ 'translation address not specified')
- # common rule verification
- verify_rule(rule, err_msg)
+ # common rule verification
+ verify_rule(config, err_msg)
return None
def generate(nat):
- render(iptables_nat_config, 'firewall/nftables-nat.tmpl', nat, trim_blocks=True, permission=0o755)
+ render(iptables_nat_config, 'firewall/nftables-nat.tmpl', nat,
+ permission=0o755)
return None
def apply(nat):
diff --git a/src/conf_mode/ntp.py b/src/conf_mode/ntp.py
index d6453ec83..b102b3e9e 100755
--- a/src/conf_mode/ntp.py
+++ b/src/conf_mode/ntp.py
@@ -53,8 +53,8 @@ def generate(ntp):
if not ntp:
return None
- render(config_file, 'ntp/ntp.conf.tmpl', ntp, trim_blocks=True)
- render(systemd_override, 'ntp/override.conf.tmpl', ntp, trim_blocks=True)
+ render(config_file, 'ntp/ntp.conf.tmpl', ntp)
+ render(systemd_override, 'ntp/override.conf.tmpl', ntp)
return None
diff --git a/src/conf_mode/policy-local-route.py b/src/conf_mode/policy-local-route.py
new file mode 100755
index 000000000..c4024dce4
--- /dev/null
+++ b/src/conf_mode/policy-local-route.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+
+from sys import exit
+
+from vyos.config import Config
+from vyos.configdict import dict_merge
+from vyos.configdict import node_changed
+from vyos.configdict import leaf_node_changed
+from vyos.template import render
+from vyos.util import call
+from vyos import ConfigError
+from vyos import airbag
+airbag.enable()
+
+
+def get_config(config=None):
+
+ if config:
+ conf = config
+ else:
+ conf = Config()
+ base = ['policy', 'local-route']
+ pbr = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+
+ # delete policy local-route
+ dict = {}
+ tmp = node_changed(conf, ['policy', 'local-route', 'rule'])
+ if tmp:
+ for rule in (tmp or []):
+ src = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'source'])
+ if src:
+ dict = dict_merge({'rule_remove' : {rule : {'source' : src}}}, dict)
+ pbr.update(dict)
+
+ # delete policy local-route rule x source x.x.x.x
+ if 'rule' in pbr:
+ for rule in pbr['rule']:
+ src = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'source'])
+ if src:
+ dict = dict_merge({'rule_remove' : {rule : {'source' : src}}}, dict)
+ pbr.update(dict)
+
+ return pbr
+
+def verify(pbr):
+ # bail out early - looks like removal from running config
+ if not pbr:
+ return None
+
+ if 'rule' in pbr:
+ for rule in pbr['rule']:
+ if 'source' not in pbr['rule'][rule]:
+ raise ConfigError('Source address required!')
+ else:
+ if 'set' not in pbr['rule'][rule] or 'table' not in pbr['rule'][rule]['set']:
+ raise ConfigError('Table set is required!')
+
+ return None
+
+def generate(pbr):
+ if not pbr:
+ return None
+
+ return None
+
+def apply(pbr):
+ if not pbr:
+ return None
+
+ # Delete old rule if needed
+ if 'rule_remove' in pbr:
+ for rule in pbr['rule_remove']:
+ for src in pbr['rule_remove'][rule]['source']:
+ call(f'ip rule del prio {rule} from {src}')
+
+ # Generate new config
+ if 'rule' in pbr:
+ for rule in pbr['rule']:
+ table = pbr['rule'][rule]['set']['table']
+ if pbr['rule'][rule]['source']:
+ for src in pbr['rule'][rule]['source']:
+ call(f'ip rule add prio {rule} from {src} lookup {table}')
+
+ return None
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index 654874232..642738b09 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -14,16 +14,16 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import os
-
from sys import exit
from vyos.config import Config
from vyos.util import call
+from vyos.util import dict_search
from vyos.template import render
from vyos.template import render_to_string
+from vyos import ConfigError
from vyos import frr
-from vyos import ConfigError, airbag
+from vyos import airbag
airbag.enable()
config_file = r'/tmp/bgp.frr'
@@ -31,8 +31,10 @@ config_file = r'/tmp/bgp.frr'
def get_config():
conf = Config()
base = ['protocols', 'nbgp']
- bgp = conf.get_config_dict(base, key_mangling=('-', '_'))
+ bgp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ # XXX: any reason we can not move this into the FRR template?
+ # we shall not call vtysh directly, especially not in get_config()
if not conf.exists(base):
bgp = {}
call('vtysh -c \"conf t\" -c \"no ip protocol bgp\" ')
@@ -40,9 +42,6 @@ def get_config():
if not conf.exists(base + ['route-map']):
call('vtysh -c \"conf t\" -c \"no ip protocol bgp\" ')
- from pprint import pprint
- pprint(bgp)
-
return bgp
def verify(bgp):
@@ -50,9 +49,23 @@ def verify(bgp):
return None
# Check if declared more than one ASN
- for asn in bgp['nbgp'].items():
- if len(bgp['nbgp']) > 1:
- raise ConfigError('Only one bgp ASN process can be definded')
+ if len(bgp) > 1:
+ raise ConfigError('Only one BGP AS can be defined!')
+
+ for asn, asn_config in bgp.items():
+ # Common verification for both peer-group and neighbor statements
+ for neigh in ['neighbor', 'peer_group']:
+ # bail out early if there is no neighbor or peer-group statement
+ # this also saves one indention level
+ if neigh not in asn_config:
+ continue
+
+ for neighbor, config in asn_config[neigh].items():
+ if 'remote_as' not in config and 'peer_group' not in config:
+ raise ConfigError(f'BGP remote-as must be specified for "{neighbor}"!')
+
+ if 'remote_as' in config and 'peer_group' in config:
+ raise ConfigError(f'BGP peer-group member "{neighbor}" cannot override remote-as of peer-group!')
return None
@@ -61,33 +74,40 @@ def generate(bgp):
bgp['new_frr_config'] = ''
return None
- # render(config) not needed, its only for debug
- render(config_file, 'frr/bgp.frr.tmpl', bgp)
+ # only one BGP AS is supported, so we can directly send the first key
+ # of the config dict
+ asn = list(bgp.keys())[0]
+ bgp[asn]['asn'] = asn
- bgp['new_frr_config'] = render_to_string('frr/bgp.frr.tmpl', bgp)
+ # render(config) not needed, its only for debug
+ render(config_file, 'frr/bgp.frr.tmpl', bgp[asn])
+ bgp['new_frr_config'] = render_to_string('frr/bgp.frr.tmpl', bgp[asn])
return None
def apply(bgp):
- # Save original configration prior to starting any commit actions
- bgp['original_config'] = frr.get_configuration(daemon='bgpd')
- bgp['modified_config'] = frr.replace_section(bgp['original_config'], bgp['new_frr_config'], from_re='router bgp .*')
+ # Save original configuration prior to starting any commit actions
+ frr_cfg = {}
+ frr_cfg['original_config'] = frr.get_configuration(daemon='bgpd')
+ frr_cfg['modified_config'] = frr.replace_section(frr_cfg['original_config'], bgp['new_frr_config'], from_re='router bgp .*')
# Debugging
+ print('')
print('--------- DEBUGGING ----------')
- print(f'Existing config:\n{bgp["original_config"]}\n\n')
+ print(f'Existing config:\n{frr_cfg["original_config"]}\n\n')
print(f'Replacement config:\n{bgp["new_frr_config"]}\n\n')
- print(f'Modified config:\n{bgp["modified_config"]}\n\n')
+ print(f'Modified config:\n{frr_cfg["modified_config"]}\n\n')
- # Frr Mark configuration will test for syntax errors and exception out if any syntax errors are detected
- frr.mark_configuration(bgp['modified_config'])
+ # FRR mark configuration will test for syntax errors and throws an
+ # exception if any syntax errors is detected
+ frr.mark_configuration(frr_cfg['modified_config'])
- # Commit the resulting new configuration to frr, this will render an frr.CommitError() Exception on fail
- frr.reload_configuration(bgp['modified_config'], daemon='bgpd')
+ # Commit resulting configuration to FRR, this will throw CommitError
+ # on failure
+ frr.reload_configuration(frr_cfg['modified_config'], daemon='bgpd')
return None
-
if __name__ == '__main__':
try:
c = get_config()
diff --git a/src/conf_mode/protocols_igmp.py b/src/conf_mode/protocols_igmp.py
index 6f4fc784d..8606e7364 100755
--- a/src/conf_mode/protocols_igmp.py
+++ b/src/conf_mode/protocols_igmp.py
@@ -21,8 +21,9 @@ from sys import exit
from vyos import ConfigError
from vyos.config import Config
-from vyos.util import call
+from vyos.util import call, process_named_running
from vyos.template import render
+from signal import SIGTERM
from vyos import airbag
airbag.enable()
@@ -36,12 +37,20 @@ def get_config(config=None):
conf = Config()
igmp_conf = {
'igmp_conf' : False,
+ 'pim_conf' : False,
+ 'igmp_proxy_conf' : False,
'old_ifaces' : {},
'ifaces' : {}
}
if not (conf.exists('protocols igmp') or conf.exists_effective('protocols igmp')):
return None
+ if conf.exists('protocols igmp-proxy'):
+ igmp_conf['igmp_proxy_conf'] = True
+
+ if conf.exists('protocols pim'):
+ igmp_conf['pim_conf'] = True
+
if conf.exists('protocols igmp'):
igmp_conf['igmp_conf'] = True
@@ -79,6 +88,10 @@ def verify(igmp):
return None
if igmp['igmp_conf']:
+ # Check conflict with IGMP-Proxy
+ if igmp['igmp_proxy_conf']:
+ raise ConfigError(f"IGMP proxy and PIM cannot be both configured at the same time")
+
# Check interfaces
if not igmp['ifaces']:
raise ConfigError(f"IGMP require defined interfaces!")
@@ -99,9 +112,16 @@ def apply(igmp):
if igmp is None:
return None
- if os.path.exists(config_file):
- call(f'vtysh -d pimd -f {config_file}')
- os.remove(config_file)
+ pim_pid = process_named_running('pimd')
+ if igmp['igmp_conf'] or igmp['pim_conf']:
+ if not pim_pid:
+ call(f'pimd -d -F traditional --daemon -A 127.0.0.1')
+
+ if os.path.exists(config_file):
+ call(f'vtysh -d pimd -f {config_file}')
+ os.remove(config_file)
+ elif pim_pid:
+ os.kill(int(pim_pid), SIGTERM)
return None
diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py
index b0b8d705b..bd372a7b3 100755
--- a/src/conf_mode/protocols_isis.py
+++ b/src/conf_mode/protocols_isis.py
@@ -107,16 +107,15 @@ def generate(isis):
isis[process]['process'] = process
# render(config) not needed, its only for debug
- render(config_file, 'frr/isis.frr.tmpl', isis[process], trim_blocks=True)
+ render(config_file, 'frr/isis.frr.tmpl', isis[process])
isis['new_frr_config'] = render_to_string('frr/isis.frr.tmpl',
- isis[process], trim_blocks=True)
+ isis[process])
return None
def apply(isis):
-
- # Save original configration prior to starting any commit actions
+ # Save original configuration prior to starting any commit actions
frr_cfg = {}
frr_cfg['original_config'] = frr.get_configuration(daemon='isisd')
frr_cfg['modified_config'] = frr.replace_section(frr_cfg['original_config'], isis['new_frr_config'], from_re='interface .*')
diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py
index 84948baf4..791b18110 100755
--- a/src/conf_mode/protocols_mpls.py
+++ b/src/conf_mode/protocols_mpls.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# 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
@@ -16,351 +16,124 @@
import os
+from sys import exit
+
from vyos.config import Config
-from vyos import ConfigError
+from vyos.configdict import node_changed
+from vyos.template import render_to_string
from vyos.util import call
-from vyos.template import render
-
+from vyos.util import dict_search
+from vyos import ConfigError
+from vyos import frr
from vyos import airbag
airbag.enable()
config_file = r'/tmp/ldpd.frr'
-def sysctl(name, value):
- call('sysctl -wq {}={}'.format(name, value))
-
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
- mpls_conf = {
- 'router_id' : None,
- 'mpls_ldp' : False,
- 'old_parameters' : {
- 'no_ttl_propagation' : False,
- 'maximum_ttl' : None
- },
- 'parameters' : {
- 'no_ttl_propagation' : False,
- 'maximum_ttl' : None
- },
- 'old_ldp' : {
- 'interfaces' : [],
- 'neighbors' : {},
- 'd_transp_ipv4' : None,
- 'd_transp_ipv6' : None,
- 'hello_ipv4_holdtime' : None,
- 'hello_ipv4_interval' : None,
- 'hello_ipv6_holdtime' : None,
- 'hello_ipv6_interval' : None,
- 'ses_ipv4_hold' : None,
- 'ses_ipv6_hold' : None,
- 'export_ipv4_exp' : False,
- 'export_ipv6_exp' : False,
- 'cisco_interop_tlv' : False,
- 'transport_prefer_ipv4' : False,
- 'target_ipv4_addresses' : [],
- 'target_ipv6_addresses' : [],
- 'target_ipv4_enable' : False,
- 'target_ipv6_enable' : False,
- 'target_ipv4_hello_int' : None,
- 'target_ipv6_hello_int' : None,
- 'target_ipv4_hello_hold' : None,
- 'target_ipv6_hello_hold' : None
- },
- 'ldp' : {
- 'interfaces' : [],
- 'neighbors' : {},
- 'd_transp_ipv4' : None,
- 'd_transp_ipv6' : None,
- 'hello_ipv4_holdtime' : None,
- 'hello_ipv4_interval' : None,
- 'hello_ipv6_holdtime' : None,
- 'hello_ipv6_interval' : None,
- 'ses_ipv4_hold' : None,
- 'ses_ipv6_hold' : None,
- 'export_ipv4_exp' : False,
- 'export_ipv6_exp' : False,
- 'cisco_interop_tlv' : False,
- 'transport_prefer_ipv4' : False,
- 'target_ipv4_addresses' : [],
- 'target_ipv6_addresses' : [],
- 'target_ipv4_enable' : False,
- 'target_ipv6_enable' : False,
- 'target_ipv4_hello_int' : None,
- 'target_ipv6_hello_int' : None,
- 'target_ipv4_hello_hold' : None,
- 'target_ipv6_hello_hold' : None
- }
- }
- if not (conf.exists('protocols mpls') or conf.exists_effective('protocols mpls')):
- return None
-
- # If LDP is defined then enable LDP portion of code
- if conf.exists('protocols mpls ldp'):
- mpls_conf['mpls_ldp'] = True
-
- # Set to MPLS hierarchy configuration level
- conf.set_level('protocols mpls')
-
- # Get no_ttl_propagation
- if conf.exists_effective('parameters no-propagate-ttl'):
- mpls_conf['old_parameters']['no_ttl_propagation'] = True
-
- if conf.exists('parameters no-propagate-ttl'):
- mpls_conf['parameters']['no_ttl_propagation'] = True
-
- # Get maximum_ttl
- if conf.exists_effective('parameters maximum-ttl'):
- mpls_conf['old_parameters']['maximum_ttl'] = conf.return_effective_value('parameters maximum-ttl')
-
- if conf.exists('parameters maximum-ttl'):
- mpls_conf['parameters']['maximum_ttl'] = conf.return_value('parameters maximum-ttl')
-
- # Set to LDP hierarchy configuration level
- conf.set_level('protocols mpls ldp')
-
- # Get router-id
- if conf.exists_effective('router-id'):
- mpls_conf['old_router_id'] = conf.return_effective_value('router-id')
-
- if conf.exists('router-id'):
- mpls_conf['router_id'] = conf.return_value('router-id')
-
- # Get hello-ipv4-holdtime
- if conf.exists_effective('discovery hello-ipv4-holdtime'):
- mpls_conf['old_ldp']['hello_ipv4_holdtime'] = conf.return_effective_value('discovery hello-ipv4-holdtime')
-
- if conf.exists('discovery hello-ipv4-holdtime'):
- mpls_conf['ldp']['hello_ipv4_holdtime'] = conf.return_value('discovery hello-ipv4-holdtime')
-
- # Get hello-ipv4-interval
- if conf.exists_effective('discovery hello-ipv4-interval'):
- mpls_conf['old_ldp']['hello_ipv4_interval'] = conf.return_effective_value('discovery hello-ipv4-interval')
-
- if conf.exists('discovery hello-ipv4-interval'):
- mpls_conf['ldp']['hello_ipv4_interval'] = conf.return_value('discovery hello-ipv4-interval')
-
- # Get hello-ipv6-holdtime
- if conf.exists_effective('discovery hello-ipv6-holdtime'):
- mpls_conf['old_ldp']['hello_ipv6_holdtime'] = conf.return_effective_value('discovery hello-ipv6-holdtime')
-
- if conf.exists('discovery hello-ipv6-holdtime'):
- mpls_conf['ldp']['hello_ipv6_holdtime'] = conf.return_value('discovery hello-ipv6-holdtime')
-
- # Get hello-ipv6-interval
- if conf.exists_effective('discovery hello-ipv6-interval'):
- mpls_conf['old_ldp']['hello_ipv6_interval'] = conf.return_effective_value('discovery hello-ipv6-interval')
-
- if conf.exists('discovery hello-ipv6-interval'):
- mpls_conf['ldp']['hello_ipv6_interval'] = conf.return_value('discovery hello-ipv6-interval')
-
- # Get session-ipv4-holdtime
- if conf.exists_effective('discovery session-ipv4-holdtime'):
- mpls_conf['old_ldp']['ses_ipv4_hold'] = conf.return_effective_value('discovery session-ipv4-holdtime')
-
- if conf.exists('discovery session-ipv4-holdtime'):
- mpls_conf['ldp']['ses_ipv4_hold'] = conf.return_value('discovery session-ipv4-holdtime')
-
- # Get session-ipv6-holdtime
- if conf.exists_effective('discovery session-ipv6-holdtime'):
- mpls_conf['old_ldp']['ses_ipv6_hold'] = conf.return_effective_value('discovery session-ipv6-holdtime')
-
- if conf.exists('discovery session-ipv6-holdtime'):
- mpls_conf['ldp']['ses_ipv6_hold'] = conf.return_value('discovery session-ipv6-holdtime')
-
- # Get discovery transport-ipv4-address
- if conf.exists_effective('discovery transport-ipv4-address'):
- mpls_conf['old_ldp']['d_transp_ipv4'] = conf.return_effective_value('discovery transport-ipv4-address')
-
- if conf.exists('discovery transport-ipv4-address'):
- mpls_conf['ldp']['d_transp_ipv4'] = conf.return_value('discovery transport-ipv4-address')
-
- # Get discovery transport-ipv6-address
- if conf.exists_effective('discovery transport-ipv6-address'):
- mpls_conf['old_ldp']['d_transp_ipv6'] = conf.return_effective_value('discovery transport-ipv6-address')
-
- if conf.exists('discovery transport-ipv6-address'):
- mpls_conf['ldp']['d_transp_ipv6'] = conf.return_value('discovery transport-ipv6-address')
-
- # Get export ipv4 explicit-null
- if conf.exists_effective('export ipv4 explicit-null'):
- mpls_conf['old_ldp']['export_ipv4_exp'] = True
-
- if conf.exists('export ipv4 explicit-null'):
- mpls_conf['ldp']['export_ipv4_exp'] = True
-
- # Get export ipv6 explicit-null
- if conf.exists_effective('export ipv6 explicit-null'):
- mpls_conf['old_ldp']['export_ipv6_exp'] = True
-
- if conf.exists('export ipv6 explicit-null'):
- mpls_conf['ldp']['export_ipv6_exp'] = True
-
- # Get target_ipv4_addresses
- if conf.exists_effective('targeted-neighbor ipv4 address'):
- mpls_conf['old_ldp']['target_ipv4_addresses'] = conf.return_effective_values('targeted-neighbor ipv4 address')
+ base = ['protocols', 'mpls']
- if conf.exists('targeted-neighbor ipv4 address'):
- mpls_conf['ldp']['target_ipv4_addresses'] = conf.return_values('targeted-neighbor ipv4 address')
-
- # Get target_ipv4_enable
- if conf.exists_effective('targeted-neighbor ipv4 enable'):
- mpls_conf['old_ldp']['target_ipv4_enable'] = True
-
- if conf.exists('targeted-neighbor ipv4 enable'):
- mpls_conf['ldp']['target_ipv4_enable'] = True
-
- # Get target_ipv4_hello_int
- if conf.exists_effective('targeted-neighbor ipv4 hello-interval'):
- mpls_conf['old_ldp']['target_ipv4_hello_int'] = conf.return_effective_value('targeted-neighbor ipv4 hello-interval')
-
- if conf.exists('targeted-neighbor ipv4 hello-interval'):
- mpls_conf['ldp']['target_ipv4_hello_int'] = conf.return_value('targeted-neighbor ipv4 hello-interval')
-
- # Get target_ipv4_hello_hold
- if conf.exists_effective('targeted-neighbor ipv4 hello-holdtime'):
- mpls_conf['old_ldp']['target_ipv4_hello_hold'] = conf.return_effective_value('targeted-neighbor ipv4 hello-holdtime')
-
- if conf.exists('targeted-neighbor ipv4 hello-holdtime'):
- mpls_conf['ldp']['target_ipv4_hello_hold'] = conf.return_value('targeted-neighbor ipv4 hello-holdtime')
-
- # Get target_ipv6_addresses
- if conf.exists_effective('targeted-neighbor ipv6 address'):
- mpls_conf['old_ldp']['target_ipv6_addresses'] = conf.return_effective_values('targeted-neighbor ipv6 address')
-
- if conf.exists('targeted-neighbor ipv6 address'):
- mpls_conf['ldp']['target_ipv6_addresses'] = conf.return_values('targeted-neighbor ipv6 address')
-
- # Get target_ipv6_enable
- if conf.exists_effective('targeted-neighbor ipv6 enable'):
- mpls_conf['old_ldp']['target_ipv6_enable'] = True
-
- if conf.exists('targeted-neighbor ipv6 enable'):
- mpls_conf['ldp']['target_ipv6_enable'] = True
-
- # Get target_ipv6_hello_int
- if conf.exists_effective('targeted-neighbor ipv6 hello-interval'):
- mpls_conf['old_ldp']['target_ipv6_hello_int'] = conf.return_effective_value('targeted-neighbor ipv6 hello-interval')
-
- if conf.exists('targeted-neighbor ipv6 hello-interval'):
- mpls_conf['ldp']['target_ipv6_hello_int'] = conf.return_value('targeted-neighbor ipv6 hello-interval')
-
- # Get target_ipv6_hello_hold
- if conf.exists_effective('targeted-neighbor ipv6 hello-holdtime'):
- mpls_conf['old_ldp']['target_ipv6_hello_hold'] = conf.return_effective_value('targeted-neighbor ipv6 hello-holdtime')
-
- if conf.exists('targeted-neighbor ipv6 hello-holdtime'):
- mpls_conf['ldp']['target_ipv6_hello_hold'] = conf.return_value('targeted-neighbor ipv6 hello-holdtime')
-
- # Get parameters cisco-interop-tlv
- if conf.exists_effective('parameters cisco-interop-tlv'):
- mpls_conf['old_ldp']['cisco_interop_tlv'] = True
-
- if conf.exists('parameters cisco-interop-tlv'):
- mpls_conf['ldp']['cisco_interop_tlv'] = True
-
- # Get parameters transport-prefer-ipv4
- if conf.exists_effective('parameters transport-prefer-ipv4'):
- mpls_conf['old_ldp']['transport_prefer_ipv4'] = True
-
- if conf.exists('parameters transport-prefer-ipv4'):
- mpls_conf['ldp']['transport_prefer_ipv4'] = True
-
- # Get interfaces
- if conf.exists_effective('interface'):
- mpls_conf['old_ldp']['interfaces'] = conf.return_effective_values('interface')
-
- if conf.exists('interface'):
- mpls_conf['ldp']['interfaces'] = conf.return_values('interface')
-
- # Get neighbors
- for neighbor in conf.list_effective_nodes('neighbor'):
- mpls_conf['old_ldp']['neighbors'].update({
- neighbor : {
- 'password' : conf.return_effective_value('neighbor {0} password'.format(neighbor), default=''),
- 'ttl_security' : conf.return_effective_value('neighbor {0} ttl-security'.format(neighbor), default=''),
- 'session_holdtime' : conf.return_effective_value('neighbor {0} session-holdtime'.format(neighbor), default='')
- }
- })
-
- for neighbor in conf.list_nodes('neighbor'):
- mpls_conf['ldp']['neighbors'].update({
- neighbor : {
- 'password' : conf.return_value('neighbor {0} password'.format(neighbor), default=''),
- 'ttl_security' : conf.return_value('neighbor {0} ttl-security'.format(neighbor), default=''),
- 'session_holdtime' : conf.return_value('neighbor {0} session-holdtime'.format(neighbor), default='')
- }
- })
-
- return mpls_conf
-
-def operate_mpls_on_intfc(interfaces, action):
- rp_filter = 0
- if action == 1:
- rp_filter = 2
- for iface in interfaces:
- sysctl('net.mpls.conf.{0}.input'.format(iface), action)
- # Operate rp filter
- sysctl('net.ipv4.conf.{0}.rp_filter'.format(iface), rp_filter)
+ mpls = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ return mpls
def verify(mpls):
- if mpls is None:
+ # If no config, then just bail out early.
+ if not mpls:
return None
- if mpls['mpls_ldp']:
- # Require router-id
- if not mpls['router_id']:
- raise ConfigError(f"MPLS ldp router-id is mandatory!")
+ # Checks to see if LDP is properly configured
+ if 'ldp' in mpls:
+ # If router ID not defined
+ if 'router_id' not in mpls['ldp']:
+ raise ConfigError('Router ID missing. An LDP router id is mandatory!')
- # Require discovery transport-address
- if not mpls['ldp']['d_transp_ipv4'] and not mpls['ldp']['d_transp_ipv6']:
- raise ConfigError(f"MPLS ldp discovery transport address is mandatory!")
+ # If interface not set
+ if 'interface' not in mpls['ldp']:
+ raise ConfigError('LDP interfaces are missing. An LDP interface is mandatory!')
- # Require interface
- if not mpls['ldp']['interfaces']:
- raise ConfigError(f"MPLS ldp interface is mandatory!")
+ # If transport addresses are not set
+ if not dict_search('ldp.discovery.transport_ipv4_address', mpls) and \
+ not dict_search('ldp.discovery.transport_ipv6_address', mpls):
+ raise ConfigError('LDP transport address missing!')
+
+ return None
def generate(mpls):
- if mpls is None:
+ # If there's no MPLS config generated, create dictionary key with no value.
+ if not mpls:
+ mpls['new_frr_config'] = ''
return None
- render(config_file, 'frr/ldpd.frr.tmpl', mpls)
+ mpls['new_frr_config'] = render_to_string('frr/ldpd.frr.tmpl', mpls)
return None
def apply(mpls):
- if mpls is None:
- return None
-
- # Set number of entries in the platform label table
- if mpls['mpls_ldp']:
- sysctl('net.mpls.platform_labels', '1048575')
+ # Define dictionary that will load FRR config
+ frr_cfg = {}
+ # Save original configuration prior to starting any commit actions
+ frr_cfg['original_config'] = frr.get_configuration(daemon='ldpd')
+ frr_cfg['modified_config'] = frr.replace_section(frr_cfg['original_config'], mpls['new_frr_config'], from_re='mpls.*')
+
+ # If FRR config is blank, rerun the blank commit three times due to frr-reload
+ # behavior/bug not properly clearing out on one commit.
+ if mpls['new_frr_config'] == '':
+ for x in range(3):
+ frr.reload_configuration(frr_cfg['modified_config'], daemon='ldpd')
+ elif not 'ldp' in mpls:
+ for x in range(3):
+ frr.reload_configuration(frr_cfg['modified_config'], daemon='ldpd')
else:
- sysctl('net.mpls.platform_labels', '0')
-
- # Choose whether to copy IP TTL to MPLS header TTL
- if mpls['parameters']['no_ttl_propagation']:
- sysctl('net.mpls.ip_ttl_propagate', '0')
+ # FRR mark configuration will test for syntax errors and throws an
+ # exception if any syntax errors is detected
+ frr.mark_configuration(frr_cfg['modified_config'])
+
+ # Commit resulting configuration to FRR, this will throw CommitError
+ # on failure
+ frr.reload_configuration(frr_cfg['modified_config'], daemon='ldpd')
+
+ # Set number of entries in the platform label tables
+ labels = '0'
+ if 'interface' in mpls:
+ labels = '1048575'
+ call(f'sysctl -wq net.mpls.platform_labels={labels}')
+
+ # Check for changes in global MPLS options
+ if 'parameters' in mpls:
+ # Choose whether to copy IP TTL to MPLS header TTL
+ if 'no_propagate_ttl' in mpls['parameters']:
+ call('sysctl -wq net.mpls.ip_ttl_propagate=0')
+ # Choose whether to limit maximum MPLS header TTL
+ if 'maximum_ttl' in mpls['parameters']:
+ ttl = mpls['parameters']['maximum_ttl']
+ call(f'sysctl -wq net.mpls.default_ttl={ttl}')
else:
- sysctl('net.mpls.ip_ttl_propagate', '1')
-
- # Choose whether to limit maximum MPLS header TTL
- if mpls['parameters']['maximum_ttl']:
- sysctl('net.mpls.default_ttl', '%s' %(mpls['parameters']['maximum_ttl']))
+ # Set default global MPLS options if not defined.
+ call('sysctl -wq net.mpls.ip_ttl_propagate=1')
+ call('sysctl -wq net.mpls.default_ttl=255')
+
+ # Enable and disable MPLS processing on interfaces per configuration
+ if 'interface' in mpls:
+ system_interfaces = []
+ system_interfaces.append(((os.popen('sysctl net.mpls.conf').read()).split('\n')))
+ del system_interfaces[0][-1]
+ for configured_interface in mpls['interface']:
+ for system_interface in system_interfaces[0]:
+ if configured_interface in system_interface:
+ call(f'sysctl -wq net.mpls.conf.{configured_interface}.input=1')
+ elif system_interface.endswith(' = 1'):
+ system_interface = system_interface.replace(' = 1', '=0')
+ call(f'sysctl -wq {system_interface}')
else:
- sysctl('net.mpls.default_ttl', '255')
-
- # Allow mpls on interfaces
- operate_mpls_on_intfc(mpls['ldp']['interfaces'], 1)
-
- # Disable mpls on deleted interfaces
- diactive_ifaces = set(mpls['old_ldp']['interfaces']).difference(mpls['ldp']['interfaces'])
- operate_mpls_on_intfc(diactive_ifaces, 0)
-
- if os.path.exists(config_file):
- call(f'vtysh -d ldpd -f {config_file}')
- os.remove(config_file)
+ # If MPLS interfaces are not configured, set MPLS processing disabled
+ system_interfaces = []
+ system_interfaces.append(((os.popen('sysctl net.mpls.conf').read()).replace(" = 1", "=0")).split('\n'))
+ del system_interfaces[0][-1]
+ for interface in (system_interfaces[0]):
+ call(f'sysctl -wq {interface}')
return None
diff --git a/src/conf_mode/protocols_pim.py b/src/conf_mode/protocols_pim.py
index 6d333e19a..8a9f034d5 100755
--- a/src/conf_mode/protocols_pim.py
+++ b/src/conf_mode/protocols_pim.py
@@ -21,8 +21,9 @@ from sys import exit
from vyos.config import Config
from vyos import ConfigError
-from vyos.util import call
+from vyos.util import call, process_named_running
from vyos.template import render
+from signal import SIGTERM
from vyos import airbag
airbag.enable()
@@ -36,6 +37,8 @@ def get_config(config=None):
conf = Config()
pim_conf = {
'pim_conf' : False,
+ 'igmp_conf' : False,
+ 'igmp_proxy_conf' : False,
'old_pim' : {
'ifaces' : {},
'rp' : {}
@@ -48,6 +51,12 @@ def get_config(config=None):
if not (conf.exists('protocols pim') or conf.exists_effective('protocols pim')):
return None
+ if conf.exists('protocols igmp-proxy'):
+ pim_conf['igmp_proxy_conf'] = True
+
+ if conf.exists('protocols igmp'):
+ pim_conf['igmp_conf'] = True
+
if conf.exists('protocols pim'):
pim_conf['pim_conf'] = True
@@ -92,6 +101,10 @@ def verify(pim):
return None
if pim['pim_conf']:
+ # Check conflict with IGMP-Proxy
+ if pim['igmp_proxy_conf']:
+ raise ConfigError(f"IGMP proxy and PIM cannot be both configured at the same time")
+
# Check interfaces
if not pim['pim']['ifaces']:
raise ConfigError(f"PIM require defined interfaces!")
@@ -126,9 +139,16 @@ def apply(pim):
if pim is None:
return None
- if os.path.exists(config_file):
- call("vtysh -d pimd -f " + config_file)
- os.remove(config_file)
+ pim_pid = process_named_running('pimd')
+ if pim['igmp_conf'] or pim['pim_conf']:
+ if not pim_pid:
+ call(f'pimd -d -F traditional --daemon -A 127.0.0.1')
+
+ if os.path.exists(config_file):
+ call("vtysh -d pimd -f " + config_file)
+ os.remove(config_file)
+ elif pim_pid:
+ os.kill(int(pim_pid), SIGTERM)
return None
diff --git a/src/conf_mode/service_ids_fastnetmon.py b/src/conf_mode/service_ids_fastnetmon.py
index 27d0ee60c..67edeb630 100755
--- a/src/conf_mode/service_ids_fastnetmon.py
+++ b/src/conf_mode/service_ids_fastnetmon.py
@@ -56,7 +56,7 @@ def verify(fastnetmon):
if not os.access(fastnetmon["alert_script"], os.X_OK):
raise ConfigError('Script {0} does not have permissions for execution'.format(fastnetmon["alert_script"]))
else:
- raise ConfigError('File {0} does not exists!'.format(fastnetmon["alert_script"]))
+ raise ConfigError('File {0} does not exists!'.format(fastnetmon["alert_script"]))
def generate(fastnetmon):
if not fastnetmon:
@@ -67,8 +67,8 @@ def generate(fastnetmon):
return
- render(config_file, 'ids/fastnetmon.tmpl', fastnetmon, trim_blocks=True)
- render(networks_list, 'ids/fastnetmon_networks_list.tmpl', fastnetmon, trim_blocks=True)
+ render(config_file, 'ids/fastnetmon.tmpl', fastnetmon)
+ render(networks_list, 'ids/fastnetmon_networks_list.tmpl', fastnetmon)
return None
diff --git a/src/conf_mode/service_ipoe-server.py b/src/conf_mode/service_ipoe-server.py
index 68c554360..f676fdbbe 100755
--- a/src/conf_mode/service_ipoe-server.py
+++ b/src/conf_mode/service_ipoe-server.py
@@ -283,7 +283,7 @@ def generate(ipoe):
if not ipoe:
return None
- render(ipoe_conf, 'accel-ppp/ipoe.config.tmpl', ipoe, trim_blocks=True)
+ render(ipoe_conf, 'accel-ppp/ipoe.config.tmpl', ipoe)
if ipoe['auth_mode'] == 'local':
render(ipoe_chap_secrets, 'accel-ppp/chap-secrets.ipoe.tmpl', ipoe)
diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py
index 2260b3fe1..9fbd531da 100755
--- a/src/conf_mode/service_pppoe-server.py
+++ b/src/conf_mode/service_pppoe-server.py
@@ -73,11 +73,11 @@ def generate(pppoe):
if not pppoe:
return None
- render(pppoe_conf, 'accel-ppp/pppoe.config.tmpl', pppoe, trim_blocks=True)
+ render(pppoe_conf, 'accel-ppp/pppoe.config.tmpl', pppoe)
if dict_search('authentication.mode', pppoe) == 'local':
render(pppoe_chap_secrets, 'accel-ppp/chap-secrets.config_dict.tmpl',
- pppoe, trim_blocks=True, permission=0o640)
+ pppoe, permission=0o640)
else:
if os.path.exists(pppoe_chap_secrets):
os.unlink(pppoe_chap_secrets)
diff --git a/src/conf_mode/service_router-advert.py b/src/conf_mode/service_router-advert.py
index 687d7068f..65eb11ce3 100755
--- a/src/conf_mode/service_router-advert.py
+++ b/src/conf_mode/service_router-advert.py
@@ -93,7 +93,7 @@ def generate(rtradv):
if not rtradv:
return None
- render(config_file, 'router-advert/radvd.conf.tmpl', rtradv, trim_blocks=True, permission=0o644)
+ render(config_file, 'router-advert/radvd.conf.tmpl', rtradv, permission=0o644)
return None
def apply(rtradv):
diff --git a/src/conf_mode/ssh.py b/src/conf_mode/ssh.py
index e07745963..8f99053d2 100755
--- a/src/conf_mode/ssh.py
+++ b/src/conf_mode/ssh.py
@@ -66,8 +66,8 @@ def generate(ssh):
return None
- render(config_file, 'ssh/sshd_config.tmpl', ssh, trim_blocks=True)
- render(systemd_override, 'ssh/override.conf.tmpl', ssh, trim_blocks=True)
+ render(config_file, 'ssh/sshd_config.tmpl', ssh)
+ render(systemd_override, 'ssh/override.conf.tmpl', ssh)
return None
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py
index 2c0bbd4f7..39bad717d 100755
--- a/src/conf_mode/system-login.py
+++ b/src/conf_mode/system-login.py
@@ -256,7 +256,7 @@ def generate(login):
if len(login['radius_server']) > 0:
render(radius_config_file, 'system-login/pam_radius_auth.conf.tmpl',
- login, trim_blocks=True)
+ login)
uid = getpwnam('root').pw_uid
gid = getpwnam('root').pw_gid
diff --git a/src/conf_mode/system-options.py b/src/conf_mode/system-option.py
index 1061b90ac..910c14474 100755
--- a/src/conf_mode/system-options.py
+++ b/src/conf_mode/system-option.py
@@ -39,7 +39,7 @@ def get_config(config=None):
conf = config
else:
conf = Config()
- base = ['system', 'options']
+ base = ['system', 'option']
options = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
# We have gathered the dict representation of the CLI, but there are default
@@ -73,13 +73,13 @@ def verify(options):
return None
def generate(options):
- render(curlrc_config, 'system/curlrc.tmpl', options, trim_blocks=True)
- render(ssh_config, 'system/ssh_config.tmpl', options, trim_blocks=True)
+ render(curlrc_config, 'system/curlrc.tmpl', options)
+ render(ssh_config, 'system/ssh_config.tmpl', options)
return None
def apply(options):
# System bootup beep
- if 'beep_if_fully_booted' in options:
+ if 'startup_beep' in options:
cmd('systemctl enable vyos-beep.service')
else:
cmd('systemctl disable vyos-beep.service')
@@ -87,10 +87,10 @@ def apply(options):
# Ctrl-Alt-Delete action
if os.path.exists(systemd_action_file):
os.unlink(systemd_action_file)
- if 'ctrl_alt_del_action' in options:
- if options['ctrl_alt_del_action'] == 'reboot':
+ if 'ctrl_alt_del' in options:
+ if options['ctrl_alt_del'] == 'reboot':
os.symlink('/lib/systemd/system/reboot.target', systemd_action_file)
- elif options['ctrl_alt_del_action'] == 'poweroff':
+ elif options['ctrl_alt_del'] == 'poweroff':
os.symlink('/lib/systemd/system/poweroff.target', systemd_action_file)
# Configure HTTP client
@@ -123,7 +123,7 @@ def apply(options):
# Keyboard layout - there will be always the default key inside the dict
# but we check for key existence anyway
if 'keyboard_layout' in options:
- cmd('loadkeys -C /dev/console {keyboard_layout}'.format(**options))
+ cmd('loadkeys {keyboard_layout}'.format(**options))
if __name__ == '__main__':
try:
@@ -134,4 +134,3 @@ if __name__ == '__main__':
except ConfigError as e:
print(e)
exit(1)
-
diff --git a/src/conf_mode/system-syslog.py b/src/conf_mode/system-syslog.py
index b1daf7a82..3d8a51cd8 100755
--- a/src/conf_mode/system-syslog.py
+++ b/src/conf_mode/system-syslog.py
@@ -203,12 +203,12 @@ def generate(c):
return None
conf = '/etc/rsyslog.d/vyos-rsyslog.conf'
- render(conf, 'syslog/rsyslog.conf.tmpl', c, trim_blocks=True)
+ render(conf, 'syslog/rsyslog.conf.tmpl', c)
# eventually write for each file its own logrotate file, since size is
# defined it shouldn't matter
conf = '/etc/logrotate.d/vyos-rsyslog'
- render(conf, 'syslog/logrotate.tmpl', c, trim_blocks=True)
+ render(conf, 'syslog/logrotate.tmpl', c)
def verify(c):
diff --git a/src/conf_mode/system_lcd.py b/src/conf_mode/system_lcd.py
index a540d1b9e..b5ce32beb 100755
--- a/src/conf_mode/system_lcd.py
+++ b/src/conf_mode/system_lcd.py
@@ -61,9 +61,9 @@ def generate(lcd):
lcd['device'] = find_device_file(lcd['device'])
# Render config file for daemon LCDd
- render(lcdd_conf, 'lcd/LCDd.conf.tmpl', lcd, trim_blocks=True)
+ render(lcdd_conf, 'lcd/LCDd.conf.tmpl', lcd)
# Render config file for client lcdproc
- render(lcdproc_conf, 'lcd/lcdproc.conf.tmpl', lcd, trim_blocks=True)
+ render(lcdproc_conf, 'lcd/lcdproc.conf.tmpl', lcd)
return None
diff --git a/src/conf_mode/tftp_server.py b/src/conf_mode/tftp_server.py
index 56e195b6a..2409eec1f 100755
--- a/src/conf_mode/tftp_server.py
+++ b/src/conf_mode/tftp_server.py
@@ -92,7 +92,7 @@ def generate(tftpd):
config['listen_address'] = f'[{address}]:{port} -6'
file = config_file + str(idx)
- render(file, 'tftp-server/default.tmpl', config, trim_blocks=True)
+ render(file, 'tftp-server/default.tmpl', config)
idx = idx + 1
return None
diff --git a/src/conf_mode/vpn_l2tp.py b/src/conf_mode/vpn_l2tp.py
index 80eb8daf2..e970d2ef5 100755
--- a/src/conf_mode/vpn_l2tp.py
+++ b/src/conf_mode/vpn_l2tp.py
@@ -357,7 +357,7 @@ def generate(l2tp):
if not l2tp:
return None
- render(l2tp_conf, 'accel-ppp/l2tp.config.tmpl', l2tp, trim_blocks=True)
+ render(l2tp_conf, 'accel-ppp/l2tp.config.tmpl', l2tp)
if l2tp['auth_mode'] == 'local':
render(l2tp_chap_secrets, 'accel-ppp/chap-secrets.tmpl', l2tp)
diff --git a/src/conf_mode/vpn_openconnect.py b/src/conf_mode/vpn_openconnect.py
index af8604972..b2aa13c0d 100755
--- a/src/conf_mode/vpn_openconnect.py
+++ b/src/conf_mode/vpn_openconnect.py
@@ -34,12 +34,10 @@ ocserv_passwd = cfg_dir + '/ocpasswd'
radius_cfg = cfg_dir + '/radiusclient.conf'
radius_servers = cfg_dir + '/radius_servers'
-
# Generate hash from user cleartext password
def get_hash(password):
return crypt(password, mksalt(METHOD_SHA512))
-
def get_config():
conf = Config()
base = ['vpn', 'openconnect']
@@ -47,10 +45,12 @@ def get_config():
return None
ocserv = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
default_values = defaults(base)
ocserv = dict_merge(default_values, ocserv)
- return ocserv
+ return ocserv
def verify(ocserv):
if ocserv is None:
@@ -88,7 +88,7 @@ def verify(ocserv):
ocserv["network_settings"]["push_route"].remove("0.0.0.0/0")
ocserv["network_settings"]["push_route"].append("default")
else:
- ocserv["network_settings"]["push_route"] = "default"
+ ocserv["network_settings"]["push_route"] = "default"
else:
raise ConfigError('openconnect network settings required')
@@ -99,19 +99,18 @@ def generate(ocserv):
if "radius" in ocserv["authentication"]["mode"]:
# Render radius client configuration
- render(radius_cfg, 'ocserv/radius_conf.tmpl', ocserv["authentication"]["radius"], trim_blocks=True)
+ render(radius_cfg, 'ocserv/radius_conf.tmpl', ocserv["authentication"]["radius"])
# Render radius servers
- render(radius_servers, 'ocserv/radius_servers.tmpl', ocserv["authentication"]["radius"], trim_blocks=True)
+ render(radius_servers, 'ocserv/radius_servers.tmpl', ocserv["authentication"]["radius"])
else:
if "local_users" in ocserv["authentication"]:
for user in ocserv["authentication"]["local_users"]["username"]:
ocserv["authentication"]["local_users"]["username"][user]["hash"] = get_hash(ocserv["authentication"]["local_users"]["username"][user]["password"])
# Render local users
- render(ocserv_passwd, 'ocserv/ocserv_passwd.tmpl', ocserv["authentication"]["local_users"], trim_blocks=True)
+ render(ocserv_passwd, 'ocserv/ocserv_passwd.tmpl', ocserv["authentication"]["local_users"])
# Render config
- render(ocserv_conf, 'ocserv/ocserv_config.tmpl', ocserv, trim_blocks=True)
-
+ render(ocserv_conf, 'ocserv/ocserv_config.tmpl', ocserv)
def apply(ocserv):
diff --git a/src/conf_mode/vpn_pptp.py b/src/conf_mode/vpn_pptp.py
index 3125ee9d0..30abe4782 100755
--- a/src/conf_mode/vpn_pptp.py
+++ b/src/conf_mode/vpn_pptp.py
@@ -264,10 +264,10 @@ def generate(pptp):
if not pptp:
return None
- render(pptp_conf, 'accel-ppp/pptp.config.tmpl', pptp, trim_blocks=True)
+ render(pptp_conf, 'accel-ppp/pptp.config.tmpl', pptp)
if pptp['local_users']:
- render(pptp_chap_secrets, 'accel-ppp/chap-secrets.tmpl', pptp, trim_blocks=True)
+ render(pptp_chap_secrets, 'accel-ppp/chap-secrets.tmpl', pptp)
os.chmod(pptp_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP)
else:
if os.path.exists(pptp_chap_secrets):
diff --git a/src/conf_mode/vpn_sstp.py b/src/conf_mode/vpn_sstp.py
index 1b2b80ce5..47367f125 100755
--- a/src/conf_mode/vpn_sstp.py
+++ b/src/conf_mode/vpn_sstp.py
@@ -82,11 +82,11 @@ def generate(sstp):
return None
# accel-cmd reload doesn't work so any change results in a restart of the daemon
- render(sstp_conf, 'accel-ppp/sstp.config.tmpl', sstp, trim_blocks=True)
+ render(sstp_conf, 'accel-ppp/sstp.config.tmpl', sstp)
if dict_search('authentication.mode', sstp) == 'local':
render(sstp_chap_secrets, 'accel-ppp/chap-secrets.config_dict.tmpl',
- sstp, trim_blocks=True, permission=0o640)
+ sstp, permission=0o640)
else:
if os.path.exists(sstp_chap_secrets):
os.unlink(sstp_chap_secrets)
diff --git a/src/migration-scripts/interfaces/14-to-15 b/src/migration-scripts/interfaces/14-to-15
new file mode 100755
index 000000000..5c25f8628
--- /dev/null
+++ b/src/migration-scripts/interfaces/14-to-15
@@ -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/>.
+
+# T3048: remove smp-affinity node from ethernet and use tuned instead
+
+import os
+
+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', 'ethernet']
+
+ if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+ migrate = False
+ for interface in config.list_nodes(base):
+ smp_base = base + [interface, 'smp-affinity']
+ # if any one interface had smp-affinity configured manually, we will
+ # configure "system option performance"
+ if config.exists(smp_base):
+ if config.return_value(smp_base) != 'auto':
+ migrate = True
+ config.delete(smp_base)
+
+ if migrate:
+ config.set(['system', 'options', 'performance'], value='throughput')
+
+ 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/migration-scripts/system/19-to-20 b/src/migration-scripts/system/19-to-20
new file mode 100755
index 000000000..eb20fd8db
--- /dev/null
+++ b/src/migration-scripts/system/19-to-20
@@ -0,0 +1,64 @@
+#!/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/>.
+
+# T3048: remove smp-affinity node from ethernet and use tuned instead
+
+import os
+
+from sys import exit, argv
+from vyos.configtree import ConfigTree
+
+if (len(argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base = ['system', 'options']
+base_new = ['system', 'option']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+if config.exists(base_new):
+ for node in config.list_nodes(base):
+ config.copy(base + [node], base_new + [node])
+else:
+ config.copy(base, base_new)
+
+config.delete(base)
+
+# Rename "system option beep-if-fully-booted" -> "system option startup-beep"
+base_beep = base_new + ['beep-if-fully-booted']
+if config.exists(base_beep):
+ config.rename(base_beep, 'startup-beep')
+
+# Rename "system option ctrl-alt-del-action" -> "system option ctrl-alt-delete"
+base_ctrl_alt_del = base_new + ['ctrl-alt-del-action']
+if config.exists(base_ctrl_alt_del):
+ config.rename(base_ctrl_alt_del, 'ctrl-alt-delete')
+
+
+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/dynamic_dns.py b/src/op_mode/dynamic_dns.py
index 021acfd73..962943896 100755
--- a/src/op_mode/dynamic_dns.py
+++ b/src/op_mode/dynamic_dns.py
@@ -26,13 +26,13 @@ from vyos.util import call
cache_file = r'/run/ddclient/ddclient.cache'
OUT_TMPL_SRC = """
-{%- for entry in hosts -%}
+{% for entry in hosts %}
ip address : {{ entry.ip }}
host-name : {{ entry.host }}
last update : {{ entry.time }}
update-status: {{ entry.status }}
-{% endfor -%}
+{% endfor %}
"""
def show_status():
diff --git a/src/op_mode/lldp_op.py b/src/op_mode/lldp_op.py
index 172ce71b7..fa19e7d45 100755
--- a/src/op_mode/lldp_op.py
+++ b/src/op_mode/lldp_op.py
@@ -117,7 +117,7 @@ if __name__ == '__main__':
parser.print_help()
exit(1)
- tmpl = jinja2.Template(lldp_out, trim_blocks=True)
+ tmpl = jinja2.Template(lldp_out)
config_text = tmpl.render(parse_data(neighbors))
print(config_text)
diff --git a/src/op_mode/show_cpu.py b/src/op_mode/show_cpu.py
index 0a540da1d..0040e950d 100755
--- a/src/op_mode/show_cpu.py
+++ b/src/op_mode/show_cpu.py
@@ -21,16 +21,16 @@ from sys import exit
from vyos.util import popen, DEVNULL
OUT_TMPL_SRC = """
-{%- if cpu -%}
-{% if 'vendor' in cpu %}CPU Vendor: {{cpu.vendor}}{%- endif %}
-{% if 'model' in cpu %}Model: {{cpu.model}}{%- endif %}
-{% if 'cpus' in cpu %}Total CPUs: {{cpu.cpus}}{%- endif %}
-{% if 'sockets' in cpu %}Sockets: {{cpu.sockets}}{%- endif %}
-{% if 'cores' in cpu %}Cores: {{cpu.cores}}{%- endif %}
-{% if 'threads' in cpu %}Threads: {{cpu.threads}}{%- endif %}
-{% if 'mhz' in cpu %}Current MHz: {{cpu.mhz}}{%- endif %}
-{% if 'mhz_min' in cpu %}Minimum MHz: {{cpu.mhz_min}}{%- endif %}
-{% if 'mhz_max' in cpu %}Maximum MHz: {{cpu.mhz_max}}{%- endif %}
+{% if cpu %}
+{% if 'vendor' in cpu %}CPU Vendor: {{cpu.vendor}}{% endif %}
+{% if 'model' in cpu %}Model: {{cpu.model}}{% endif %}
+{% if 'cpus' in cpu %}Total CPUs: {{cpu.cpus}}{% endif %}
+{% if 'sockets' in cpu %}Sockets: {{cpu.sockets}}{% endif %}
+{% if 'cores' in cpu %}Cores: {{cpu.cores}}{% endif %}
+{% if 'threads' in cpu %}Threads: {{cpu.threads}}{% endif %}
+{% if 'mhz' in cpu %}Current MHz: {{cpu.mhz}}{% endif %}
+{% if 'mhz_min' in cpu %}Minimum MHz: {{cpu.mhz_min}}{% endif %}
+{% if 'mhz_max' in cpu %}Maximum MHz: {{cpu.mhz_max}}{% endif %}
{% endif %}
"""
diff --git a/src/op_mode/show_igmpproxy.py b/src/op_mode/show_igmpproxy.py
index 5ccc16287..4714e494b 100755
--- a/src/op_mode/show_igmpproxy.py
+++ b/src/op_mode/show_igmpproxy.py
@@ -36,9 +36,9 @@ import vyos.config
# eth0.3 0.0b 0 0.0b 0 xxx.xxx.x.7
# tun1 0.0b 0 0.0b 0 xxx.xxx.xxx.2
vif_out_tmpl = """
-{%- for r in data %}
+{% for r in data %}
{{ "%-10s"|format(r.interface) }} {{ "%-12s"|format(r.bytes_in) }} {{ "%-12s"|format(r.pkts_in) }} {{ "%-12s"|format(r.bytes_out) }} {{ "%-12s"|format(r.pkts_out) }} {{ "%-15s"|format(r.loc) }}
-{%- endfor %}
+{% endfor %}
"""
# Output Template for "show ip multicast mfc" command
@@ -48,9 +48,9 @@ vif_out_tmpl = """
# xxx.xxx.xxx.250 xxx.xx.xxx.75 --
# xxx.xxx.xx.124 xx.xxx.xxx.26 --
mfc_out_tmpl = """
-{%- for r in data %}
+{% for r in data %}
{{ "%-15s"|format(r.group) }} {{ "%-15s"|format(r.origin) }} {{ "%-12s"|format(r.pkts) }} {{ "%-12s"|format(r.bytes) }} {{ "%-12s"|format(r.wrong) }} {{ "%-10s"|format(r.iif) }} {{ "%-20s"|format(r.oifs|join(', ')) }}
-{%- endfor %}
+{% endfor %}
"""
parser = argparse.ArgumentParser()
diff --git a/src/op_mode/show_nat_statistics.py b/src/op_mode/show_nat_statistics.py
index 0b53112f2..482993d06 100755
--- a/src/op_mode/show_nat_statistics.py
+++ b/src/op_mode/show_nat_statistics.py
@@ -26,14 +26,14 @@ OUT_TMPL_SRC="""
rule pkts bytes interface
---- ---- ----- ---------
{% for r in output %}
-{%- if r.comment -%}
-{%- set packets = r.counter.packets -%}
-{%- set bytes = r.counter.bytes -%}
-{%- set interface = r.interface -%}
+{% if r.comment %}
+{% set packets = r.counter.packets %}
+{% set bytes = r.counter.bytes %}
+{% set interface = r.interface %}
{# remove rule comment prefix #}
-{%- set comment = r.comment | replace('SRC-NAT-', '') | replace('DST-NAT-', '') | replace(' tcp_udp', '') -%}
+{% set comment = r.comment | replace('SRC-NAT-', '') | replace('DST-NAT-', '') | replace(' tcp_udp', '') %}
{{ "%-4s" | format(comment) }} {{ "%9s" | format(packets) }} {{ "%12s" | format(bytes) }} {{ interface }}
-{%- endif %}
+{% endif %}
{% endfor %}
"""
diff --git a/src/op_mode/show_openvpn.py b/src/op_mode/show_openvpn.py
index 1da4c7ecb..f7b99cc0d 100755
--- a/src/op_mode/show_openvpn.py
+++ b/src/op_mode/show_openvpn.py
@@ -28,9 +28,9 @@ OpenVPN status on {{ intf }}
Client CN Remote Host Local Host TX bytes RX bytes Connected Since
--------- ----------- ---------- -------- -------- ---------------
-{%- for c in clients %}
+{% for c in clients %}
{{ "%-15s"|format(c.name) }} {{ "%-21s"|format(c.remote) }} {{ "%-21s"|format(local) }} {{ "%-9s"|format(c.tx_bytes) }} {{ "%-9s"|format(c.rx_bytes) }} {{ c.online_since }}
-{%- endfor %}
+{% endfor %}
{% endif %}
"""
diff --git a/src/op_mode/show_usb_serial.py b/src/op_mode/show_usb_serial.py
index 776898c25..973bf19c8 100755
--- a/src/op_mode/show_usb_serial.py
+++ b/src/op_mode/show_usb_serial.py
@@ -22,9 +22,9 @@ from sys import exit
OUT_TMPL_SRC = """Device Model Vendor
------ ------ ------
-{%- for d in devices %}
+{% for d in devices %}
{{ "%-16s" | format(d.device) }} {{ "%-19s" | format(d.model)}} {{d.vendor}}
-{%- endfor %}
+{% endfor %}
"""
diff --git a/src/op_mode/show_vrf.py b/src/op_mode/show_vrf.py
index b6bb73d01..94358c6e4 100755
--- a/src/op_mode/show_vrf.py
+++ b/src/op_mode/show_vrf.py
@@ -23,9 +23,9 @@ from vyos.util import cmd
vrf_out_tmpl = """
VRF name state mac address flags interfaces
-------- ----- ----------- ----- ----------
-{%- for v in vrf %}
+{% for v in vrf %}
{{"%-16s"|format(v.ifname)}} {{ "%-8s"|format(v.operstate | lower())}} {{"%-17s"|format(v.address | lower())}} {{ v.flags|join(',')|lower()}} {{v.members|join(',')|lower()}}
-{%- endfor %}
+{% endfor %}
"""
diff --git a/src/op_mode/show_wireless.py b/src/op_mode/show_wireless.py
index b5ee3aee1..19ab6771c 100755
--- a/src/op_mode/show_wireless.py
+++ b/src/op_mode/show_wireless.py
@@ -28,7 +28,6 @@ parser.add_argument("-s", "--scan", help="Scan for Wireless APs on given interfa
parser.add_argument("-b", "--brief", action="store_true", help="Show wireless configuration")
parser.add_argument("-c", "--stations", help="Show wireless clients connected on interface, e.g. 'wlan0'")
-
def show_brief():
config = Config()
if len(config.list_effective_nodes('interfaces wireless')) == 0:
@@ -37,17 +36,11 @@ def show_brief():
interfaces = []
for intf in config.list_effective_nodes('interfaces wireless'):
- config.set_level('interfaces wireless {}'.format(intf))
- data = {
- 'name': intf,
- 'type': '',
- 'ssid': '',
- 'channel': ''
- }
- data['type'] = config.return_effective_value('type')
- data['ssid'] = config.return_effective_value('ssid')
- data['channel'] = config.return_effective_value('channel')
-
+ config.set_level(f'interfaces wireless {intf}')
+ data = { 'name': intf }
+ data['type'] = config.return_effective_value('type') or '-'
+ data['ssid'] = config.return_effective_value('ssid') or '-'
+ data['channel'] = config.return_effective_value('channel') or '-'
interfaces.append(data)
return interfaces
diff --git a/src/op_mode/snmp_v3.py b/src/op_mode/snmp_v3.py
index 92601f15e..a1f76f0bc 100755
--- a/src/op_mode/snmp_v3.py
+++ b/src/op_mode/snmp_v3.py
@@ -37,7 +37,7 @@ SNMPv3 Groups:
Group View
----- ----
- {% if group -%}{% for g in group -%}
+ {% if group %}{% for g in group %}
{{ "%-20s" | format(g.name) }}{{ g.view }}({{ g.mode }})
{% endfor %}{% endif %}
"""
@@ -47,7 +47,7 @@ SNMPv3 Trap-targets:
Tpap-target Port Protocol Auth Priv Type EngineID User
----------- ---- -------- ---- ---- ---- -------- ----
- {% if trap -%}{% for t in trap -%}
+ {% if trap %}{% for t in trap %}
{{ "%-20s" | format(t.name) }} {{ t.port }} {{ t.proto }} {{ t.auth }} {{ t.priv }} {{ t.type }} {{ "%-32s" | format(t.engID) }} {{ t.user }}
{% endfor %}{% endif %}
"""
@@ -57,14 +57,14 @@ SNMPv3 Users:
User Auth Priv Mode Group
---- ---- ---- ---- -----
- {% if user -%}{% for u in user -%}
+ {% if user %}{% for u in user %}
{{ "%-20s" | format(u.name) }}{{ u.auth }} {{ u.priv }} {{ u.mode }} {{ u.group }}
{% endfor %}{% endif %}
"""
VIEW_OUTP_TMPL_SRC = """
SNMPv3 Views:
- {% if view -%}{% for v in view %}
+ {% if view %}{% for v in view %}
View : {{ v.name }}
OIDs : .{{ v.oids | join("\n .")}}
{% endfor %}{% endif %}
diff --git a/src/systemd/isc-dhcp-relay.service b/src/systemd/isc-dhcp-relay.service
index 56bcec840..de2e51a87 100644
--- a/src/systemd/isc-dhcp-relay.service
+++ b/src/systemd/isc-dhcp-relay.service
@@ -3,7 +3,7 @@ Description=ISC DHCP IPv4 relay
Documentation=man:dhcrelay(8)
Wants=network-online.target
RequiresMountsFor=/run
-ConditionPathExists=/run/dhcp-relay/dhcp.conf
+ConditionPathExists=/run/dhcp-relay/dhcrelay.conf
After=vyos-router.service
[Service]
@@ -11,7 +11,7 @@ Type=forking
WorkingDirectory=/run/dhcp-relay
RuntimeDirectory=dhcp-relay
RuntimeDirectoryPreserve=yes
-EnvironmentFile=/run/dhcp-relay/dhcp.conf
+EnvironmentFile=/run/dhcp-relay/dhcrelay.conf
PIDFile=/run/dhcp-relay/dhcrelay.pid
ExecStart=/usr/sbin/dhcrelay -4 -pf /run/dhcp-relay/dhcrelay.pid $OPTIONS
Restart=always
diff --git a/src/systemd/isc-dhcp-relay6.service b/src/systemd/isc-dhcp-relay6.service
index 85ff16e41..30037e013 100644
--- a/src/systemd/isc-dhcp-relay6.service
+++ b/src/systemd/isc-dhcp-relay6.service
@@ -3,7 +3,7 @@ Description=ISC DHCP IPv6 relay
Documentation=man:dhcrelay(8)
Wants=network-online.target
RequiresMountsFor=/run
-ConditionPathExists=/run/dhcp-relay/dhcpv6.conf
+ConditionPathExists=/run/dhcp-relay/dhcrelay6.conf
After=vyos-router.service
[Service]
@@ -11,9 +11,9 @@ Type=forking
WorkingDirectory=/run/dhcp-relay
RuntimeDirectory=dhcp-relay
RuntimeDirectoryPreserve=yes
-EnvironmentFile=/run/dhcp-relay/dhcpv6.conf
-PIDFile=/run/dhcp-relay/dhcrelayv6.pid
-ExecStart=/usr/sbin/dhcrelay -6 -pf /run/dhcp-relay/dhcrelayv6.pid $OPTIONS
+EnvironmentFile=/run/dhcp-relay/dhcrelay6.conf
+PIDFile=/run/dhcp-relay/dhcrelay6.pid
+ExecStart=/usr/sbin/dhcrelay -6 -pf /run/dhcp-relay/dhcrelay6.pid $OPTIONS
Restart=always
[Install]