summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/PULL_REQUEST_TEMPLATE.md6
-rw-r--r--CONTRIBUTING.md94
-rw-r--r--Makefile2
-rw-r--r--data/templates/dhcp-relay/dhcrelay.conf.tmpl2
-rw-r--r--data/templates/dhcp-relay/dhcrelay6.conf.tmpl21
-rw-r--r--data/templates/dhcp-server/dhcpd.conf.tmpl165
-rw-r--r--data/templates/dhcp-server/dhcpdv6.conf.tmpl (renamed from data/templates/dhcpv6-server/dhcpdv6.conf.tmpl)0
-rw-r--r--data/templates/dhcpv6-relay/config.tmpl4
-rw-r--r--data/templates/frr/isis.frr.tmpl7
-rw-r--r--data/templates/vyos-hostsd/hosts.tmpl14
-rw-r--r--interface-definitions/dhcpv6-relay.xml.in1
-rw-r--r--interface-definitions/include/vpn-ipsec-encryption.xml.i233
-rw-r--r--interface-definitions/include/vpn-ipsec-hash.xml.i65
-rw-r--r--interface-definitions/protocols-isis.xml.in12
-rw-r--r--interface-definitions/vpn_ipsec.xml.in1188
-rw-r--r--op-mode-definitions/show-ip-route.xml6
-rw-r--r--python/vyos/frr.py182
-rw-r--r--python/vyos/limericks.py16
-rwxr-xr-xsmoketest/scripts/cli/test_configd_inspect.py7
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcp-relay.py8
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcp-server.py295
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcpv6-relay.py112
-rwxr-xr-xsrc/conf_mode/dhcpv6_relay.py94
-rwxr-xr-xsrc/conf_mode/dhcpv6_server.py2
-rwxr-xr-xsrc/conf_mode/nat.py2
-rwxr-xr-xsrc/conf_mode/protocols_igmp.py5
-rwxr-xr-xsrc/conf_mode/protocols_isis.py39
-rwxr-xr-xsrc/conf_mode/protocols_mpls.py36
-rwxr-xr-xsrc/conf_mode/protocols_pim.py5
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py67
-rwxr-xr-xsrc/op_mode/lldp_op.py96
-rw-r--r--src/systemd/isc-dhcp-relay6.service8
32 files changed, 2511 insertions, 283 deletions
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index adbe21090..cc5e2f536 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,11 +1,12 @@
<!-- All PR should follow this template to allow a clean and transparent review -->
-<!-- Text placed between these delimiters is considered a commend and not rendered -->
+<!-- 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: -->
+<!--- 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)
@@ -17,6 +18,7 @@
<!-- 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 -->
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 efcb61dfc..cd66809e3 100644
--- a/Makefile
+++ b/Makefile
@@ -79,12 +79,12 @@ interface_definitions: $(BUILD_DIR) $(obj)
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 -rf $(TMPL_DIR)/protocols/isis
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
+ rm -rf $(TMPL_DIR)/vpn/nipsec
.PHONY: op_mode_definitions
.ONESHELL:
diff --git a/data/templates/dhcp-relay/dhcrelay.conf.tmpl b/data/templates/dhcp-relay/dhcrelay.conf.tmpl
index df990207b..a9d17ed9a 100644
--- a/data/templates/dhcp-relay/dhcrelay.conf.tmpl
+++ b/data/templates/dhcp-relay/dhcrelay.conf.tmpl
@@ -2,5 +2,5 @@
{% 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="-4 -c {{ relay_options.hop_count }} -a -m {{ relay_options.relay_agents_packets }} {{ max_size }} -i {{ interface | join(' -i ') }} {{ server | join(' ') }}"
+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 f431d3207..d172018bf 100644
--- a/data/templates/dhcp-server/dhcpd.conf.tmpl
+++ b/data/templates/dhcp-server/dhcpd.conf.tmpl
@@ -1,4 +1,3 @@
-
### Autogenerated by dhcp_server.py ###
# For options please consult the following website:
@@ -23,10 +22,12 @@ on expiry {
execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", ClientName, ClientIp, ClientMac, ClientDomain);
}
{% endif %}
+
{% if host_decl_name %}
use-host-decl-names on;
{% endif %}
-ddns-update-style {% if ddns_enable %} interim {% else %} none {% endif %};
+
+ddns-update-style {{ 'interim' if ddns_enable else 'none' }};
{% if static_route %}
option rfc3442-static-route code 121 = array of integer 8;
option windows-static-route code 249 = array of integer 8;
@@ -37,24 +38,24 @@ option wpad-url code 252 = text;
{% 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 %}
+{% 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,109 +64,106 @@ 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 %}
+{% for network in shared_network 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 %}
- {% if subnet.lease %}
+{% 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 {{ host.name if host_decl_name else network.name + '_' + host.name }} {
+{% 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 %}
+{% endfor %}
+{% if subnet.failover_name %}
pool {
failover peer "{{ subnet.failover_name }}";
deny dynamic bootp clients;
@@ -173,23 +171,22 @@ shared-network {{ network.name }} {
range {{ range.start }} {{ range.stop }};
{% 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 %}
{% endfor %}
diff --git a/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl b/data/templates/dhcp-server/dhcpdv6.conf.tmpl
index aa6d7fb5d..aa6d7fb5d 100644
--- a/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl
+++ b/data/templates/dhcp-server/dhcpdv6.conf.tmpl
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/frr/isis.frr.tmpl b/data/templates/frr/isis.frr.tmpl
index 929f5bdb2..a1dae0c7c 100644
--- a/data/templates/frr/isis.frr.tmpl
+++ b/data/templates/frr/isis.frr.tmpl
@@ -70,13 +70,6 @@ router isis {{ process }}
{% endif %}
{% endif %}
!
-{% if interface_remove is defined and interface_remove is not none %}
-{% for iface in interface_remove %}
-interface {{ iface }}
- no ip router isis
-{% endfor %}
-{% endif %}
-!
{% if interface is defined and interface is not none %}
{% for iface, iface_config in interface.items() %}
interface {{ iface }}
diff --git a/data/templates/vyos-hostsd/hosts.tmpl b/data/templates/vyos-hostsd/hosts.tmpl
index be8692104..8b73c6e51 100644
--- a/data/templates/vyos-hostsd/hosts.tmpl
+++ b/data/templates/vyos-hostsd/hosts.tmpl
@@ -12,15 +12,13 @@ ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
-{% if hosts %}
+{% if hosts is defined and hosts is not none %}
# 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'] %}
-{{ hostprops['address'] }} {{ host }}{% for a in hostprops['aliases'] %} {{ a }}{% endfor %}
-{% endif %}
-{% endfor %}
-{% endfor %}
+{% for host, hostprops in taghosts.items() if hostprops.address is defined %}
+{{ "%-15s" | format(hostprops.address) }} {{ host }} {{ hostprops.aliases|join(' ') if hostprops.aliases is defined }}
+{% endfor %}
+{% endfor %}
{% endif %}
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/include/vpn-ipsec-encryption.xml.i b/interface-definitions/include/vpn-ipsec-encryption.xml.i
new file mode 100644
index 000000000..1c1d728fc
--- /dev/null
+++ b/interface-definitions/include/vpn-ipsec-encryption.xml.i
@@ -0,0 +1,233 @@
+<!-- included start from vpn-ipsec-encryption.xml.i -->
+ <leafNode name="encryption">
+ <properties>
+ <help>Encryption algorithm</help>
+ <completionHelp>
+ <list>null aes128 aes192 aes256 aes128ctr aes192ctr aes256ctr aes128ccm64 aes192ccm64 aes256ccm64 aes128ccm96 aes192ccm96 aes256ccm96 aes128ccm128 aes192ccm128 aes256ccm128 aes128gcm64 aes192gcm64 aes256gcm64 aes128gcm96 aes192gcm96 aes256gcm96 aes128gcm128 aes192gcm128 aes256gcm128 aes128gmac aes192gmac aes256gmac 3des blowfish128 blowfish192 blowfish256 camellia128 camellia192 camellia256 camellia128ctr camellia192ctr camellia256ctr camellia128ccm64 camellia192ccm64 camellia256ccm64 camellia128ccm96 camellia192ccm96 camellia256ccm96 camellia128ccm128 camellia192ccm128 camellia256ccm128 serpent128 serpent192 serpent256 twofish128 twofish192 twofish256 cast128 chacha20poly1305</list>
+ </completionHelp>
+ <valueHelp>
+ <format>null</format>
+ <description>Null encryption</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes128</format>
+ <description>128 bit AES-CBC (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes192</format>
+ <description>192 bit AES-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes256</format>
+ <description>256 bit AES-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes128ctr</format>
+ <description>128 bit AES-COUNTER</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes192ctr</format>
+ <description>192 bit AES-COUNTER</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes256ctr</format>
+ <description>256 bit AES-COUNTER</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes128ccm64</format>
+ <description>128 bit AES-CCM with 64 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes192ccm64</format>
+ <description>192 bit AES-CCM with 64 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes256ccm64</format>
+ <description>256 bit AES-CCM with 64 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes128ccm96</format>
+ <description>128 bit AES-CCM with 96 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes192ccm96</format>
+ <description>192 bit AES-CCM with 96 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes256ccm96</format>
+ <description>256 bit AES-CCM with 96 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes128ccm128</format>
+ <description>128 bit AES-CCM with 128 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes192ccm128</format>
+ <description>192 bit AES-CCM with 128 bit IC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes256ccm128</format>
+ <description>256 bit AES-CCM with 128 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes128gcm64</format>
+ <description>128 bit AES-GCM with 64 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes192gcm64</format>
+ <description>192 bit AES-GCM with 64 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes256gcm64</format>
+ <description>256 bit AES-GCM with 64 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes128gcm96</format>
+ <description>128 bit AES-GCM with 96 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes192gcm96</format>
+ <description>192 bit AES-GCM with 96 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes256gcm96</format>
+ <description>256 bit AES-GCM with 96 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes128gcm128</format>
+ <description>128 bit AES-GCM with 128 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes192gcm128</format>
+ <description>192 bit AES-GCM with 128 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes256gcm128</format>
+ <description>256 bit AES-GCM with 128 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes128gmac</format>
+ <description>Null encryption with 128 bit AES-GMAC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes192gmac</format>
+ <description>Null encryption with 192 bit AES-GMAC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes256gmac</format>
+ <description>Null encryption with 256 bit AES-GMAC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>3des</format>
+ <description>168 bit 3DES-EDE-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>blowfish128</format>
+ <description>128 bit Blowfish-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>blowfish192</format>
+ <description>192 bit Blowfish-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>blowfish256</format>
+ <description>256 bit Blowfish-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>camellia128</format>
+ <description>128 bit Camellia-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>camellia192</format>
+ <description>192 bit Camellia-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>camellia256</format>
+ <description>256 bit Camellia-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>camellia128ctr</format>
+ <description>128 bit Camellia-COUNTER</description>
+ </valueHelp>
+ <valueHelp>
+ <format>camellia192ctr</format>
+ <description>192 bit Camellia-COUNTER</description>
+ </valueHelp>
+ <valueHelp>
+ <format>camellia256ctr</format>
+ <description>256 bit Camellia-COUNTER</description>
+ </valueHelp>
+ <valueHelp>
+ <format>camellia128ccm64</format>
+ <description>128 bit Camellia-CCM with 64 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>camellia192ccm64</format>
+ <description>192 bit Camellia-CCM with 64 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>camellia256ccm64</format>
+ <description>256 bit Camellia-CCM with 64 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>camellia128ccm96</format>
+ <description>128 bit Camellia-CCM with 96 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>camellia192ccm96</format>
+ <description>192 bit Camellia-CCM with 96 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>camellia256ccm96</format>
+ <description>256 bit Camellia-CCM with 96 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>camellia128ccm128</format>
+ <description>128 bit Camellia-CCM with 128 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>camellia192ccm128</format>
+ <description>192 bit Camellia-CCM with 128 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>camellia256ccm128</format>
+ <description>256 bit Camellia-CCM with 128 bit ICV</description>
+ </valueHelp>
+ <valueHelp>
+ <format>serpent128</format>
+ <description>128 bit Serpent-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>serpent192</format>
+ <description>192 bit Serpent-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>serpent256</format>
+ <description>256 bit Serpent-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>twofish128</format>
+ <description>128 bit Twofish-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>twofish192</format>
+ <description>192 bit Twofish-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>twofish256</format>
+ <description>256 bit Twofish-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>cast128</format>
+ <description>128 bit CAST-CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>chacha20poly1305</format>
+ <description>256 bit ChaCha20/Poly1305 with 128 bit ICV</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(null|aes128|aes192|aes256|aes128ctr|aes192ctr|aes256ctr|aes128ccm64|aes192ccm64|aes256ccm64|aes128ccm96|aes192ccm96|aes256ccm96|aes128ccm128|aes192ccm128|aes256ccm128|aes128gcm64|aes192gcm64|aes256gcm64|aes128gcm96|aes192gcm96|aes256gcm96|aes128gcm128|aes192gcm128|aes256gcm128|aes128gmac|aes192gmac|aes256gmac|3des|blowfish128|blowfish192|blowfish256|camellia128|camellia192|camellia256|camellia128ctr|camellia192ctr|camellia256ctr|camellia128ccm64|camellia192ccm64|camellia256ccm64|camellia128ccm96|camellia192ccm96|camellia256ccm96|camellia128ccm128|camellia192ccm128|camellia256ccm128|serpent128|serpent192|serpent256|twofish128|twofish192|twofish256|cast128|chacha20poly1305)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+<!-- included end -->
diff --git a/interface-definitions/include/vpn-ipsec-hash.xml.i b/interface-definitions/include/vpn-ipsec-hash.xml.i
new file mode 100644
index 000000000..ca5976d27
--- /dev/null
+++ b/interface-definitions/include/vpn-ipsec-hash.xml.i
@@ -0,0 +1,65 @@
+<!-- included start from pn-ipsec-hash.xml.i -->
+ <leafNode name="hash">
+ <properties>
+ <help>Hash algorithm</help>
+ <completionHelp>
+ <list>md5 md5_128 sha1 sha1_160 sha256 sha256_96 sha384 sha512 aesxcbc aescmac aes128gmac aes192gmac aes256gmac</list>
+ </completionHelp>
+ <valueHelp>
+ <format>md5</format>
+ <description>MD5 HMAC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>md5_128</format>
+ <description>MD5_128 HMAC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>sha1</format>
+ <description>SHA1 HMAC (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>sha1_160</format>
+ <description>SHA1_160 HMAC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>sha256</format>
+ <description>SHA2_256_128 HMAC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>sha256_96</format>
+ <description>SHA2_256_96 HMAC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>sha384</format>
+ <description>SHA2_384_192 HMAC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>sha512</format>
+ <description>SHA2_512_256 HMAC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aesxcbc</format>
+ <description>AES XCBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aescmac</format>
+ <description>AES CMAC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes128gmac</format>
+ <description>128-bit AES-GMAC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes192gmac</format>
+ <description>192-bit AES-GMAC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes256gmac</format>
+ <description>256-bit AES-GMAC</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(md5|md5_128|sha1|sha1_160|sha256|sha256_96|sha384|sha512|aesxcbc|aescmac|aes128gmac|aes192gmac|aes256gmac)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+<!-- included end -->
diff --git a/interface-definitions/protocols-isis.xml.in b/interface-definitions/protocols-isis.xml.in
index 988231108..c98b04220 100644
--- a/interface-definitions/protocols-isis.xml.in
+++ b/interface-definitions/protocols-isis.xml.in
@@ -5,7 +5,7 @@
<children>
<tagNode name="isis" owner="${vyos_conf_scripts_dir}/protocols_isis.py">
<properties>
- <help>Intermediate System to Intermediate System (ISIS)</help>
+ <help>Intermediate System to Intermediate System (IS-IS)</help>
<valueHelp>
<format>text(TAG)</format>
<description>ISO Routing area tag</description>
@@ -264,7 +264,7 @@
</node>
<node name="connected">
<properties>
- <help>Redistribute connected routes into ISIS</help>
+ <help>Redistribute connected routes into IS-IS</help>
</properties>
<children>
#include <include/isis-redistribute-ipv4.xml.i>
@@ -272,7 +272,7 @@
</node>
<node name="kernel">
<properties>
- <help>Redistribute kernel routes into ISIS</help>
+ <help>Redistribute kernel routes into IS-IS</help>
</properties>
<children>
#include <include/isis-redistribute-ipv4.xml.i>
@@ -280,7 +280,7 @@
</node>
<node name="ospf">
<properties>
- <help>Redistribute OSPF routes into ISIS</help>
+ <help>Redistribute OSPF routes into IS-IS</help>
</properties>
<children>
#include <include/isis-redistribute-ipv4.xml.i>
@@ -288,7 +288,7 @@
</node>
<node name="rip">
<properties>
- <help>Redistribute RIP routes into ISIS</help>
+ <help>Redistribute RIP routes into IS-IS</help>
</properties>
<children>
#include <include/isis-redistribute-ipv4.xml.i>
@@ -296,7 +296,7 @@
</node>
<node name="static">
<properties>
- <help>Redistribute static routes into ISIS</help>
+ <help>Redistribute static routes into IS-IS</help>
</properties>
<children>
#include <include/isis-redistribute-ipv4.xml.i>
diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in
new file mode 100644
index 000000000..93eb7e667
--- /dev/null
+++ b/interface-definitions/vpn_ipsec.xml.in
@@ -0,0 +1,1188 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="vpn">
+ <children>
+ <node name="nipsec" owner="${vyos_conf_scripts_dir}/vpn_ipsec.py">
+ <properties>
+ <help>VPN IP security (IPsec) parameters</help>
+ </properties>
+ <children>
+ <leafNode name="auto-update">
+ <properties>
+ <help>Set auto-update interval for IPsec daemon</help>
+ <valueHelp>
+ <format>30-65535</format>
+ <description>Auto-update interval (s)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 30-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="disable-uniqreqids">
+ <properties>
+ <help>Option to disable requirement for unique IDs in the Security Database</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <tagNode name="esp-group">
+ <properties>
+ <help>Name of Encapsulating Security Payload (ESP) group</help>
+ </properties>
+ <children>
+ <leafNode name="compression">
+ <properties>
+ <help>ESP compression</help>
+ <completionHelp>
+ <list>disable enable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable ESP compression (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable ESP compression</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(disable|enable)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="lifetime">
+ <properties>
+ <help>ESP lifetime</help>
+ <valueHelp>
+ <format>30-86400</format>
+ <description>ESP lifetime in seconds (default 3600)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 30-86400"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="mode">
+ <properties>
+ <help>ESP mode</help>
+ <completionHelp>
+ <list>tunnel transport</list>
+ </completionHelp>
+ <valueHelp>
+ <format>tunnel</format>
+ <description>Tunnel mode (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>transport</format>
+ <description>Transport mode</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(tunnel|transport)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="pfs">
+ <properties>
+ <help>ESP Perfect Forward Secrecy</help>
+ <completionHelp>
+ <list>enable dh-group1 dh-group2 dh-group5 dh-group14 dh-group15 dh-group16 dh-group17 dh-group18 dh-group19 dh-group20 dh-group21 dh-group22 dh-group23 dh-group24 dh-group25 dh-group26 dh-group27 dh-group28 dh-group29 dh-group30 dh-group31 dh-group32 disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable PFS. Use ike-groups dh-group (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group1</format>
+ <description>Enable PFS. Use Diffie-Hellman group 1 (modp768)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group2</format>
+ <description>Enable PFS. Use Diffie-Hellman group 2 (modp1024)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group5</format>
+ <description>Enable PFS. Use Diffie-Hellman group 5 (modp1536)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group14</format>
+ <description>Enable PFS. Use Diffie-Hellman group 14 (modp2048)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group15</format>
+ <description>Enable PFS. Use Diffie-Hellman group 15 (modp3072)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group16</format>
+ <description>Enable PFS. Use Diffie-Hellman group 16 (modp4096)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group17</format>
+ <description>Enable PFS. Use Diffie-Hellman group 17 (modp6144)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group18</format>
+ <description>Enable PFS. Use Diffie-Hellman group 18 (modp8192)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group19</format>
+ <description>Enable PFS. Use Diffie-Hellman group 19 (ecp256)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group20</format>
+ <description>Enable PFS. Use Diffie-Hellman group 20 (ecp384)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group21</format>
+ <description>Enable PFS. Use Diffie-Hellman group 21 (ecp521)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group22</format>
+ <description>Enable PFS. Use Diffie-Hellman group 22 (modp1024s160)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group23</format>
+ <description>Enable PFS. Use Diffie-Hellman group 23 (modp2048s224)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group24</format>
+ <description>Enable PFS. Use Diffie-Hellman group 24 (modp2048s256)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group25</format>
+ <description>Enable PFS. Use Diffie-Hellman group 25 (ecp192)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group26</format>
+ <description>Enable PFS. Use Diffie-Hellman group 26 (ecp224)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group27</format>
+ <description>Enable PFS. Use Diffie-Hellman group 27 (ecp224bp)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group28</format>
+ <description>Enable PFS. Use Diffie-Hellman group 28 (ecp256bp)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group29</format>
+ <description>Enable PFS. Use Diffie-Hellman group 29 (ecp384bp)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group30</format>
+ <description>Enable PFS. Use Diffie-Hellman group 30 (ecp512bp)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group31</format>
+ <description>Enable PFS. Use Diffie-Hellman group 31 (curve25519)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dh-group32</format>
+ <description>Enable PFS. Use Diffie-Hellman group 32 (curve448)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable PFS</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(enable|dh-group1|dh-group2|dh-group5|dh-group14|dh-group15|dh-group16|dh-group17|dh-group18|dh-group19|dh-group20|dh-group21|dh-group22|dh-group23|dh-group24|dh-group25|dh-group26|dh-group27|dh-group28|dh-group29|dh-group30|dh-group31|dh-group32|disable)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <tagNode name="proposal">
+ <properties>
+ <help>ESP-group proposal [REQUIRED]</help>
+ <valueHelp>
+ <format>&lt;1-65535&gt;</format>
+ <description>ESP-group proposal number</description>
+ </valueHelp>
+ </properties>
+ <children>
+ #include <include/vpn-ipsec-encryption.xml.i>
+ #include <include/vpn-ipsec-hash.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </tagNode>
+ <tagNode name="ike-group">
+ <properties>
+ <help>Name of Internet Key Exchange (IKE) group</help>
+ </properties>
+ <children>
+ <leafNode name="close-action">
+ <properties>
+ <help>close-action_help</help>
+ <completionHelp>
+ <list>none hold clear restart</list>
+ </completionHelp>
+ <valueHelp>
+ <format>none</format>
+ <description>Set action to none (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>hold</format>
+ <description>Set action to hold</description>
+ </valueHelp>
+ <valueHelp>
+ <format>clear</format>
+ <description>Set action to clear</description>
+ </valueHelp>
+ <valueHelp>
+ <format>restart</format>
+ <description>Set action to restart</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(none|hold|clear|restart)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <node name="dead-peer-detection">
+ <properties>
+ <help>Dead Peer Detection (DPD)</help>
+ </properties>
+ <children>
+ <leafNode name="action">
+ <properties>
+ <help>Keep-alive failure action</help>
+ <completionHelp>
+ <list>hold clear restart</list>
+ </completionHelp>
+ <valueHelp>
+ <format>hold</format>
+ <description>Set action to hold (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>clear</format>
+ <description>Set action to clear</description>
+ </valueHelp>
+ <valueHelp>
+ <format>restart</format>
+ <description>Set action to restart</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(hold|clear|restart)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="interval">
+ <properties>
+ <help>Keep-alive interval</help>
+ <valueHelp>
+ <format>&lt;2-86400&gt;</format>
+ <description>Keep-alive interval in seconds (default 30)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 2-86400"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="timeout">
+ <properties>
+ <help>Dead-Peer-Detection keep-alive timeout (IKEv1 only)</help>
+ <valueHelp>
+ <format>&lt;2-86400&gt;</format>
+ <description>Keep-alive timeout in seconds (default 120)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 2-86400"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="ikev2-reauth">
+ <properties>
+ <help>ikev2-reauth_help</help>
+ <completionHelp>
+ <list>yes no</list>
+ </completionHelp>
+ <valueHelp>
+ <format>yes</format>
+ <description>Enable remote host re-autentication during an IKE rekey. Currently broken due to a strong swan bug</description>
+ </valueHelp>
+ <valueHelp>
+ <format>no</format>
+ <description>Disable remote host re-authenticaton during an IKE rekey. (Default)</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(yes|no)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="key-exchange">
+ <properties>
+ <help>Key Exchange Version</help>
+ <completionHelp>
+ <list>ikev1 ikev2</list>
+ </completionHelp>
+ <valueHelp>
+ <format>ikev1</format>
+ <description>Use IKEv1 for Key Exchange [DEFAULT]</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ikev2</format>
+ <description>Use IKEv2 for Key Exchange</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(ikev1|ikev2)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="lifetime">
+ <properties>
+ <help>IKE lifetime</help>
+ <valueHelp>
+ <format>&lt;30-86400&gt;</format>
+ <description>IKE lifetime in seconds (default 28800)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 30-86400"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="mobike">
+ <properties>
+ <help>Enable MOBIKE Support. MOBIKE is only available for IKEv2.</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable MOBIKE (default for IKEv2)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable MOBIKE</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(enable|disable)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="mode">
+ <properties>
+ <help>IKEv1 Phase 1 Mode Selection</help>
+ <completionHelp>
+ <list>main aggressive</list>
+ </completionHelp>
+ <valueHelp>
+ <format>main</format>
+ <description>Use Main mode for Key Exchanges in the IKEv1 Protocol (Recommended Default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aggressive</format>
+ <description>Use Aggressive mode for Key Exchanges in the IKEv1 protocol - We do not recommend users to use aggressive mode as it is much more insecure compared to Main mode.</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(main|aggressive)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <tagNode name="proposal">
+ <properties>
+ <help>proposal_help</help>
+ <valueHelp>
+ <format>&lt;1-65535&gt;</format>
+ <description>IKE-group proposal</description>
+ </valueHelp>
+ </properties>
+ <children>
+ <leafNode name="dh-group">
+ <properties>
+ <help>dh-grouphelp</help>
+ <completionHelp>
+ <list>1 2 5 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32</list>
+ </completionHelp>
+ <valueHelp>
+ <format>1</format>
+ <description>Diffie-Hellman group 1 (modp768)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>2</format>
+ <description>Diffie-Hellman group 2 (modp1024)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>5</format>
+ <description>Diffie-Hellman group 5 (modp1536)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>14</format>
+ <description>Diffie-Hellman group 14 (modp2048)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>15</format>
+ <description>Diffie-Hellman group 15 (modp3072)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>16</format>
+ <description>Diffie-Hellman group 16 (modp4096)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>17</format>
+ <description>Diffie-Hellman group 17 (modp6144)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>18</format>
+ <description>Diffie-Hellman group 18 (modp8192)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>19</format>
+ <description>Diffie-Hellman group 19 (ecp256)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>20</format>
+ <description>Diffie-Hellman group 20 (ecp384)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>21</format>
+ <description>Diffie-Hellman group 21 (ecp521)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>22</format>
+ <description>Diffie-Hellman group 22 (modp1024s160)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>23</format>
+ <description>Diffie-Hellman group 23 (modp2048s224)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>24</format>
+ <description>Diffie-Hellman group 24 (modp2048s256)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>25</format>
+ <description>Diffie-Hellman group 25 (ecp192)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>26</format>
+ <description>Diffie-Hellman group 26 (ecp224)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>27</format>
+ <description>Diffie-Hellman group 27 (ecp224bp)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>28</format>
+ <description>Diffie-Hellman group 28 (ecp256bp)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>29</format>
+ <description>Diffie-Hellman group 29 (ecp384bp)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>30</format>
+ <description>Diffie-Hellman group 30 (ecp512bp)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>31</format>
+ <description>Diffie-Hellman group 31 (curve25519)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>32</format>
+ <description>Diffie-Hellman group 32 (curve448)</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(1|2|5|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/vpn-ipsec-encryption.xml.i>
+ #include <include/vpn-ipsec-hash.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </tagNode>
+ <leafNode name="include-ipsec-conf">
+ <properties>
+ <help>Sets to include an additional configuration directive file for strongSwan. Use an absolute path to specify the included file</help>
+ </properties>
+ </leafNode>
+ <leafNode name="include-ipsec-secrets">
+ <properties>
+ <help>Sets to include an additional secrets file for strongSwan. Use an absolute path to specify the included file.</help>
+ </properties>
+ </leafNode>
+ <node name="ipsec-interfaces">
+ <properties>
+ <help>Interface to use for VPN [REQUIRED]</help>
+ </properties>
+ <children>
+ <leafNode name="interface">
+ <properties>
+ <help>IPsec interface [REQUIRED]</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="logging">
+ <properties>
+ <help>IPsec logging</help>
+ </properties>
+ <children>
+ <leafNode name="log-level">
+ <properties>
+ <help>strongSwan Logger Level</help>
+ <valueHelp>
+ <format>&lt;0-2&gt;</format>
+ <description>Logger Verbosity Level (default 0)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-2"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="log-modes">
+ <properties>
+ <help>Log mode. To see what each log mode exactly does, please refer to the strongSwan documentation</help>
+ <completionHelp>
+ <list>dmn mgr ike chd job cfg knl net asn enc lib esp tls tnc imc imv pts any</list>
+ </completionHelp>
+ <valueHelp>
+ <format>dmn</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>mgr</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ike</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>chd</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>job</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>cfg</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>knl</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>net</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>asn</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>enc</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>lib</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>esp</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>tls</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>tnc</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>imc</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>imv</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>pts</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <valueHelp>
+ <format>any</format>
+ <description>Debug log option for strongSwan</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(dmn|mgr|ike|chd|job|cfg|knl|net|asn|enc|lib|esp|tls|tnc|imc|imv|pts|any)$</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="nat-networks">
+ <properties>
+ <help>Network Address Translation (NAT) networks</help>
+ </properties>
+ <children>
+ <tagNode name="allowed-network">
+ <properties>
+ <help>NAT networks to allow</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>NAT networks to allow</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-prefix"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="exclude">
+ <properties>
+ <help>NAT networks to exclude from allowed-networks</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>NAT networks to exclude from allowed-networks</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-prefix"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ <leafNode name="nat-traversal">
+ <properties>
+ <help>Network Address Translation (NAT) traversal</help>
+ <completionHelp>
+ <list>disable enable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable NAT-T</description>
+ </valueHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable NAT-T</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(disable|enable)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <node name="options">
+ <properties>
+ <help>Global IPsec settings</help>
+ </properties>
+ <children>
+ <leafNode name="disable-route-autoinstall">
+ <properties>
+ <help>Do not automatically install routes to remote networks</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <tagNode name="profile">
+ <properties>
+ <help>VPN IPSec Profile</help>
+ </properties>
+ <children>
+ <node name="authentication">
+ <properties>
+ <help>Authentication [REQUIRED]</help>
+ </properties>
+ <children>
+ <node name="mode">
+ <properties>
+ <help>Authentication mode</help>
+ </properties>
+ <children>
+ <leafNode name="pre-shared-secret">
+ <properties>
+ <help>Use pre-shared secret key</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="pre-shared-secret">
+ <properties>
+ <help>Pre-shared secret key</help>
+ <valueHelp>
+ <format>&lt;text&gt;</format>
+ <description>Pre-shared secret key</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="bind">
+ <properties>
+ <help>DMVPN crypto configuration</help>
+ </properties>
+ <children>
+ <leafNode name="bind_child">
+ <properties>
+ <help>bind_child_help</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="esp-group">
+ <properties>
+ <help>Esp group name [REQUIRED]</help>
+ <completionHelp>
+ <path>vpn ipsec esp-group</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="ike-group">
+ <properties>
+ <help>Ike group name [REQUIRED]</help>
+ <completionHelp>
+ <path>vpn ipsec ike-group</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ <node name="site-to-site">
+ <properties>
+ <help>Site to site VPN</help>
+ </properties>
+ <children>
+ <tagNode name="peer">
+ <properties>
+ <help>VPN peer</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address of the peer</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address of the peer</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;text&gt;</format>
+ <description>Hostname of the peer</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;@text&gt;</format>
+ <description>ID of the peer</description>
+ </valueHelp>
+ </properties>
+ <children>
+ <node name="authentication">
+ <properties>
+ <help>Peer authentication [REQUIRED]</help>
+ </properties>
+ <children>
+ <leafNode name="id">
+ <properties>
+ <help>ID for peer authentication</help>
+ <valueHelp>
+ <format>&lt;text&gt;</format>
+ <description>ID used for peer authentication</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="mode">
+ <properties>
+ <help>Authentication mode</help>
+ <completionHelp>
+ <list>pre-shared-secret rsa x509</list>
+ </completionHelp>
+ <valueHelp>
+ <format>pre-shared-secret</format>
+ <description>pre-shared-secret_description</description>
+ </valueHelp>
+ <valueHelp>
+ <format>rsa</format>
+ <description>rsa_description</description>
+ </valueHelp>
+ <valueHelp>
+ <format>x509</format>
+ <description>x509_description</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(pre-shared-secret|rsa|x509)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="pre-shared-secret">
+ <properties>
+ <help>Pre-shared secret key</help>
+ <valueHelp>
+ <format>&lt;text&gt;</format>
+ <description>Pre-shared secret key</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="remote-id">
+ <properties>
+ <help>ID for remote authentication</help>
+ <valueHelp>
+ <format>&lt;text&gt;</format>
+ <description>ID used for peer authentication</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="rsa-key-name">
+ <properties>
+ <help>RSA key name</help>
+ </properties>
+ </leafNode>
+ <leafNode name="use-x509-id">
+ <properties>
+ <help>Use certificate common name as ID</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="x509">
+ <properties>
+ <help>X.509 certificate</help>
+ </properties>
+ <children>
+ <leafNode name="ca-cert-file">
+ <properties>
+ <help>File containing the X.509 certificate for the Certificate Authority (CA)</help>
+ <valueHelp>
+ <format>&lt;text&gt;</format>
+ <description>File in /config/auth</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="cert-file">
+ <properties>
+ <help>File containing the X.509 certificate for this host</help>
+ <valueHelp>
+ <format>&lt;text&gt;</format>
+ <description>File in /config/auth</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="crl-file">
+ <properties>
+ <help>File containing the X.509 Certificate Revocation List (CRL)</help>
+ <valueHelp>
+ <format>&lt;text&gt;</format>
+ <description>File in /config/auth</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <node name="key">
+ <properties>
+ <help>Key file and password to open it</help>
+ </properties>
+ <children>
+ <leafNode name="file">
+ <properties>
+ <help>File containing the private key for the X.509 certificate for this host</help>
+ <valueHelp>
+ <format>&lt;text&gt;</format>
+ <description>File in /config/auth</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="password">
+ <properties>
+ <help>Password that protects the private key</help>
+ <valueHelp>
+ <format>&lt;text&gt;</format>
+ <description>Password that protects the private key</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+ <leafNode name="connection-type">
+ <properties>
+ <help>Connection type</help>
+ <completionHelp>
+ <list>initiate respond</list>
+ </completionHelp>
+ <valueHelp>
+ <format>initiate</format>
+ <description>initiate_description</description>
+ </valueHelp>
+ <valueHelp>
+ <format>respond</format>
+ <description>respond_description</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(initiate|respond)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="default-esp-group">
+ <properties>
+ <help>Defult ESP group name</help>
+ </properties>
+ </leafNode>
+ <leafNode name="description">
+ <properties>
+ <help>VPN peer description</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="dhcp-interface">
+ <properties>
+ <help>DHCP interface to listen on</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="force-encapsulation">
+ <properties>
+ <help>Force UDP Encapsulation for ESP Payloads</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>This endpoint will force UDP encapsulation for this peer</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>This endpoint will not force UDP encapsulation for this peer</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(enable|disable)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="ike-group">
+ <properties>
+ <help>Internet Key Exchange (IKE) group name [REQUIRED]</help>
+ <completionHelp>
+ <path>vpn ipsec ike-group</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="ikev2-reauth">
+ <properties>
+ <help>Re-authentication of the remote peer during an IKE re-key. IKEv2 option only</help>
+ <completionHelp>
+ <list>yes no inherit</list>
+ </completionHelp>
+ <valueHelp>
+ <format>yes</format>
+ <description>Enable remote host re-autentication during an IKE re-key. Currently broken due to a strong swan bug</description>
+ </valueHelp>
+ <valueHelp>
+ <format>no</format>
+ <description>Disable remote host re-authenticaton during an IKE re-key.</description>
+ </valueHelp>
+ <valueHelp>
+ <format>inherit</format>
+ <description>Inherit the reauth configuration form your IKE-group (Default)</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(yes|no|inherit)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="local-address">
+ <properties>
+ <help>IPv4 or IPv6 address of a local interface to use for VPN</help>
+ <completionHelp>
+ <list>any</list>
+ </completionHelp>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address of a local interface for VPN</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address of a local interface for VPN</description>
+ </valueHelp>
+ <valueHelp>
+ <format>any</format>
+ <description>Allow any IPv4 address present on the system to be used for VPN</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
+ <regex>^(any)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <tagNode name="tunnel">
+ <properties>
+ <help>Peer tunnel [REQUIRED]</help>
+ <valueHelp>
+ <format>&lt;0-4294967295&gt;</format>
+ <description>Peer tunnel [REQUIRED]</description>
+ </valueHelp>
+ </properties>
+ <children>
+ <leafNode name="allow-nat-networks">
+ <properties>
+ <help>Option to allow NAT networks</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable NAT networks</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable NAT networks (default)</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(enable|disable)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="allow-public-networks">
+ <properties>
+ <help>Option to allow public networks</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable public networks</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable public networks (default)</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(enable|disable)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="disable">
+ <properties>
+ <help>Option to disable vpn tunnel</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="esp-group">
+ <properties>
+ <help>ESP group name</help>
+ <completionHelp>
+ <path>vpn ipsec esp-group</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <node name="local">
+ <properties>
+ <help>Local parameters for interesting traffic</help>
+ </properties>
+ <children>
+ <leafNode name="port">
+ <properties>
+ <help>Any TCP or UDP port</help>
+ <valueHelp>
+ <format>&lt;port name&gt;</format>
+ <description>Named port (any name in /etc/services, e.g., http)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;1-65535&gt;</format>
+ <description>Numbered port</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="prefix">
+ <properties>
+ <help>Local IPv4 or IPv6 prefix</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Local IPv4 prefix</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Local IPv6 prefix</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ <validator name="ipv6-prefix"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="protocol">
+ <properties>
+ <help>Protocol to encrypt</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="remote">
+ <properties>
+ <help>Remote parameters for interesting traffic</help>
+ </properties>
+ <children>
+ <leafNode name="port">
+ <properties>
+ <help>Any TCP or UDP port</help>
+ <valueHelp>
+ <format>&lt;port name&gt;</format>
+ <description>Named port (any name in /etc/services, e.g., http)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;1-65535&gt;</format>
+ <description>Numbered port</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="prefix">
+ <properties>
+ <help>Remote IPv4 or IPv6 prefix</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Remote IPv4 prefix</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Remote IPv6 prefix</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ <validator name="ipv6-prefix"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ <node name="vti">
+ <properties>
+ <help>Virtual tunnel interface [REQUIRED]</help>
+ </properties>
+ <children>
+ <leafNode name="bind">
+ <properties>
+ <help>VTI tunnel interface associated with this configuration [REQUIRED]</help>
+ </properties>
+ </leafNode>
+ <leafNode name="esp-group">
+ <properties>
+ <help>ESP group name [REQUIRED]</help>
+ <completionHelp>
+ <path>vpn ipsec esp-group</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/show-ip-route.xml b/op-mode-definitions/show-ip-route.xml
index 48ebbc74a..a98048785 100644
--- a/op-mode-definitions/show-ip-route.xml
+++ b/op-mode-definitions/show-ip-route.xml
@@ -55,6 +55,12 @@
</properties>
<command>ip -s route list $5</command>
</tagNode>
+ <leafNode name="isis">
+ <properties>
+ <help>Show IP IS-IS routes</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip route isis"</command>
+ </leafNode>
<leafNode name="kernel">
<properties>
<help>Show IP kernel routes</help>
diff --git a/python/vyos/frr.py b/python/vyos/frr.py
index 3fc75bbdf..3bab64301 100644
--- a/python/vyos/frr.py
+++ b/python/vyos/frr.py
@@ -68,6 +68,9 @@ Apply the new configuration:
import tempfile
import re
from vyos import util
+import logging
+LOG = logging.getLogger(__name__)
+
_frr_daemons = ['zebra', 'bgpd', 'fabricd', 'isisd', 'ospf6d', 'ospfd', 'pbrd',
'pimd', 'ripd', 'ripngd', 'sharpd', 'staticd', 'vrrpd', 'ldpd']
@@ -250,6 +253,7 @@ def _replace_section(config, replacement, replace_re, before_re):
return: modified configuration as a text file
"""
+ # DEPRECATED, this is replaced by a new implementation
# Check if block is configured, remove the existing instance else add a new one
if re.findall(replace_re, config, flags=re.MULTILINE | re.DOTALL):
# Section is in the configration, replace it
@@ -281,8 +285,186 @@ def replace_section(config, replacement, from_re, to_re=r'!', before_re=r'line v
startline and endline tags will be automatically added to the resulting from_re/to_re and before_re regex'es
"""
+ # DEPRECATED, this is replaced by a new implementation
return _replace_section(config, replacement, replace_re=rf'^{from_re}$.*?^{to_re}$', before_re=rf'^({before_re})$')
def remove_section(config, from_re, to_re='!'):
+ # DEPRECATED, this is replaced by a new implementation
return _replace_section(config, '', replace_re=rf'^{from_re}$.*?^{to_re}$', before_re=None)
+
+
+def _find_first_block(config, start_pattern, stop_pattern, start_at=0):
+ '''Find start and stop line numbers for a config block
+ config: (list) A list conaining the configuration that is searched
+ start_pattern: (raw-str) The pattern searched for a a start of block tag
+ stop_pattern: (raw-str) The pattern searched for to signify the end of the block
+ start_at: (int) The index to start searching at in the <config>
+
+ Returns:
+ None: No complete block could be found
+ set(int, int): A complete block found between the line numbers returned in the set
+
+ The object <config> is searched from the start for the regex <start_pattern> until the first match is found.
+ On a successful match it continues the search for the regex <stop_pattern> until it is found.
+ After a successful run a set is returned containing the start and stop line numbers.
+ '''
+ LOG.debug(f'_find_first_block: find start={repr(start_pattern)} stop={repr(stop_pattern)} start_at={start_at}')
+ _start = None
+ for i, element in enumerate(config[start_at:], start=start_at):
+ # LOG.debug(f'_find_first_block: running line {i:3} "{element}"')
+ if not _start:
+ if not re.match(start_pattern, element):
+ LOG.debug(f'_find_first_block: no match {i:3} "{element}"')
+ continue
+ _start = i
+ LOG.debug(f'_find_first_block: Found start {i:3} "{element}"')
+ continue
+
+ if not re.match(stop_pattern, element):
+ LOG.debug(f'_find_first_block: no match {i:3} "{element}"')
+ continue
+
+ LOG.debug(f'_find_first_block: Found stop {i:3} "{element}"')
+ return (_start, i)
+
+ LOG.debug('_find_first_block: exit start={repr(start_pattern)} stop={repr(stop_pattern)} start_at={start_at}')
+ return None
+
+
+def _find_first_element(config, pattern, start_at=0):
+ '''Find the first element that matches the current pattern in config
+ config: (list) A list containing the configuration that is searched
+ start_pattern: (raw-str) The pattern searched for
+ start_at: (int) The index to start searching at in the <config>
+
+ return: Line index of the line containing the searched pattern
+
+ TODO: for now it returns -1 on a no-match because 0 also returns as False
+ TODO: that means that we can not use False matching to tell if its
+ '''
+ LOG.debug(f'_find_first_element: find start="{pattern}" start_at={start_at}')
+ for i, element in enumerate(config[start_at:], start=0):
+ if re.match(pattern + '$', element):
+ LOG.debug(f'_find_first_element: Found stop {i:3} "{element}"')
+ return i
+ LOG.debug(f'_find_first_element: no match {i:3} "{element}"')
+ LOG.debug(f'_find_first_element: Did not find any match, exiting')
+ return -1
+
+
+def _find_elements(config, pattern, start_at=0):
+ '''Find all instances of pattern and return a list containing all element indexes
+ config: (list) A list containing the configuration that is searched
+ start_pattern: (raw-str) The pattern searched for
+ start_at: (int) The index to start searching at in the <config>
+
+ return: A list of line indexes containing the searched pattern
+ TODO: refactor this to return a generator instead
+ '''
+ return [i for i, element in enumerate(config[start_at:], start=0) if re.match(pattern + '$', element)]
+
+
+class FRRConfig:
+ '''Main FRR Configuration manipulation object
+ Using this object the user could load, manipulate and commit the configuration to FRR
+ '''
+ def __init__(self, config=[]):
+ self.imported_config = ''
+
+ if isinstance(config, list):
+ self.config = config.copy()
+ self.original_config = config.copy()
+ elif isinstance(config, str):
+ self.config = config.split('\n')
+ self.original_config = self.config.copy()
+ else:
+ raise ValueError(
+ 'The config element needs to be a string or list type object')
+
+ def load_configuration(self, daemon=None):
+ '''Load the running configuration from FRR into the config object
+ daemon: str with name of the FRR Daemon to load configuration from or
+ None to load the consolidated config
+
+ Using this overwrites the current loaded config objects and replaces the original loaded config
+ '''
+ self.imported_config = get_configuration(daemon=daemon)
+ LOG.debug(f'load_configuration: Configuration loaded from FRR: {self.imported_config}')
+ self.original_config = self.imported_config.split('\n')
+ self.config = self.original_config.copy()
+ return
+
+ def test_configuration(self):
+ '''Test the current configuration against FRR
+ This will exception if FRR failes to load the current configuration object
+ '''
+ LOG.debug('test_configation: Testing configuration')
+ mark_configuration('\n'.join(self.config))
+
+ def commit_configuration(self, daemon=None):
+ '''Commit the current configuration to FRR
+ daemon: str with name of the FRR daemon to commit to or
+ None to use the consolidated config
+ '''
+ LOG.debug('commit_configuration: Commiting configuration')
+ reload_configuration('\n'.join(self.config), daemon=daemon)
+
+ def modify_section(self, start_pattern, replacement=[], stop_pattern=r'\S+', remove_stop_mark=False, count=0):
+ if isinstance(replacement, str):
+ replacement = replacement.split('\n')
+ elif not isinstance(replacement, list):
+ return ValueError("The replacement element needs to be a string or list type object")
+ LOG.debug(f'modify_section: starting search for {repr(start_pattern)} until {repr(stop_pattern)}')
+
+ _count = 0
+ _next_start = 0
+ while True:
+ if count and count <= _count:
+ # Break out of the loop after specified amount of matches
+ LOG.debug(f'modify_section: reached limit ({_count}), exiting loop at line {_next_start}')
+ break
+ # While searching, always assume that the user wants to search for the exact pattern he entered
+ # To be more specific the user needs a override, eg. a "pattern.*"
+ _w = _find_first_block(
+ self.config, start_pattern+'$', stop_pattern, start_at=_next_start)
+ if not _w:
+ # Reached the end, no more elements to remove
+ LOG.debug(f'modify_section: No more config sections found, exiting')
+ break
+ start_element, end_element = _w
+ LOG.debug(f'modify_section: found match between {start_element} and {end_element}')
+ for i, e in enumerate(self.config[start_element:end_element+1 if remove_stop_mark else end_element],
+ start=start_element):
+ LOG.debug(f'modify_section: remove {i:3} {e}')
+ del self.config[start_element:end_element +
+ 1 if remove_stop_mark else end_element]
+ if replacement:
+ # Append the replacement config at the current position
+ for i, e in enumerate(replacement, start=start_element):
+ LOG.debug(f'modify_section: add {i:3} {e}')
+ self.config[start_element:start_element] = replacement
+ _count += 1
+ _next_start = start_element + len(replacement)
+
+ return _count
+
+ def add_before(self, before_pattern, addition):
+ '''Add config block before this element in the configuration'''
+ if isinstance(addition, str):
+ addition = addition.split('\n')
+ elif not isinstance(addition, list):
+ return ValueError("The replacement element needs to be a string or list type object")
+
+ start = _find_first_element(self.config, before_pattern)
+ if start < 0:
+ return False
+
+ self.config[start:start] = addition
+ return True
+
+ def __str__(self):
+ return '\n'.join(self.config)
+
+ def __repr__(self):
+ return f'frr({repr(str(self))})'
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/smoketest/scripts/cli/test_configd_inspect.py b/smoketest/scripts/cli/test_configd_inspect.py
index 5181187e9..4ebee8cc5 100755
--- a/smoketest/scripts/cli/test_configd_inspect.py
+++ b/smoketest/scripts/cli/test_configd_inspect.py
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
+import re
import json
import unittest
import warnings
@@ -78,7 +79,8 @@ class TestConfigdInclude(unittest.TestCase):
if not f:
continue
str_f = getsource(f)
- n = str_f.count('Config()')
+ # Regex not XXXConfig() T3108
+ n = len(re.findall(r'[^a-zA-Z]Config\(\)', str_f))
if i == 'get_config':
self.assertEqual(n, 1,
f"'{s}': '{i}' no instance of Config")
@@ -91,7 +93,8 @@ class TestConfigdInclude(unittest.TestCase):
for s in self.inc_list:
m = import_script(s)
str_m = getsource(m)
- n = str_m.count('Config()')
+ # Regex not XXXConfig T3108
+ n = len(re.findall(r'[^a-zA-Z]Config\(\)', str_m))
self.assertEqual(n, 1,
f"'{s}' more than one instance of Config")
diff --git a/smoketest/scripts/cli/test_service_dhcp-relay.py b/smoketest/scripts/cli/test_service_dhcp-relay.py
index d56f6aa16..4b020db72 100755
--- a/smoketest/scripts/cli/test_service_dhcp-relay.py
+++ b/smoketest/scripts/cli/test_service_dhcp-relay.py
@@ -29,17 +29,9 @@ PROCESS_NAME = 'dhcrelay'
RELAY_CONF = '/run/dhcp-relay/dhcrelay.conf'
base_path = ['service', 'dhcp-relay']
-def get_config_value(key):
- tmp = read_file(SSHD_CONF)
- tmp = re.findall(f'\n?{key}\s+(.*)', tmp)
- return tmp
-
class TestServiceDHCPRelay(unittest.TestCase):
def setUp(self):
self.session = ConfigSession(os.getpid())
- # ensure we can also run this test on a live system - so lets clean
- # out the current configuration :)
- self.session.delete(base_path)
def tearDown(self):
self.session.delete(base_path)
diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py
new file mode 100755
index 000000000..4493534be
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_dhcp-server.py
@@ -0,0 +1,295 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019-2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import os
+import unittest
+
+from vyos.configsession import ConfigSession
+from vyos.configsession import ConfigSessionError
+from vyos.util import cmd
+from vyos.util import process_named_running
+from vyos.util import read_file
+from vyos.template import inc_ip
+from vyos.template import address_from_cidr
+from vyos.template import netmask_from_cidr
+
+PROCESS_NAME = 'dhcpd'
+DHCPD_CONF = '/run/dhcp-server/dhcpd.conf'
+base_path = ['service', 'dhcp-server']
+subnet = '192.0.2.0/25'
+router = inc_ip(subnet, 1)
+dns_1 = inc_ip(subnet, 2)
+dns_2 = inc_ip(subnet, 3)
+domain_name = 'vyos.net'
+
+class TestServiceDHCPServer(unittest.TestCase):
+ def setUp(self):
+ self.session = ConfigSession(os.getpid())
+ cidr_mask = subnet.split('/')[-1]
+ self.session.set(['interfaces', 'dummy', 'dum8765', 'address', f'{router}/{cidr_mask}'])
+
+ def tearDown(self):
+ self.session.delete(['interfaces', 'dummy', 'dum8765'])
+ self.session.delete(base_path)
+ self.session.commit()
+ del self.session
+
+ def test_single_pool_range(self):
+ shared_net_name = 'SMOKE-1'
+
+ range_0_start = inc_ip(subnet, 10)
+ range_0_stop = inc_ip(subnet, 20)
+ range_1_start = inc_ip(subnet, 40)
+ range_1_stop = inc_ip(subnet, 50)
+
+ self.session.set(base_path + ['dynamic-dns-update'])
+
+ pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
+ # we use the first subnet IP address as default gateway
+ self.session.set(pool + ['default-router', router])
+ self.session.set(pool + ['dns-server', dns_1])
+ self.session.set(pool + ['dns-server', dns_2])
+ self.session.set(pool + ['domain-name', domain_name])
+
+ # check validate() - No DHCP address range or active static-mapping set
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(pool + ['range', '0', 'start', range_0_start])
+ self.session.set(pool + ['range', '0', 'stop', range_0_stop])
+ self.session.set(pool + ['range', '1', 'start', range_1_start])
+ self.session.set(pool + ['range', '1', 'stop', range_1_stop])
+
+ # commit changes
+ self.session.commit()
+
+ config = read_file(DHCPD_CONF)
+ network = address_from_cidr(subnet)
+ netmask = netmask_from_cidr(subnet)
+ self.assertIn(f'ddns-update-style interim;', config)
+ self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config)
+ self.assertIn(f'option domain-name-servers {dns_1}, {dns_2};', config)
+ self.assertIn(f'option routers {router};', config)
+ self.assertIn(f'option domain-name "{domain_name}";', config)
+ self.assertIn(f'default-lease-time 86400;', config)
+ self.assertIn(f'max-lease-time 86400;', config)
+ self.assertIn(f'range {range_0_start} {range_0_stop};', config)
+ self.assertIn(f'range {range_1_start} {range_1_stop};', config)
+ self.assertIn(f'set shared-networkname = "{shared_net_name}";', config)
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ def test_single_pool_options(self):
+ shared_net_name = 'SMOKE-0815'
+
+ range_0_start = inc_ip(subnet, 10)
+ range_0_stop = inc_ip(subnet, 20)
+ smtp_server = '1.2.3.4'
+ time_server = '4.3.2.1'
+ tftp_server = 'tftp.vyos.io'
+ search_domain = 'foo.vyos.net'
+ bootfile_name = 'vyos'
+ bootfile_server = '192.0.2.1'
+ wpad = 'http://wpad.vyos.io/foo/bar'
+ server_identifier = bootfile_server
+
+ pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
+ # we use the first subnet IP address as default gateway
+ self.session.set(pool + ['default-router', router])
+ self.session.set(pool + ['dns-server', dns_1])
+ self.session.set(pool + ['dns-server', dns_2])
+ self.session.set(pool + ['domain-name', domain_name])
+ self.session.set(pool + ['ip-forwarding'])
+ self.session.set(pool + ['smtp-server', smtp_server])
+ self.session.set(pool + ['pop-server', smtp_server])
+ self.session.set(pool + ['time-server', time_server])
+ self.session.set(pool + ['tftp-server-name', tftp_server])
+ self.session.set(pool + ['domain-search', search_domain])
+ self.session.set(pool + ['bootfile-name', bootfile_name])
+ self.session.set(pool + ['bootfile-server', bootfile_server])
+ self.session.set(pool + ['wpad-url', wpad])
+ self.session.set(pool + ['server-identifier', server_identifier])
+
+ self.session.set(pool + ['static-route', 'destination-subnet', '10.0.0.0/24'])
+ self.session.set(pool + ['static-route', 'router', '192.0.2.1'])
+
+
+ # check validate() - No DHCP address range or active static-mapping set
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(pool + ['range', '0', 'start', range_0_start])
+ self.session.set(pool + ['range', '0', 'stop', range_0_stop])
+
+ # commit changes
+ self.session.commit()
+
+ config = read_file(DHCPD_CONF)
+ network = address_from_cidr(subnet)
+ netmask = netmask_from_cidr(subnet)
+ self.assertIn(f'ddns-update-style none;', config)
+ self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config)
+ self.assertIn(f'option domain-name-servers {dns_1}, {dns_2};', config)
+ self.assertIn(f'option routers {router};', config)
+ self.assertIn(f'option domain-name "{domain_name}";', config)
+ self.assertIn(f'option domain-search "{search_domain}";', config)
+ self.assertIn(f'option ip-forwarding true;', config)
+ self.assertIn(f'option smtp-server {smtp_server};', config)
+ self.assertIn(f'option pop-server {smtp_server};', config)
+ self.assertIn(f'option time-servers {time_server};', config)
+ self.assertIn(f'option wpad-url "{wpad}";', config)
+ self.assertIn(f'option dhcp-server-identifier {server_identifier};', config)
+ self.assertIn(f'option tftp-server-name "{tftp_server}";', config)
+ self.assertIn(f'option bootfile-name "{bootfile_name}";', config)
+ self.assertIn(f'filename "{bootfile_name}";', config)
+ self.assertIn(f'next-server {bootfile_server};', config)
+ self.assertIn(f'default-lease-time 86400;', config)
+ self.assertIn(f'max-lease-time 86400;', config)
+ self.assertIn(f'range {range_0_start} {range_0_stop};', config)
+ self.assertIn(f'set shared-networkname = "{shared_net_name}";', config)
+
+ # weird syntax for those static routes
+ self.assertIn(f'option rfc3442-static-route 24,10,0,0,192,0,2,1, 0,192,0,2,1;', config)
+ self.assertIn(f'option windows-static-route 24,10,0,0,192,0,2,1;', config)
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ def test_single_pool_static_mapping(self):
+ shared_net_name = 'SMOKE-2'
+
+ pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
+ # we use the first subnet IP address as default gateway
+ self.session.set(pool + ['default-router', router])
+ self.session.set(pool + ['dns-server', dns_1])
+ self.session.set(pool + ['dns-server', dns_2])
+ self.session.set(pool + ['domain-name', domain_name])
+
+ # check validate() - No DHCP address range or active static-mapping set
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+
+ client_base = 10
+ for client in ['client1', 'client2', 'client3']:
+ mac = '00:50:00:00:00:{}'.format(client_base)
+ self.session.set(pool + ['static-mapping', client, 'mac-address', mac])
+ self.session.set(pool + ['static-mapping', client, 'ip-address', inc_ip(subnet, client_base)])
+ client_base += 1
+
+ # commit changes
+ self.session.commit()
+
+ config = read_file(DHCPD_CONF)
+ network = address_from_cidr(subnet)
+ netmask = netmask_from_cidr(subnet)
+ self.assertIn(f'ddns-update-style none;', config)
+ self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config)
+ self.assertIn(f'option domain-name-servers {dns_1}, {dns_2};', config)
+ self.assertIn(f'option routers {router};', config)
+ self.assertIn(f'option domain-name "{domain_name}";', config)
+ self.assertIn(f'default-lease-time 86400;', config)
+ self.assertIn(f'max-lease-time 86400;', config)
+
+ client_base = 10
+ for client in ['client1', 'client2', 'client3']:
+ mac = '00:50:00:00:00:{}'.format(client_base)
+ ip = inc_ip(subnet, client_base)
+ self.assertIn(f'host {shared_net_name}_{client}' + ' {', config)
+ self.assertIn(f'fixed-address {ip};', config)
+ self.assertIn(f'hardware ethernet {mac};', config)
+ client_base += 1
+
+ self.assertIn(f'set shared-networkname = "{shared_net_name}";', config)
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ def test_multi_pools(self):
+ lease_time = '14400'
+
+ for network in ['0', '1', '2', '3']:
+ shared_net_name = f'VyOS-SMOKETEST-{network}'
+ subnet = f'192.0.{network}.0/24'
+ router = inc_ip(subnet, 1)
+ dns_1 = inc_ip(subnet, 2)
+
+ range_0_start = inc_ip(subnet, 10)
+ range_0_stop = inc_ip(subnet, 20)
+ range_1_start = inc_ip(subnet, 30)
+ range_1_stop = inc_ip(subnet, 40)
+
+ pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
+ # we use the first subnet IP address as default gateway
+ self.session.set(pool + ['default-router', router])
+ self.session.set(pool + ['dns-server', dns_1])
+ self.session.set(pool + ['domain-name', domain_name])
+ self.session.set(pool + ['lease', lease_time])
+
+ self.session.set(pool + ['range', '0', 'start', range_0_start])
+ self.session.set(pool + ['range', '0', 'stop', range_0_stop])
+ self.session.set(pool + ['range', '1', 'start', range_1_start])
+ self.session.set(pool + ['range', '1', 'stop', range_1_stop])
+
+ client_base = 60
+ for client in ['client1', 'client2', 'client3', 'client4']:
+ mac = '02:50:00:00:00:{}'.format(client_base)
+ self.session.set(pool + ['static-mapping', client, 'mac-address', mac])
+ self.session.set(pool + ['static-mapping', client, 'ip-address', inc_ip(subnet, client_base)])
+ client_base += 1
+
+ # commit changes
+ self.session.commit()
+
+ config = read_file(DHCPD_CONF)
+ for network in ['0', '1', '2', '3']:
+ shared_net_name = f'VyOS-SMOKETEST-{network}'
+ subnet = f'192.0.{network}.0/24'
+ router = inc_ip(subnet, 1)
+ dns_1 = inc_ip(subnet, 2)
+
+ range_0_start = inc_ip(subnet, 10)
+ range_0_stop = inc_ip(subnet, 20)
+ range_1_start = inc_ip(subnet, 30)
+ range_1_stop = inc_ip(subnet, 40)
+
+ network = address_from_cidr(subnet)
+ netmask = netmask_from_cidr(subnet)
+
+ self.assertIn(f'ddns-update-style none;', config)
+ self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config)
+ self.assertIn(f'option domain-name-servers {dns_1};', config)
+ self.assertIn(f'option routers {router};', config)
+ self.assertIn(f'option domain-name "{domain_name}";', config)
+ self.assertIn(f'default-lease-time {lease_time};', config)
+ self.assertIn(f'max-lease-time {lease_time};', config)
+ self.assertIn(f'range {range_0_start} {range_0_stop};', config)
+ self.assertIn(f'range {range_1_start} {range_1_stop};', config)
+ self.assertIn(f'set shared-networkname = "{shared_net_name}";', config)
+
+ client_base = 60
+ for client in ['client1', 'client2', 'client3', 'client4']:
+ mac = '02:50:00:00:00:{}'.format(client_base)
+ ip = inc_ip(subnet, client_base)
+ self.assertIn(f'host {shared_net_name}_{client}' + ' {', config)
+ self.assertIn(f'fixed-address {ip};', config)
+ self.assertIn(f'hardware ethernet {mac};', config)
+ client_base += 1
+
+ # 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/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/dhcpv6_server.py b/src/conf_mode/dhcpv6_server.py
index c2868e078..db248de50 100755
--- a/src/conf_mode/dhcpv6_server.py
+++ b/src/conf_mode/dhcpv6_server.py
@@ -390,7 +390,7 @@ def generate(dhcpv6):
if not dhcpv6 or dhcpv6['disabled']:
return None
- render(config_file, 'dhcpv6-server/dhcpdv6.conf.tmpl', dhcpv6)
+ render(config_file, 'dhcp-server/dhcpdv6.conf.tmpl', dhcpv6)
return None
def apply(dhcpv6):
diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py
index f5c023b81..1ccec3d2e 100755
--- a/src/conf_mode/nat.py
+++ b/src/conf_mode/nat.py
@@ -130,7 +130,7 @@ def verify(nat):
# no need to verify the CLI as NAT is going to be deactivated
return None
- if nat['helper_functions']:
+ if 'helper_functions' in 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')
diff --git a/src/conf_mode/protocols_igmp.py b/src/conf_mode/protocols_igmp.py
index 8606e7364..28d560d03 100755
--- a/src/conf_mode/protocols_igmp.py
+++ b/src/conf_mode/protocols_igmp.py
@@ -28,6 +28,9 @@ from signal import SIGTERM
from vyos import airbag
airbag.enable()
+# Required to use the full path to pimd, in another case daemon will not be started
+pimd_cmd = f'/usr/lib/frr/pimd -d -F traditional --daemon -A 127.0.0.1'
+
config_file = r'/tmp/igmp.frr'
def get_config(config=None):
@@ -115,7 +118,7 @@ def apply(igmp):
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')
+ call(pimd_cmd)
if os.path.exists(config_file):
call(f'vtysh -d pimd -f {config_file}')
diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py
index df03fd990..97ab79583 100755
--- a/src/conf_mode/protocols_isis.py
+++ b/src/conf_mode/protocols_isis.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2017-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
@@ -28,8 +28,6 @@ from vyos import frr
from vyos import airbag
airbag.enable()
-config_file = r'/tmp/isis.frr'
-
def get_config(config=None):
if config:
conf = config
@@ -39,13 +37,6 @@ def get_config(config=None):
isis = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # determine which members have been removed
- for instance in isis:
- conf.set_level(base + [instance])
- tmp = node_changed(conf, ['interface'])
- if tmp:
- isis[instance].update({'interface_remove': tmp})
-
return isis
def verify(isis):
@@ -106,9 +97,6 @@ def generate(isis):
process = list(isis.keys())[0]
isis[process]['process'] = process
- # render(config) not needed, its only for debug
- render(config_file, 'frr/isis.frr.tmpl', isis[process])
-
isis['new_frr_config'] = render_to_string('frr/isis.frr.tmpl',
isis[process])
@@ -116,24 +104,27 @@ def generate(isis):
def apply(isis):
# 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='router isis .*')
+ frr_cfg = frr.FRRConfig()
+ frr_cfg.load_configuration(daemon='isisd')
+ frr_cfg.modify_section(r'interface \S+', '')
+ frr_cfg.modify_section(f'router isis \S+', '')
+ frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', isis['new_frr_config'])
+ frr_cfg.commit_configuration(daemon='isisd')
+
+ # If FRR config is blank, rerun the blank commit x times due to frr-reload
+ # behavior/bug not properly clearing out on one commit.
+ if isis['new_frr_config'] == '':
+ for a in range(5):
+ frr_cfg.commit_configuration(daemon='isisd')
# Debugging
+ '''
print('')
print('--------- DEBUGGING ----------')
print(f'Existing config:\n{frr_cfg["original_config"]}\n\n')
print(f'Replacement config:\n{isis["new_frr_config"]}\n\n')
print(f'Modified config:\n{frr_cfg["modified_config"]}\n\n')
-
- # 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='isisd')
+ '''
return None
diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py
index 791b18110..3b27608da 100755
--- a/src/conf_mode/protocols_mpls.py
+++ b/src/conf_mode/protocols_mpls.py
@@ -18,11 +18,13 @@ import os
from sys import exit
+from glob import glob
from vyos.config import Config
from vyos.configdict import node_changed
from vyos.template import render_to_string
from vyos.util import call
from vyos.util import dict_search
+from vyos.util import read_file
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -118,22 +120,28 @@ def apply(mpls):
# 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}')
+ # Populate system interfaces list with local MPLS capable interfaces
+ for interface in glob('/proc/sys/net/mpls/conf/*'):
+ system_interfaces.append(os.path.basename(interface))
+ # This is where the comparison is done on if an interface needs to be enabled/disabled.
+ for system_interface in system_interfaces:
+ interface_state = read_file(f'/proc/sys/net/mpls/conf/{system_interface}/input')
+ if '1' in interface_state:
+ if system_interface not in mpls['interface']:
+ system_interface = system_interface.replace('.', '/')
+ call(f'sysctl -wq net.mpls.conf.{system_interface}.input=0')
+ elif '0' in interface_state:
+ if system_interface in mpls['interface']:
+ system_interface = system_interface.replace('.', '/')
+ call(f'sysctl -wq net.mpls.conf.{system_interface}.input=1')
else:
- # 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}')
+ # If MPLS interfaces are not configured, set MPLS processing disabled
+ for interface in glob('/proc/sys/net/mpls/conf/*'):
+ system_interfaces.append(os.path.basename(interface))
+ for system_interface in system_interfaces:
+ system_interface = system_interface.replace('.', '/')
+ call(f'sysctl -wq net.mpls.conf.{system_interface}.input=0')
return None
diff --git a/src/conf_mode/protocols_pim.py b/src/conf_mode/protocols_pim.py
index 8a9f034d5..df2e6f941 100755
--- a/src/conf_mode/protocols_pim.py
+++ b/src/conf_mode/protocols_pim.py
@@ -28,6 +28,9 @@ from signal import SIGTERM
from vyos import airbag
airbag.enable()
+# Required to use the full path to pimd, in another case daemon will not be started
+pimd_cmd = f'/usr/lib/frr/pimd -d -F traditional --daemon -A 127.0.0.1'
+
config_file = r'/tmp/pimd.frr'
def get_config(config=None):
@@ -142,7 +145,7 @@ def apply(pim):
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')
+ call(pimd_cmd)
if os.path.exists(config_file):
call("vtysh -d pimd -f " + config_file)
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py
new file mode 100755
index 000000000..969266c30
--- /dev/null
+++ b/src/conf_mode/vpn_ipsec.py
@@ -0,0 +1,67 @@
+#!/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.template import render
+from vyos.util import call
+from vyos.util import dict_search
+from vyos import ConfigError
+from vyos import airbag
+from pprint import pprint
+airbag.enable()
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+ base = ['vpn', 'nipsec']
+ if not conf.exists(base):
+ return None
+
+ # retrieve common dictionary keys
+ ipsec = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ return ipsec
+
+def verify(ipsec):
+ if not ipsec:
+ return None
+
+def generate(ipsec):
+ if not ipsec:
+ return None
+
+ return ipsec
+
+def apply(ipsec):
+ if not ipsec:
+ return None
+
+ pprint(ipsec)
+
+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/op_mode/lldp_op.py b/src/op_mode/lldp_op.py
index fa19e7d45..731e71891 100755
--- a/src/op_mode/lldp_op.py
+++ b/src/op_mode/lldp_op.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2020 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -45,47 +45,51 @@ Device ID Local Proto Cap Platform Port ID
def get_neighbors():
return cmd('/usr/sbin/lldpcli -f json show neighbors')
-def parse_data(data):
+def parse_data(data, interface):
output = []
- for local_if, values in data.items():
- for chassis, c_value in values.get('chassis', {}).items():
- capabilities = c_value['capability']
- if isinstance(capabilities, dict):
- capabilities = [capabilities]
-
- cap = ''
- for capability in capabilities:
- if capability['enabled']:
- if capability['type'] == 'Router':
- cap += 'R'
- if capability['type'] == 'Bridge':
- cap += 'B'
- if capability['type'] == 'Wlan':
- cap += 'W'
- if capability['type'] == 'Station':
- cap += 'S'
- if capability['type'] == 'Repeater':
- cap += 'r'
- if capability['type'] == 'Telephone':
- cap += 'T'
- if capability['type'] == 'Docsis':
- cap += 'D'
- if capability['type'] == 'Other':
- cap += 'O'
-
-
- remote_if = 'Unknown'
- if 'descr' in values.get('port', {}):
- remote_if = values.get('port', {}).get('descr')
- elif 'id' in values.get('port', {}):
- remote_if = values.get('port', {}).get('id').get('value', 'Unknown')
-
- output.append({local_if: {'chassis': chassis,
- 'remote_if': remote_if,
- 'proto': values.get('via','Unknown'),
- 'platform': c_value.get('descr', 'Unknown'),
- 'capabilities': cap}})
-
+ if not isinstance(data, list):
+ data = [data]
+
+ for neighbor in data:
+ for local_if, values in neighbor.items():
+ if interface is not None and local_if != interface:
+ continue
+ for chassis, c_value in values.get('chassis', {}).items():
+ capabilities = c_value['capability']
+ if isinstance(capabilities, dict):
+ capabilities = [capabilities]
+
+ cap = ''
+ for capability in capabilities:
+ if capability['enabled']:
+ if capability['type'] == 'Router':
+ cap += 'R'
+ if capability['type'] == 'Bridge':
+ cap += 'B'
+ if capability['type'] == 'Wlan':
+ cap += 'W'
+ if capability['type'] == 'Station':
+ cap += 'S'
+ if capability['type'] == 'Repeater':
+ cap += 'r'
+ if capability['type'] == 'Telephone':
+ cap += 'T'
+ if capability['type'] == 'Docsis':
+ cap += 'D'
+ if capability['type'] == 'Other':
+ cap += 'O'
+
+ remote_if = 'Unknown'
+ if 'descr' in values.get('port', {}):
+ remote_if = values.get('port', {}).get('descr')
+ elif 'id' in values.get('port', {}):
+ remote_if = values.get('port', {}).get('id').get('value', 'Unknown')
+
+ output.append({local_if: {'chassis': chassis,
+ 'remote_if': remote_if,
+ 'proto': values.get('via','Unknown'),
+ 'platform': c_value.get('descr', 'Unknown'),
+ 'capabilities': cap}})
output = {'neighbors': output}
return output
@@ -107,18 +111,14 @@ if __name__ == '__main__':
neighbors = dict()
if 'interface' in tmp.get('lldp'):
- if args.all:
- neighbors = tmp['lldp']['interface']
- elif args.interface:
- if args.interface in tmp['lldp']['interface']:
- neighbors[args.interface] = tmp['lldp']['interface'][args.interface]
+ neighbors = tmp['lldp']['interface']
else:
parser.print_help()
exit(1)
- tmpl = jinja2.Template(lldp_out)
- config_text = tmpl.render(parse_data(neighbors))
+ tmpl = jinja2.Template(lldp_out, trim_blocks=True)
+ config_text = tmpl.render(parse_data(neighbors, interface=args.interface))
print(config_text)
exit(0)
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]