diff options
628 files changed, 31957 insertions, 11779 deletions
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index cc5e2f536..04ca4070d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -5,8 +5,11 @@ <!--- Provide a general summary of your changes in the Title above --> ## Types of changes -<!--- What types of changes does your code introduce? Put an 'x' in all the boxes that apply. --> -<!--- NOTE: Markdown requires no leading or trailing whitespace inside the [ ] for checking the box, please use [x] --> +<!--- +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) @@ -16,6 +19,7 @@ ## Related Task(s) <!-- All submitted PRs must be linked to a Task on Phabricator. --> +* https://phabricator.vyos.net/Txxxx ## Component(s) name <!-- A rather incomplete list of components: ethernet, wireguard, bgp, mpls, ldp, l2tp, dhcp ... --> @@ -24,8 +28,14 @@ <!--- Describe your changes in detail --> ## How to test -<!--- Please describe in detail how you tested your changes. --> -<!--- Include details of your testing environment, and the tests you ran to --> +<!--- +Please describe in detail how you tested your changes. Include details of your testing +environment, and the tests you ran. When pasting configs, logs, shell output, backtraces, +and other large chunks of text, surround this text with triple backtics +``` +like this +``` +--> ## Checklist: <!--- Go over all the following points, and put an `x` in all the boxes that apply. --> @@ -33,6 +43,7 @@ <!--- The entire development process is outlined here: https://docs.vyos.io/en/latest/contributing/development.html --> - [ ] I have read the [**CONTRIBUTING**](https://github.com/vyos/vyos-1x/blob/current/CONTRIBUTING.md) document - [ ] I have linked this PR to one or more Phabricator Task(s) +- [ ] I have run the components [**SMOKETESTS**](https://github.com/vyos/vyos-1x/tree/current/smoketest/scripts/cli) if applicable - [ ] My commit headlines contain a valid Task id - [ ] My change requires a change to the documentation - [ ] I have updated the documentation accordingly diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ac48ee4c..8458d3208 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,6 +8,81 @@ review this contribution guideline. The following paragraphs are an excerpt from our Documentation. +## Submit a Patch + +Patches are always more than welcome. To have a clean and easy to maintain +repository we have some guidelines when working with Git. A clean repository +eases the automatic generation of a changelog file. + +A good approach for writing commit messages is actually to have a look at the +file(s) history by invoking git log path/to/file.txt. + +### Prepare patch/commit + +In a big system, such as VyOS, that is comprised of multiple components, it’s +impossible to keep track of all the changes and bugs/feature requests in one’s +head. We use a bugtracker known as Phabricator for it (“issue tracker” would +be a better term, but this one stuck). + +The information is used in three ways: + +* Keep track of the progress (what we have already done in this branch and + what we still need to do). +* Prepare automatic release notes for upcoming releases +* Help future maintainers of VyOS (it could be you!) to find out why certain + things have been changed in the codebase or why certain features have been + added + +To make this approach work, every change must be associated with a task number +(prefixed with **T**) and a component. If there is no bug report/feature +request for the changes you are going to make, you have to create a Phabricator +task first. Once there is an entry in Phabricator, you should reference its id +in your commit message, as shown below: + +* `ddclient: T1030: auto create runtime directories` +* `Jenkins: add current Git commit ID to build description` + +If there is no [Phabricator](https://phabricator.vyos.net) reference in the +commits of your pull request, we have to ask you to amend the commit message. +Otherwise we will have to reject it. + +## Writing good commit messages + +The format should be and is inspired by this very good and detailed +[Git documentation](https://git-scm.com/book/ch5-2.html), it is also worth +reading https://chris.beams.io/posts/git-commit/. + +This is nothing VyOS specific - it is more a general topic for distributed +development environments. + +* A single, short, summary of the commit (recommended 50 characters or less, + not exceeding 80 characters) containing a prefix of the changed component + and the corresponding Phabricator reference e.g. `snmp: T1111:` or + `ethernet: T2222:` - multiple components could be concatenated as in `snmp: + ethernet: T3333` +* In some contexts, the first line is treated as the subject of an email and + the rest of the text as the body. The blank line separating the summary from + the body is critical (unless you omit the body entirely); tools like rebase + can get confused if you run the two together. +* Followed by a message which describes all the details like: + * What/why/how something has been changed, makes everyone’s life easier when + working with `git bisect` + * All text of the commit message should be wrapped at 72 characters if + possible which makes reading commit logs easier with git log on a standard + terminal (which happens to be 80x25) + * If applicable a reference to a previous commit should be made linking those + commits nicely when browsing the history: `After commit abcd12ef ("snmp: + this is a headline") a Python import statement is missing, throwing the + following exception: ABCDEF` +* Always use the `-x` option to the `git cherry-pick` command when back or + forward porting an individual commit. This automatically appends the line: + `(cherry picked from commit <ID>)` to the original authors commit message + making it easier when bisecting problems. +* Every change set must be consistent (self containing)! Do not fix multiple + bugs in a single commit. If you already worked on multiple fixes in the same + file use git add –patch to only add the parts related to the one issue into + your upcoming commit. + ## Bug Report/Issue Issues or bugs are found in any software project. VyOS is not an exception. diff --git a/Jenkinsfile b/Jenkinsfile index 7a760b40b..21a6829c0 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,4 +1,4 @@ -// Copyright (C) 2020 VyOS maintainers and contributors +// Copyright (C) 2020-2021 VyOS maintainers and contributors // // This program is free software; you can redistribute it and/or modify // in order to easy exprort images built to "external" world @@ -12,7 +12,6 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. - @NonCPS // Using a version specifier library, use 'current' branch. The underscore (_) @@ -20,5 +19,5 @@ // @Library annotation is not an import statement! @Library('vyos-build@current')_ -// Start package build using library function from https://github.com/c-po/vyos-build -buildPackage() +// Start package build using library function from https://github.com/vyos/vyos-build +buildPackage(null, null, null, true) @@ -8,8 +8,10 @@ CC := gcc LIBS := -lzmq CFLAGS := -src = $(wildcard interface-definitions/*.xml.in) -obj = $(src:.xml.in=.xml) +config_xml_src = $(wildcard interface-definitions/*.xml.in) +config_xml_obj = $(config_xml_src:.xml.in=.xml) +op_xml_src = $(wildcard op-mode-definitions/*.xml.in) +op_xml_obj = $(op_xml_src:.xml.in=.xml) %.xml: %.xml.in @echo Generating $(BUILD_DIR)/$@ from $< @@ -23,51 +25,36 @@ obj = $(src:.xml.in=.xml) # -nostdinc Do not search the standard system directories for header files # -P Inhibit generation of linemarkers in the output from the # preprocessor - @$(CC) -x c-header -E -undef -nostdinc -P -I$(CURDIR)/interface-definitions -o $(BUILD_DIR)/$@ -c $< - -$(BUILD_DIR): - install -d -m 0755 $(BUILD_DIR)/interface-definitions - install -d -m 0755 $(BUILD_DIR)/op-mode-definitions + mkdir -p $(BUILD_DIR)/$(dir $@) + @$(CC) -x c-header -E -undef -nostdinc -P -I$(CURDIR)/$(dir $<) -o $(BUILD_DIR)/$@ -c $< .PHONY: interface_definitions .ONESHELL: -interface_definitions: $(BUILD_DIR) $(obj) +interface_definitions: $(config_xml_obj) mkdir -p $(TMPL_DIR) + $(CURDIR)/scripts/override-default $(BUILD_DIR)/interface-definitions + find $(BUILD_DIR)/interface-definitions -type f -name "*.xml" | xargs -I {} $(CURDIR)/scripts/build-command-templates {} $(CURDIR)/schema/interface_definition.rng $(TMPL_DIR) || exit 1 # XXX: delete top level node.def's that now live in other packages rm -f $(TMPL_DIR)/firewall/node.def rm -f $(TMPL_DIR)/interfaces/node.def - rm -f $(TMPL_DIR)/protocols/node.def - rm -rf $(TMPL_DIR)/protocols/nbgp - rm -f $(TMPL_DIR)/protocols/static/node.def rm -f $(TMPL_DIR)/policy/node.def rm -f $(TMPL_DIR)/system/node.def rm -f $(TMPL_DIR)/vpn/node.def rm -f $(TMPL_DIR)/vpn/ipsec/node.def rm -rf $(TMPL_DIR)/vpn/nipsec - - # XXX: required until OSPF and RIP is migrated from vyatta-cfg-quagga to vyos-1x - mkdir $(TMPL_DIR)/interfaces/loopback/node.tag/ipv6 - mkdir $(TMPL_DIR)/interfaces/dummy/node.tag/ipv6 - mkdir $(TMPL_DIR)/interfaces/openvpn/node.tag/ip - mkdir -p $(TMPL_DIR)/interfaces/vti/node.tag/ip - mkdir -p $(TMPL_DIR)/interfaces/vti/node.tag/ipv6 - cp $(TMPL_DIR)/interfaces/ethernet/node.tag/ipv6/node.def $(TMPL_DIR)/interfaces/loopback/node.tag/ipv6 - cp $(TMPL_DIR)/interfaces/ethernet/node.tag/ipv6/node.def $(TMPL_DIR)/interfaces/dummy/node.tag/ipv6 - cp $(TMPL_DIR)/interfaces/ethernet/node.tag/ip/node.def $(TMPL_DIR)/interfaces/openvpn/node.tag/ip - cp $(TMPL_DIR)/interfaces/ethernet/node.tag/ip/node.def $(TMPL_DIR)/interfaces/vti/node.tag/ip - cp $(TMPL_DIR)/interfaces/ethernet/node.tag/ipv6/node.def $(TMPL_DIR)/interfaces/vti/node.tag/ipv6 + rm -rf $(TMPL_DIR)/npolicy find $(TMPL_DIR) -name node.def -empty && sh -c 'echo "There are empty node.def files! Check your interface definitions." && exit 1' .PHONY: op_mode_definitions .ONESHELL: -op_mode_definitions: +op_mode_definitions: $(op_xml_obj) mkdir -p $(OP_TMPL_DIR) - find $(CURDIR)/op-mode-definitions/ -type f -name "*.xml" | xargs -I {} $(CURDIR)/scripts/build-command-op-templates {} $(CURDIR)/schema/op-mode-definition.rng $(OP_TMPL_DIR) || exit 1 + find $(BUILD_DIR)/op-mode-definitions/ -type f -name "*.xml" | xargs -I {} $(CURDIR)/scripts/build-command-op-templates {} $(CURDIR)/schema/op-mode-definition.rng $(OP_TMPL_DIR) || exit 1 # XXX: delete top level op mode node.def's that now live in other packages rm -f $(OP_TMPL_DIR)/add/node.def @@ -91,7 +78,7 @@ op_mode_definitions: .PHONY: component_versions .ONESHELL: -component_versions: $(BUILD_DIR) $(obj) +component_versions: interface_definitions $(CURDIR)/scripts/build-component-versions $(BUILD_DIR)/interface-definitions $(DATA_DIR) .PHONY: vyshim @@ -103,7 +90,7 @@ vyxdp: $(MAKE) -C $(XDP_DIR) .PHONY: all -all: clean interface_definitions op_mode_definitions component_versions vyshim vyxdp +all: clean interface_definitions op_mode_definitions component_versions vyshim .PHONY: clean clean: @@ -12,6 +12,7 @@ coverage = "*" [packages] vyos = {file = "./python"} jinja2 = "*" +paramiko = "*" [requires] python_version = "3.6" @@ -64,7 +64,7 @@ The guidelines in a nutshell: Tests are executed at build time, you can also execute them by hand with: ``` -pipenv install +pipenv install --dev pipenv shell make test ``` diff --git a/data/configd-include.json b/data/configd-include.json index eb1dd13f9..eed858363 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -21,6 +21,7 @@ "interfaces-pppoe.py", "interfaces-pseudo-ethernet.py", "interfaces-tunnel.py", +"interfaces-erspan.py", "interfaces-vxlan.py", "interfaces-wireguard.py", "interfaces-wireless.py", @@ -28,13 +29,20 @@ "ipsec-settings.py", "lldp.py", "nat.py", +"nat66.py", "ntp.py", "policy-local-route.py", +"protocols_bfd.py", +"protocols_bgp.py", "protocols_igmp.py", "protocols_isis.py", "protocols_mpls.py", +"protocols_ospf.py", +"protocols_ospfv3.py", "protocols_pim.py", "protocols_rip.py", +"protocols_ripng.py", +"protocols_static.py", "protocols_static_multicast.py", "salt-minion.py", "service_console-server.py", diff --git a/data/templates/accel-ppp/config_chap_secrets_radius.j2 b/data/templates/accel-ppp/config_chap_secrets_radius.j2 index 4e2254b21..49af3a228 100644 --- a/data/templates/accel-ppp/config_chap_secrets_radius.j2 +++ b/data/templates/accel-ppp/config_chap_secrets_radius.j2 @@ -1,33 +1,33 @@ -{% if authentication.mode is defined and authentication.mode == 'local' %}
-[chap-secrets]
-chap-secrets={{ chap_secrets_file }}
-{% elif authentication.mode is defined and authentication.mode == 'radius' %}
-[radius]
-verbose=1
-{% for server, options in authentication.radius.server.items() if not options.disable is defined %}
-server={{ server }},{{ options.key }},auth-port={{ options.port }},acct-port={{ options.acct_port }},req-limit=0,fail-time={{ options.fail_time }}
-{% endfor %}
-{% if authentication.radius.acct_interim_jitter is defined and authentication.radius.acct_interim_jitter is not none %}
-acct-interim-jitter={{ authentication.radius.acct_interim_jitter }}
-{% endif %}
-acct-timeout={{ authentication.radius.acct_timeout }}
-timeout={{ authentication.radius.timeout }}
-max-try={{ authentication.radius.max_try }}
-{% if authentication.radius.nas_identifier is defined and authentication.radius.nas_identifier is not none %}
-nas-identifier={{ authentication.radius.nas_identifier }}
-{% endif %}
-{% if authentication.radius.nas_ip_address is defined and authentication.radius.nas_ip_address is not none %}
-nas-ip-address={{ authentication.radius.nas_ip_address }}
-{% endif %}
-{% if authentication.radius.source_address is defined and authentication.radius.source_address is not none %}
-bind={{ authentication.radius.source_address }}
-{% endif %}
-{% if authentication.radius.dynamic_author.server is defined and authentication.radius.dynamic_author.server is not none %}
-dae-server={{ authentication.radius.dynamic_author.server }}:{{ authentication.radius.dynamic_author.port }},{{ authentication.radius.dynamic_author.key }}
-{% endif %}
-{% endif %}
-{# Both chap-secrets and radius block required the gw-ip-address #}
-{% if gateway_address is defined and gateway_address is not none %}
-gw-ip-address={{ gateway_address }}
-{% endif %}
-
+{% if authentication.mode is defined and authentication.mode == 'local' %} +[chap-secrets] +chap-secrets={{ chap_secrets_file }} +{% elif authentication.mode is defined and authentication.mode == 'radius' %} +[radius] +verbose=1 +{% for server, options in authentication.radius.server.items() if not options.disable is defined %} +server={{ server }},{{ options.key }},auth-port={{ options.port }},acct-port={{ options.acct_port }},req-limit=0,fail-time={{ options.fail_time }} +{% endfor %} +{% if authentication.radius.acct_interim_jitter is defined and authentication.radius.acct_interim_jitter is not none %} +acct-interim-jitter={{ authentication.radius.acct_interim_jitter }} +{% endif %} +acct-timeout={{ authentication.radius.acct_timeout }} +timeout={{ authentication.radius.timeout }} +max-try={{ authentication.radius.max_try }} +{% if authentication.radius.nas_identifier is defined and authentication.radius.nas_identifier is not none %} +nas-identifier={{ authentication.radius.nas_identifier }} +{% endif %} +{% if authentication.radius.nas_ip_address is defined and authentication.radius.nas_ip_address is not none %} +nas-ip-address={{ authentication.radius.nas_ip_address }} +{% endif %} +{% if authentication.radius.source_address is defined and authentication.radius.source_address is not none %} +bind={{ authentication.radius.source_address }} +{% endif %} +{% if authentication.radius.dynamic_author.server is defined and authentication.radius.dynamic_author.server is not none %} +dae-server={{ authentication.radius.dynamic_author.server }}:{{ authentication.radius.dynamic_author.port }},{{ authentication.radius.dynamic_author.key }} +{% endif %} +{% endif %} +{# Both chap-secrets and radius block required the gw-ip-address #} +{% if gateway_address is defined and gateway_address is not none %} +gw-ip-address={{ gateway_address }} +{% endif %} + diff --git a/data/templates/accel-ppp/config_ip_pool.j2 b/data/templates/accel-ppp/config_ip_pool.j2 index 973bc0071..3b0f68084 100644 --- a/data/templates/accel-ppp/config_ip_pool.j2 +++ b/data/templates/accel-ppp/config_ip_pool.j2 @@ -4,7 +4,7 @@ gw-ip-address={{ gateway_address }} {% endif %} {% if client_ip_pool.start is defined and client_ip_pool.stop is defined and client_ip_pool.start is not none and client_ip_pool.stop is not none %} -{{ client_ip_pool.start }}-{{ client_ip_pool.stop }} +{{ client_ip_pool.start }}-{{ client_ip_pool.stop.split('.')[3] }} {% endif %} {% if client_ip_pool.subnet is defined and client_ip_pool.subnet is not none %} {% for subnet in client_ip_pool.subnet %} diff --git a/data/templates/accel-ppp/config_modules_auth_mode.j2 b/data/templates/accel-ppp/config_modules_auth_mode.j2 index 5eca76f91..e3d578b38 100644 --- a/data/templates/accel-ppp/config_modules_auth_mode.j2 +++ b/data/templates/accel-ppp/config_modules_auth_mode.j2 @@ -1,5 +1,5 @@ -{% if authentication is defined and authentication.mode is defined and authentication.mode == 'local' %}
-chap-secrets
-{% elif authentication is defined and authentication.mode is defined and authentication.mode == 'radius' %}
-radius
-{% endif %}
+{% if authentication is defined and authentication.mode is defined and authentication.mode == 'local' %} +chap-secrets +{% elif authentication is defined and authentication.mode is defined and authentication.mode == 'radius' %} +radius +{% endif %} diff --git a/data/templates/accel-ppp/config_modules_auth_protocols.j2 b/data/templates/accel-ppp/config_modules_auth_protocols.j2 index e122d6c48..454d37792 100644 --- a/data/templates/accel-ppp/config_modules_auth_protocols.j2 +++ b/data/templates/accel-ppp/config_modules_auth_protocols.j2 @@ -1,10 +1,10 @@ -{% for protocol in authentication.protocols %}
-{# this should be fixed in the CLI by a migrator #}
-{% if protocol == 'chap' %}
-auth_chap_md5
-{% elif protocol == 'mschap' %}
-auth_mschap_v1
-{% else %}
-auth_{{ protocol.replace('-', '_') }}
-{% endif %}
-{% endfor %}
+{% for protocol in authentication.protocols %} +{# this should be fixed in the CLI by a migrator #} +{% if protocol == 'chap' %} +auth_chap_md5 +{% elif protocol == 'mschap' %} +auth_mschap_v1 +{% else %} +auth_{{ protocol.replace('-', '_') }} +{% endif %} +{% endfor %} diff --git a/data/templates/accel-ppp/config_modules_ipv6.j2 b/data/templates/accel-ppp/config_modules_ipv6.j2 index e9ea4924b..02740ce7c 100644 --- a/data/templates/accel-ppp/config_modules_ipv6.j2 +++ b/data/templates/accel-ppp/config_modules_ipv6.j2 @@ -1,5 +1,5 @@ -{% if ppp_options.ipv6 is defined and ppp_options.ipv6 != 'deny' %}
-ipv6pool
-ipv6_nd
-ipv6_dhcp
-{% endif %}
+{% if ppp_options.ipv6 is defined and ppp_options.ipv6 != 'deny' %} +ipv6pool +ipv6_nd +ipv6_dhcp +{% endif %} diff --git a/data/templates/accel-ppp/config_shaper_radius.j2 b/data/templates/accel-ppp/config_shaper_radius.j2 index 2a6641245..8de5f5df3 100644 --- a/data/templates/accel-ppp/config_shaper_radius.j2 +++ b/data/templates/accel-ppp/config_shaper_radius.j2 @@ -1,10 +1,10 @@ -{% if authentication is defined and authentication.mode is defined and authentication.mode == 'radius' %}
-{% if authentication is defined and authentication.radius is defined and authentication.radius.rate_limit is defined and authentication.radius.rate_limit.enable is defined %}
-[shaper]
-verbose=1
-attr={{ authentication.radius.rate_limit.attribute }}
-{% if authentication.radius.rate_limit.vendor is defined and authentication.radius.rate_limit.vendor is not none %}
-vendor={{ authentication.radius.rate_limit.vendor }}
-{% endif %}
-{% endif %}
-{% endif %}
+{% if authentication is defined and authentication.mode is defined and authentication.mode == 'radius' %} +{% if authentication is defined and authentication.radius is defined and authentication.radius.rate_limit is defined and authentication.radius.rate_limit.enable is defined %} +[shaper] +verbose=1 +attr={{ authentication.radius.rate_limit.attribute }} +{% if authentication.radius.rate_limit.vendor is defined and authentication.radius.rate_limit.vendor is not none %} +vendor={{ authentication.radius.rate_limit.vendor }} +{% endif %} +{% endif %} +{% endif %} diff --git a/data/templates/accel-ppp/pppoe.config.tmpl b/data/templates/accel-ppp/pppoe.config.tmpl index 3dfb615da..f444af85c 100644 --- a/data/templates/accel-ppp/pppoe.config.tmpl +++ b/data/templates/accel-ppp/pppoe.config.tmpl @@ -96,12 +96,24 @@ mtu={{ mtu }} verbose=1 ac-name={{ access_concentrator }} -{% if interface %} -{% for iface in interface %} +{% if interface is defined and interface is not none %} +{% for iface, iface_config in interface.items() %} +{% if iface_config.vlan_id is not defined and iface_config.vlan_range is not defined %} interface={{ iface }} -{% if interface[iface].vlan_id is defined or interface[iface].vlan_range is defined %} -vlan-mon={{ iface }},{{ interface[iface].vlan_id | join(',') }},{{ interface[iface].vlan_range | join(',') }} -interface=re:{{ interface.name }}\.\d+ +{% endif %} +{% if iface_config.vlan_id is defined and iface_config.vlan_range is not defined %} +{% for vlan in iface_config.vlan_id %} +interface={{ iface }}.{{ vlan }} +vlan-mon={{ iface }},{{ vlan }} +{% endfor %} +{% endif %} +{% if iface_config.vlan_range is defined and iface_config.vlan_id is not defined %} +vlan-mon={{ iface }},{{ iface_config.vlan_range | join(',') }} +interface=re:{{ iface | replace('.', '\\.') }}\.\d+ +{% endif %} +{% if iface_config.vlan_id is defined and iface_config.vlan_range is defined %} +vlan-mon={{ iface }},{{ iface_config.vlan_id | join(',') }},{{ iface_config.vlan_range | join(',') }} +interface=re:{{ iface | replace('.', '\\.') }}\.\d+ {% endif %} {% endfor %} {% endif %} diff --git a/data/templates/conserver/dropbear@.service.tmpl b/data/templates/conserver/dropbear@.service.tmpl new file mode 100644 index 000000000..4bb73f751 --- /dev/null +++ b/data/templates/conserver/dropbear@.service.tmpl @@ -0,0 +1,4 @@ +[Service] +ExecStart= +ExecStart=/usr/sbin/dropbear -w -j -k -r /etc/dropbear/dropbear_rsa_host_key -c "/usr/bin/console {{ device }}" -P /run/conserver/dropbear.%I.pid -p %I +PIDFile=/run/conserver/dropbear.%I.pid diff --git a/data/templates/dhcp-client/daemon-options.tmpl b/data/templates/dhcp-client/daemon-options.tmpl index 290aefa49..40629dca1 100644 --- a/data/templates/dhcp-client/daemon-options.tmpl +++ b/data/templates/dhcp-client/daemon-options.tmpl @@ -1,4 +1,4 @@ ### Autogenerated by interface.py ### -DHCLIENT_OPTS="-nw -cf /var/lib/dhcp/dhclient_{{ifname}}.conf -pf /var/lib/dhcp/dhclient_{{ifname}}.pid -lf /var/lib/dhcp/dhclient_{{ifname}}.leases {{ifname}}" +DHCLIENT_OPTS="-nw -cf /var/lib/dhcp/dhclient_{{ifname}}.conf -pf /var/lib/dhcp/dhclient_{{ifname}}.pid -lf /var/lib/dhcp/dhclient_{{ifname}}.leases{{" -e IF_METRIC=" ~ dhcp_options.default_route_distance if dhcp_options.default_route_distance is defined and dhcp_options.default_route_distance is not none}} {{ifname}}" diff --git a/data/templates/dhcp-client/ipv6.tmpl b/data/templates/dhcp-client/ipv6.tmpl index 68f668117..c292664e9 100644 --- a/data/templates/dhcp-client/ipv6.tmpl +++ b/data/templates/dhcp-client/ipv6.tmpl @@ -2,6 +2,9 @@ # man https://www.unix.com/man-page/debian/5/dhcp6c.conf/ interface {{ ifname }} { +{% if dhcpv6_options is defined and dhcpv6_options.duid is defined and dhcpv6_options.duid is not none %} + send client-id {{ dhcpv6_options.duid }}; +{% endif %} {% if address is defined and 'dhcpv6' in address %} request domain-name-servers; request domain-name; diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl index ff2e31998..f0bfa468c 100644 --- a/data/templates/dhcp-server/dhcpd.conf.tmpl +++ b/data/templates/dhcp-server/dhcpd.conf.tmpl @@ -8,16 +8,12 @@ on release { set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name); set ClientIp = binary-to-ascii(10, 8, ".",leased-address); - set ClientMac = binary-to-ascii(16, 8, ":",substring(hardware, 1, 6)); - set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!"); - execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", ClientName, ClientIp, ClientMac, ClientDomain); + execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", "", ClientIp, "", ""); } on expiry { set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name); set ClientIp = binary-to-ascii(10, 8, ".",leased-address); - set ClientMac = binary-to-ascii(16, 8, ":",substring(hardware, 1, 6)); - set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!"); - execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", ClientName, ClientIp, ClientMac, ClientDomain); + execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", "", ClientIp, "", ""); } {% endif %} @@ -201,11 +197,15 @@ shared-network {{ network | replace('_','-') }} { on commit { set shared-networkname = "{{ network | replace('_','-') }}"; {% if hostfile_update is defined %} - 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); + set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name, "empty_hostname"); + if not (ClientName = "empty_hostname") { + set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!"); + execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "commit", ClientName, ClientIp, ClientMac, ClientDomain); + } else { + log(concat("Hostname is not defined for client with IP: ", ClientIP, " MAC: ", ClientMac)); + } {% endif %} } } diff --git a/data/templates/dhcp-server/dhcpdv6.conf.tmpl b/data/templates/dhcp-server/dhcpdv6.conf.tmpl index de7c9b29c..25e5fa592 100644 --- a/data/templates/dhcp-server/dhcpdv6.conf.tmpl +++ b/data/templates/dhcp-server/dhcpdv6.conf.tmpl @@ -8,6 +8,12 @@ log-facility local7; option dhcp6.preference {{ preference }}; {% endif %} +{% if global_parameters is defined and global_parameters.name_server is defined and global_parameters.name_server is not none %} +{% for nameserver in global_parameters.name_server %} +option dhcp6.name-servers {{ nameserver }}; +{% endfor %} +{% endif %} + # Shared network configration(s) {% if shared_network_name is defined and shared_network_name is not none %} {% for network, network_config in shared_network_name.items() if network_config.disable is not defined %} diff --git a/data/templates/firewall/nftables-nat.tmpl b/data/templates/firewall/nftables-nat.tmpl index 8769c2384..b80fc1968 100644 --- a/data/templates/firewall/nftables-nat.tmpl +++ b/data/templates/firewall/nftables-nat.tmpl @@ -1,87 +1,103 @@ #!/usr/sbin/nft -f {% macro nat_rule(rule, config, chain) %} -{% set comment = "" %} -{% set base_log = "" %} -{% set src_addr = "ip saddr " + config.source.address if config.source is defined and config.source.address is defined and config.source.address is not none %} -{% set dst_addr = "ip daddr " + config.destination.address if config.destination is defined and config.destination.address is defined and config.destination.address is not none %} +{% set comment = '' %} +{% set base_log = '' %} +{% set src_addr = 'ip saddr ' + config.source.address.replace('!','!= ') if config.source is defined and config.source.address is defined and config.source.address is not none %} +{% set dst_addr = 'ip daddr ' + config.destination.address.replace('!','!= ') if config.destination is defined and config.destination.address is defined and config.destination.address is not none %} {# negated port groups need special treatment, move != in front of { } group #} {% if config.source is defined and config.source.port is defined and config.source.port is not none and config.source.port.startswith('!=') %} -{% set src_port = "sport != { " + config.source.port.replace('!=','') +" }" %} +{% set src_port = 'sport != { ' + config.source.port.replace('!=','') + ' }' %} {% else %} -{% set src_port = "sport { " + config.source.port +" }" if config.source is defined and config.source.port is defined and config.source.port is not none %} +{% set src_port = 'sport { ' + config.source.port + ' }' if config.source is defined and config.source.port is defined and config.source.port is not none %} {% endif %} {# negated port groups need special treatment, move != in front of { } group #} {% if config.destination is defined and config.destination.port is defined and config.destination.port is not none and config.destination.port.startswith('!=') %} -{% set dst_port = "dport != { " + config.destination.port.replace('!=','') +" }" %} +{% set dst_port = 'dport != { ' + config.destination.port.replace('!=','') + ' }' %} {% else %} -{% set dst_port = "dport { " + config.destination.port +" }" if config.destination is defined and config.destination.port is defined and config.destination.port is not none %} -{% endif %} -{% if chain == "PREROUTING" %} -{% set comment = "DST-NAT-" + rule %} -{% set base_log = "[NAT-DST-" + rule %} -{% set interface = " iifname \"" + config.inbound_interface + "\"" if config.inbound_interface is defined and config.inbound_interface != 'any' else '' %} -{% set trns_addr = "dnat to " + config.translation.address if config.translation is defined and config.translation.address is defined and config.translation.address is not none %} -{% elif chain == "POSTROUTING" %} -{% set comment = "SRC-NAT-" + rule %} -{% set base_log = "[NAT-SRC-" + rule %} -{% set interface = " oifname \"" + config.outbound_interface + "\"" if config.outbound_interface is defined and config.outbound_interface != 'any' else '' %} -{% if config.translation is defined and config.translation.address is defined and config.translation.address == 'masquerade' %} -{% set trns_addr = config.translation.address %} -{% if config.translation.port is defined and config.translation.port is not none %} -{% set trns_addr = trns_addr + " to " %} +{% set dst_port = 'dport { ' + config.destination.port + ' }' if config.destination is defined and config.destination.port is defined and config.destination.port is not none %} +{% endif %} +{% if chain == 'PREROUTING' %} +{% set comment = 'DST-NAT-' + rule %} +{% set base_log = '[NAT-DST-' + rule %} +{% set interface = ' iifname "' + config.inbound_interface + '"' if config.inbound_interface is defined and config.inbound_interface != 'any' else '' %} +{% if config.translation is defined and config.translation.address is defined and config.translation.address is not none %} +{# support 1:1 network translation #} +{% if config.translation.address | is_ip_network %} +{% set trns_addr = 'dnat ip prefix to ip daddr map { ' + config.destination.address + ' : ' + config.translation.address + ' }' %} +{# we can now clear out the dst_addr part as it's already covered in aboves map #} +{% set dst_addr = '' %} +{% else %} +{% set trns_addr = 'dnat to ' + config.translation.address %} +{% endif %} +{% endif %} +{% elif chain == 'POSTROUTING' %} +{% set comment = 'SRC-NAT-' + rule %} +{% set base_log = '[NAT-SRC-' + rule %} +{% set interface = ' oifname "' + config.outbound_interface + '"' if config.outbound_interface is defined and config.outbound_interface != 'any' else '' %} +{% if config.translation is defined and config.translation.address is defined and config.translation.address is not none %} +{% if config.translation.address == 'masquerade' %} +{% set trns_addr = config.translation.address %} +{% if config.translation.port is defined and config.translation.port is not none %} +{% set trns_addr = trns_addr + ' to ' %} +{% endif %} +{# support 1:1 network translation #} +{% elif config.translation.address | is_ip_network %} +{% set trns_addr = 'snat ip prefix to ip saddr map { ' + config.source.address + ' : ' + config.translation.address + ' }' %} +{# we can now clear out the src_addr part as it's already covered in aboves map #} +{% set src_addr = '' %} +{% else %} +{% set trns_addr = 'snat to ' + config.translation.address %} {% endif %} -{% else %} -{% set trns_addr = "snat to " + config.translation.address if config.translation is defined and config.translation.address is defined and config.translation.address is not none %} {% endif %} {% endif %} -{% set trns_port = ":" + config.translation.port if config.translation is defined and config.translation.port is defined and config.translation.port is not none %} +{% set trns_port = ':' + config.translation.port if config.translation is defined and config.translation.port is defined and config.translation.port is not none %} {# protocol has a default value thus it is always present #} -{% if config.protocol == "tcp_udp" %} -{% set protocol = "tcp" %} -{% set comment = comment + " tcp_udp" %} +{% if config.protocol == 'tcp_udp' %} +{% set protocol = 'tcp' %} +{% set comment = comment + ' tcp_udp' %} {% else %} {% set protocol = config.protocol %} {% endif %} {% if config.log is defined %} {% if config.exclude is defined %} -{% set log = base_log + "-EXCL]" %} +{% set log = base_log + '-EXCL]' %} {% elif config.translation is defined and config.translation.address is defined and config.translation.address == 'masquerade' %} -{% set log = base_log + "-MASQ]" %} +{% set log = base_log +'-MASQ]' %} {% else %} -{% set log = base_log + "]" %} +{% set log = base_log + ']' %} {% endif %} {% endif %} {% if config.exclude is defined %} -{# rule has been marked as "exclude" thus we simply return here #} -{% set trns_addr = "return" %} -{% set trns_port = "" %} +{# rule has been marked as 'exclude' thus we simply return here #} +{% set trns_addr = 'return' %} +{% set trns_port = '' %} {% endif %} -{% set output = "add rule ip nat " + chain + interface %} -{% if protocol != "all" %} -{% set output = output + " ip protocol " + protocol %} +{% set output = 'add rule ip nat ' + chain + interface %} +{% if protocol != 'all' %} +{% set output = output + ' ip protocol ' + protocol %} {% endif %} {% if src_addr %} -{% set output = output + " " + src_addr %} +{% set output = output + ' ' + src_addr %} {% endif %} {% if src_port %} -{% set output = output + " " + protocol + " " + src_port %} +{% set output = output + ' ' + protocol + ' ' + src_port %} {% endif %} {% if dst_addr %} -{% set output = output + " " + dst_addr %} +{% set output = output + ' ' + dst_addr %} {% endif %} {% if dst_port %} -{% set output = output + " " + protocol + " " + dst_port %} +{% set output = output + ' ' + protocol + ' ' + dst_port %} {% endif %} {# Count packets #} -{% set output = output + " counter" %} +{% set output = output + ' counter' %} {# Special handling of log option, we must repeat the entire rule before the #} {# NAT translation options are added, this is essential #} {% if log %} -{% set log_output = output + " log prefix \"" + log + "\" comment \"" + comment + "\"" %} +{% set log_output = output + ' log prefix "' + log + '" comment "' + comment + '"' %} {% endif %} {% if trns_addr %} -{% set output = output + " " + trns_addr %} +{% set output = output + ' ' + trns_addr %} {% endif %} {% if trns_port %} {# Do not add a whitespace here, translation port must be directly added after IP address #} @@ -89,23 +105,23 @@ {% set output = output + trns_port %} {% endif %} {% if comment %} -{% set output = output + " comment \"" + comment + "\"" %} +{% set output = output + ' comment "' + comment + '"' %} {% endif %} {{ log_output if log_output }} {{ output }} {# Special handling if protocol is tcp_udp, we must repeat the entire rule with udp as protocol #} -{% if config.protocol == "tcp_udp" %} +{% if config.protocol == 'tcp_udp' %} {# Beware of trailing whitespace, without it the comment tcp_udp will be changed to udp_udp #} -{{ log_output | replace("tcp ", "udp ") if log_output }} -{{ output | replace("tcp ", "udp ") }} +{{ log_output | replace('tcp ', 'udp ') if log_output }} +{{ output | replace('tcp ', 'udp ') }} {% endif %} {% endmacro %} # Start with clean NAT table -flush table nat +flush table ip nat {% if helper_functions == 'remove' %} {# NAT if going to be disabled - remove rules and targets from nftables #} -{% set base_command = "delete rule ip raw" %} +{% set base_command = 'delete rule ip raw' %} {{ base_command }} PREROUTING handle {{ pre_ct_ignore }} {{ base_command }} OUTPUT handle {{ out_ct_ignore }} {{ base_command }} PREROUTING handle {{ pre_ct_conntrack }} @@ -117,7 +133,7 @@ delete chain ip raw NAT_CONNTRACK {# NAT if enabled - add targets to nftables #} add chain ip raw NAT_CONNTRACK add rule ip raw NAT_CONNTRACK counter accept -{% set base_command = "add rule ip raw" %} +{% set base_command = 'add rule ip raw' %} {{ base_command }} PREROUTING position {{ pre_ct_ignore }} counter jump VYATTA_CT_HELPER {{ base_command }} OUTPUT position {{ out_ct_ignore }} counter jump VYATTA_CT_HELPER {{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK @@ -132,7 +148,6 @@ add rule ip raw NAT_CONNTRACK counter accept {{ nat_rule(rule, config, 'PREROUTING') }} {% endfor %} {% endif %} - # # Source NAT rules build up here # diff --git a/data/templates/firewall/nftables-nat66.tmpl b/data/templates/firewall/nftables-nat66.tmpl new file mode 100644 index 000000000..e5c1b1b8d --- /dev/null +++ b/data/templates/firewall/nftables-nat66.tmpl @@ -0,0 +1,102 @@ +#!/usr/sbin/nft -f + +{% macro nptv6_rule(rule,config, chain) %} +{% set comment = '' %} +{% set base_log = '' %} +{% set src_prefix = "ip6 saddr " + config.source.prefix if config.source is defined and config.source.prefix is defined and config.source.prefix is not none %} +{% set dest_address = "ip6 daddr " + config.destination.address if config.destination is defined and config.destination.address is defined and config.destination.address is not none %} +{% if chain == "PREROUTING" %} +{% set comment = "DST-NAT66-" + rule %} +{% set base_log = '[NAT66-DST-' + rule %} +{% set interface = " iifname \"" + config.inbound_interface + "\"" if config.inbound_interface is defined and config.inbound_interface != 'any' else '' %} +{% if config.translation.address | is_ip_network %} +{# support 1:1 network translation #} +{% set dnat_type = "dnat prefix to " %} +{% else %} +{% set dnat_type = "dnat to " %} +{% endif %} +{% set trns_address = dnat_type + config.translation.address if config.translation is defined and config.translation.address is defined and config.translation.address is not none %} +{% elif chain == "POSTROUTING" %} +{% set comment = 'SRC-NAT66-' + rule %} +{% set base_log = '[NAT66-SRC-' + rule %} +{% if config.translation is defined and config.translation.address is defined and config.translation.address is not none %} +{% if config.translation.address == 'masquerade' %} +{% set trns_address = config.translation.address %} +{% else %} +{% if config.translation.address | is_ip_network %} +{# support 1:1 network translation #} +{% set snat_type = "snat prefix to " %} +{% else %} +{% set snat_type = "snat to " %} +{% endif %} +{% set trns_address = snat_type + config.translation.address %} +{% endif %} +{% endif %} +{% set interface = " oifname \"" + config.outbound_interface + "\"" if config.outbound_interface is defined else '' %} +{% endif %} +{% if config.log is defined %} +{% if config.translation is defined and config.translation.address is defined and config.translation.address == 'masquerade' %} +{% set log = base_log +'-MASQ]' %} +{% else %} +{% set log = base_log + "]" %} +{% endif %} +{% endif %} +{% set output = "add rule ip6 nat " + chain + interface %} +{# Count packets #} +{% set output = output + " counter" %} +{# Special handling of log option, we must repeat the entire rule before the #} +{# NAT translation options are added, this is essential #} +{% if log %} +{% set log_output = output + " log prefix \"" + log + "\" comment \"" + comment + "\"" %} +{% endif %} +{% if src_prefix %} +{% set output = output + " " + src_prefix %} +{% endif %} +{% if dest_address %} +{% set output = output + " " + dest_address %} +{% endif %} +{% if trns_address %} +{% set output = output + " " + trns_address %} +{% endif %} +{% if comment %} +{% set output = output + " comment \"" + comment + "\"" %} +{% endif %} +{{ log_output if log_output }} +{{ output }} +{% endmacro %} + +# Start with clean NAT table +flush table ip6 nat +{% if helper_functions == 'remove' %} +{# NAT if going to be disabled - remove rules and targets from nftables #} +{% set base_command = "delete rule ip6 raw" %} +{{base_command}} PREROUTING handle {{ pre_ct_conntrack }} +{{base_command}} OUTPUT handle {{ out_ct_conntrack }} + +delete chain ip6 raw NAT_CONNTRACK + +{% elif helper_functions == 'add' %} +{# NAT if enabled - add targets to nftables #} +add chain ip6 raw NAT_CONNTRACK +add rule ip6 raw NAT_CONNTRACK counter accept +{% set base_command = "add rule ip6 raw" %} +{{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK +{{ base_command }} OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK +{% endif %} + +# +# Destination NAT66 rules build up here +# +{% if destination is defined and destination.rule is defined and destination.rule is not none %} +{% for rule, config in destination.rule.items() if config.disable is not defined %} +{{ nptv6_rule(rule, config, 'PREROUTING') }} +{% endfor %} +{% endif %} +# +# Source NAT66 rules build up here +# +{% if source is defined and source.rule is defined and source.rule is not none %} +{% for rule, config in source.rule.items() if config.disable is not defined %} +{{ nptv6_rule(rule, config, 'POSTROUTING') }} +{% endfor %} +{% endif %} diff --git a/data/templates/frr/bfd.frr.tmpl b/data/templates/frr/bfd.frr.tmpl index 9e5ad3379..16f8be92c 100644 --- a/data/templates/frr/bfd.frr.tmpl +++ b/data/templates/frr/bfd.frr.tmpl @@ -1,22 +1,44 @@ ! bfd -{% for peer in old_peers %} - no peer {{ peer.remote }}{% if peer.multihop %} multihop{% endif %}{% if peer.src_addr %} local-address {{ peer.src_addr }}{% endif %}{% if peer.src_if %} interface {{ peer.src_if }}{% endif %} - -{% endfor %} -! -{% for peer in new_peers %} - peer {{ peer.remote }}{% if peer.multihop %} multihop{% endif %}{% if peer.src_addr %} local-address {{ peer.src_addr }}{% endif %}{% if peer.src_if %} interface {{ peer.src_if }}{% endif %} - - detect-multiplier {{ peer.multiplier }} - receive-interval {{ peer.rx_interval }} - transmit-interval {{ peer.tx_interval }} -{% if peer.echo_mode %} - echo-mode +{% if profile is defined and profile is not none %} +{% for profile_name, profile_config in profile.items() %} + profile {{ profile_name }} + detect-multiplier {{ profile_config.interval.multiplier }} + receive-interval {{ profile_config.interval.receive }} + transmit-interval {{ profile_config.interval.transmit }} +{% if profile_config.interval['echo-interval'] is defined and profile_config.interval['echo-interval'] is not none %} + echo-interval {{ profile_config.interval['echo-interval'] }} +{% endif %} +{% if profile_config['echo-mode'] is defined %} + echo-mode +{% endif %} +{% if profile_config.shutdown is defined %} + shutdown +{% else %} + no shutdown +{% endif %} + exit +{% endfor %} {% endif %} -{% if peer.echo_interval != '' %} - echo-interval {{ peer.echo_interval }} +{% if peer is defined and peer is not none %} +{% for peer_name, peer_config in peer.items() %} + peer {{ peer_name }}{{ ' multihop' if peer_config.multihop is defined }}{{ ' local-address ' + peer_config.source.address if peer_config.source is defined and peer_config.source.address is defined }}{{ ' interface ' + peer_config.source.interface if peer_config.source is defined and peer_config.source.interface is defined }} + detect-multiplier {{ peer_config.interval.multiplier }} + receive-interval {{ peer_config.interval.receive }} + transmit-interval {{ peer_config.interval.transmit }} +{% if peer_config.interval['echo-interval'] is defined and peer_config.interval['echo-interval'] is not none %} + echo-interval {{ peer_config.interval['echo-interval'] }} +{% endif %} +{% if peer_config['echo-mode'] is defined %} + echo-mode +{% endif %} +{% if peer_config.shutdown is defined %} + shutdown +{% else %} + no shutdown +{% endif %} + exit +{% endfor %} {% endif %} - {% if not peer.shutdown %}no {% endif %}shutdown -{% endfor %} + end ! diff --git a/data/templates/frr/bgp.frr.tmpl b/data/templates/frr/bgp.frr.tmpl index 16355a1e5..53e62928b 100644 --- a/data/templates/frr/bgp.frr.tmpl +++ b/data/templates/frr/bgp.frr.tmpl @@ -9,6 +9,12 @@ {% if config.remote_as is defined and config.remote_as is not none %} neighbor {{ neighbor }} remote-as {{ config.remote_as }} {% endif %} +{% if config.interface is defined and config.interface.remote_as is defined and config.interface.remote_as is not none %} + neighbor {{ neighbor }} interface remote-as {{ config.interface.remote_as }} +{% endif %} +{% if config.advertisement_interval is defined and config.advertisement_interval is not none %} + neighbor {{ neighbor }} advertisement-interval {{ config.advertisement_interval }} +{% endif %} {% if config.bfd is defined %} neighbor {{ neighbor }} bfd {% endif %} @@ -24,7 +30,7 @@ neighbor {{ neighbor }} description {{ config.description }} {% endif %} {% if config.disable_capability_negotiation is defined %} - neighbor {{ neighbor }} disable-capability-negotiation + neighbor {{ neighbor }} dont-capability-negotiate {% endif %} {% if config.ebgp_multihop is defined and config.ebgp_multihop is not none %} neighbor {{ neighbor }} ebgp-multihop {{ config.ebgp_multihop }} @@ -43,87 +49,134 @@ {% if config.password is defined and config.password is not none %} neighbor {{ neighbor }} password {{ config.password }} {% endif %} +{% if config.port is defined and config.port is not none %} + neighbor {{ neighbor }} port {{ config.port }} +{% endif %} {% if config.shutdown is defined %} neighbor {{ neighbor }} shutdown {% endif %} +{% if config.strict_capability_match is defined %} + neighbor {{ neighbor }} strict-capability-match +{% endif %} {% if config.ttl_security is defined and config.ttl_security.hops is defined and config.ttl_security.hops is not none %} neighbor {{ neighbor }} ttl-security hops {{ config.ttl_security.hops }} {% endif %} +{% if config.timers is defined %} +{% if config.timers.connect is defined and config.timers.connect is not none %} + neighbor {{ neighbor }} timers connect {{ config.timers.connect }} +{% endif %} +{% if config.timers.holdtime is defined and config.timers.keepalive is defined and config.timers.holdtime is not none and config.timers.keepalive is not none %} + neighbor {{ neighbor }} timers {{ config.timers.keepalive }} {{ config.timers.holdtime }} +{% endif %} +{% endif %} {% if config.update_source is defined and config.update_source is not none %} neighbor {{ neighbor }} update-source {{ config.update_source }} {% endif %} +{% if config.interface is defined and config.interface is not none %} +{% if config.interface.peer_group is defined and config.interface.peer_group is not none %} + neighbor {{ neighbor }} interface peer-group {{ config.interface.peer_group }} +{% endif %} +{% if config.interface.v6only is defined and config.interface.v6only is not none %} +{% if config.interface.v6only.peer_group is defined and config.interface.v6only.peer_group is not none %} + neighbor {{ neighbor }} interface v6only peer-group {{ config.interface.v6only.peer_group }} +{% endif %} +{% if config.interface.v6only.remote_as is defined and config.interface.v6only.remote_as is not none %} + neighbor {{ neighbor }} interface v6only remote-as {{ config.interface.v6only.remote_as }} +{% endif %} +{% endif %} +{% endif %} ! {% if config.address_family is defined and config.address_family is not none %} -{% for af in config.address_family %} -{% if af == 'ipv4_unicast' %} +{% for afi, afi_config in config.address_family.items() %} +{% if afi == 'ipv4_unicast' %} address-family ipv4 unicast -{% elif af == 'ipv6_unicast' %} +{% elif afi == 'ipv6_unicast' %} address-family ipv6 unicast +{% elif afi == 'l2vpn_evpn' %} + address-family l2vpn evpn +{% endif %} +{% if afi_config.addpath_tx_all is defined %} + neighbor {{ neighbor }} addpath-tx-all-paths {% endif %} -{% if config.address_family[af].allowas_in is defined and config.address_family[af].allowas_in is not none %} - neighbor {{ neighbor }} allowas-in {{ config.address_family[af].allowas_in.number if config.address_family[af].allowas_in.number is defined }} +{% if afi_config.addpath_tx_per_as is defined %} + neighbor {{ neighbor }} addpath-tx-bestpath-per-AS {% endif %} -{% if config.address_family[af].remove_private_as is defined %} +{% if afi_config.allowas_in is defined and afi_config.allowas_in is not none %} + neighbor {{ neighbor }} allowas-in {{ afi_config.allowas_in.number if afi_config.allowas_in.number is defined }} +{% endif %} +{% if afi_config.remove_private_as is defined %} neighbor {{ neighbor }} remove-private-AS {% endif %} -{% if config.address_family[af].route_reflector_client is defined %} +{% if afi_config.route_reflector_client is defined %} neighbor {{ neighbor }} route-reflector-client {% endif %} -{% if config.address_family[af].weight is defined and config.address_family[af].weight is not none %} - neighbor {{ neighbor }} weight {{ config.address_family[af].weight }} +{% if afi_config.weight is defined and afi_config.weight is not none %} + neighbor {{ neighbor }} weight {{ afi_config.weight }} +{% endif %} +{% if afi_config.attribute_unchanged is defined and afi_config.attribute_unchanged is not none %} + neighbor {{ neighbor }} attribute-unchanged {{ 'as-path ' if afi_config.attribute_unchanged.as_path is defined }}{{ 'med ' if afi_config.attribute_unchanged.med is defined }}{{ 'next-hop ' if afi_config.attribute_unchanged.next_hop is defined }} {% endif %} -{% if config.address_family[af].attribute_unchanged is defined and config.address_family[af].attribute_unchanged is not none %} - neighbor {{ neighbor }} attribute-unchanged {{ 'as-path ' if config.address_family[af].attribute_unchanged.as_path is defined }}{{ 'med ' if config.address_family[af].attribute_unchanged.med is defined }}{{ 'next-hop ' if config.address_family[af].attribute_unchanged.next_hop is defined }} +{% if afi_config.capability is defined and afi_config.capability.orf is defined and afi_config.capability.orf.prefix_list is defined and afi_config.capability.orf.prefix_list.send is defined %} + neighbor {{ neighbor }} capability orf prefix-list send {% endif %} -{% if config.address_family[af].capability is defined and config.address_family[af].capability.orf is defined and config.address_family[af].capability.orf.prefix_list is defined and config.address_family[af].capability.orf.prefix_list is not none %} - neighbor {{ neighbor }} capability orf prefix-list {{ config.address_family[af].capability.orf.prefix_list }} +{% if afi_config.capability is defined and afi_config.capability.orf is defined and afi_config.capability.orf.prefix_list is defined and afi_config.capability.orf.prefix_list.receive is defined %} + neighbor {{ neighbor }} capability orf prefix-list receive {% endif %} -{% if config.address_family[af].default_originate is defined %} - neighbor {{ neighbor }} default-originate {{ 'route-map ' + config.address_family[af].default_originate.route_map if config.address_family[af].default_originate.route_map is defined }} +{% if afi_config.default_originate is defined %} + neighbor {{ neighbor }} default-originate {{ 'route-map ' + afi_config.default_originate.route_map if afi_config.default_originate.route_map is defined }} {% endif %} -{% if config.address_family[af].distribute_list is defined and config.address_family[af].distribute_list is not none %} -{% if config.address_family[af].distribute_list.export is defined and config.address_family[af].distribute_list.export is not none %} - neighbor {{ neighbor }} distribute-list {{ config.address_family[af].distribute_list.export }} out -{% elif config.address_family[af].distribute_list.import is defined and config.address_family[af].distribute_list.import is not none %} - neighbor {{ neighbor }} distribute-list {{ config.address_family[af].distribute_list.export }} in +{% if afi_config.distribute_list is defined and afi_config.distribute_list is not none %} +{% if afi_config.distribute_list.export is defined and afi_config.distribute_list.export is not none %} + neighbor {{ neighbor }} distribute-list {{ afi_config.distribute_list.export }} out +{% endif %} +{% if afi_config.distribute_list.import is defined and afi_config.distribute_list.import is not none %} + neighbor {{ neighbor }} distribute-list {{ afi_config.distribute_list.import }} in {% endif %} {% endif %} -{% if config.address_family[af].filter_list is defined and config.address_family[af].filter_list is not none %} -{% if config.address_family[af].filter_list.export is defined and config.address_family[af].filter_list.export is not none %} - neighbor {{ neighbor }} filter-list {{ config.address_family[af].filter_list.export }} out -{% elif config.address_family[af].filter_list.import is defined and config.address_family[af].filter_list.import is not none %} - neighbor {{ neighbor }} filter-list {{ config.address_family[af].filter_list.import }} in +{% if afi_config.filter_list is defined and afi_config.filter_list is not none %} +{% if afi_config.filter_list.export is defined and afi_config.filter_list.export is not none %} + neighbor {{ neighbor }} filter-list {{ afi_config.filter_list.export }} out +{% endif %} +{% if afi_config.filter_list.import is defined and afi_config.filter_list.import is not none %} + neighbor {{ neighbor }} filter-list {{ afi_config.filter_list.import }} in {% endif %} {% endif %} -{% if config.address_family[af].maximum_prefix is defined and config.address_family[af].maximum_prefix is not none %} - neighbor {{ neighbor }} maximum-prefix {{ config.address_family[af].maximum_prefix }} +{% if afi_config.maximum_prefix is defined and afi_config.maximum_prefix is not none %} + neighbor {{ neighbor }} maximum-prefix {{ afi_config.maximum_prefix }} {% endif %} -{% if config.address_family[af].nexthop_self is defined %} -{# https://phabricator.vyos.net/T1817 #} - neighbor {{ neighbor }} next-hop-self {{ 'force' if config.address_family[af].nexthop_self.force is defined }} +{% if afi_config.nexthop_self is defined %} + neighbor {{ neighbor }} next-hop-self {{ 'force' if afi_config.nexthop_self.force is defined }} {% endif %} -{% if config.address_family[af].route_server_client is defined %} +{% if afi_config.route_server_client is defined %} neighbor {{ neighbor }} route-server-client {% endif %} -{% if config.address_family[af].route_map is defined and config.address_family[af].route_map is not none %} -{% if config.address_family[af].route_map.export is defined and config.address_family[af].route_map.export is not none %} - neighbor {{ neighbor }} route-map {{ config.address_family[af].route_map.export }} out -{% elif config.address_family[af].route_map.import is defined and config.address_family[af].route_map.import is not none %} - neighbor {{ neighbor }} route-map {{ config.address_family[af].route_map.import }} in +{% if afi_config.route_map is defined and afi_config.route_map is not none %} +{% if afi_config.route_map.export is defined and afi_config.route_map.export is not none %} + neighbor {{ neighbor }} route-map {{ afi_config.route_map.export }} out +{% endif %} +{% if afi_config.route_map.import is defined and afi_config.route_map.import is not none %} + neighbor {{ neighbor }} route-map {{ afi_config.route_map.import }} in {% endif %} {% endif %} -{% if config.address_family[af].prefix_list is defined and config.address_family[af].prefix_list is not none %} -{% if config.address_family[af].prefix_list.export is defined and config.address_family[af].prefix_list.export is not none %} - neighbor {{ neighbor }} prefix-list {{ config.address_family[af].prefix_list.export }} out -{% elif config.address_family[af].prefix_list.import is defined and config.address_family[af].prefix_list.import is not none %} - neighbor {{ neighbor }} prefix-list {{ config.address_family[af].prefix_list.import }} in +{% if afi_config.prefix_list is defined and afi_config.prefix_list is not none %} +{% if afi_config.prefix_list.export is defined and afi_config.prefix_list.export is not none %} + neighbor {{ neighbor }} prefix-list {{ afi_config.prefix_list.export }} out +{% endif %} +{% if afi_config.prefix_list.import is defined and afi_config.prefix_list.import is not none %} + neighbor {{ neighbor }} prefix-list {{ afi_config.prefix_list.import }} in {% endif %} {% endif %} -{% if config.address_family[af].soft_reconfiguration is defined and config.address_family[af].soft_reconfiguration.inbound is defined %} +{% if afi_config.soft_reconfiguration is defined and afi_config.soft_reconfiguration.inbound is defined %} neighbor {{ neighbor }} soft-reconfiguration inbound {% endif %} -{% if config.address_family[af].unsuppress_map is defined and config.address_family[af].unsuppress_map is not none %} - neighbor {{ neighbor }} unsuppress-map {{ config.address_family[af].unsuppress_map }} +{% if afi_config.unsuppress_map is defined and afi_config.unsuppress_map is not none %} + neighbor {{ neighbor }} unsuppress-map {{ afi_config.unsuppress_map }} +{% endif %} +{% if afi_config.disable_send_community is defined and afi_config.disable_send_community.extended is defined %} + no neighbor {{ neighbor }} send-community extended +{% endif %} +{% if afi_config.disable_send_community is defined and afi_config.disable_send_community.standard is defined %} + no neighbor {{ neighbor }} send-community standard {% endif %} neighbor {{ neighbor }} activate exit-address-family @@ -132,62 +185,122 @@ {% endif %} {% endmacro %} ! -router bgp {{ asn }} - no bgp default ipv4-unicast +router bgp {{ local_as }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} +{% if parameters is defined and parameters.ebgp_requires_policy is defined %} + bgp ebgp-requires-policy +{% else %} + no bgp ebgp-requires-policy +{% endif %} +{# Workaround for T2100 until we have decided about a migration script #} + no bgp network import-check {% if address_family is defined and address_family is not none %} -{% for af in address_family %} +{% for afi, afi_config in address_family.items() %} ! -{% if af == 'ipv4_unicast' %} +{% if afi == 'ipv4_unicast' %} address-family ipv4 unicast -{% elif af == 'ipv6_unicast' %} +{% elif afi == 'ipv6_unicast' %} address-family ipv6 unicast +{% elif afi == 'l2vpn_evpn' %} + address-family l2vpn evpn {% endif %} -{% if address_family[af].aggregate_address is defined and address_family[af].aggregate_address is not none %} -{% for ip in address_family[af].aggregate_address %} - aggregate-address {{ ip }}{{ ' as-set' if address_family[af].aggregate_address[ip].as_set is defined }}{{ ' summary-only' if address_family[af].aggregate_address[ip].summary_only is defined }} +{% if afi_config.aggregate_address is defined and afi_config.aggregate_address is not none %} +{% for ip in afi_config.aggregate_address %} + aggregate-address {{ ip }}{{ ' as-set' if afi_config.aggregate_address[ip].as_set is defined }}{{ ' summary-only' if afi_config.aggregate_address[ip].summary_only is defined }} {% endfor %} {% endif %} -{% if address_family[af].redistribute is defined and address_family[af].redistribute is not none %} -{% for protocol in address_family[af].redistribute %} +{% if afi_config.maximum_paths is defined and afi_config.maximum_paths.ebgp is defined and afi_config.maximum_paths.ebgp is not none %} + maximum-paths {{ afi_config.maximum_paths.ebgp }} +{% endif %} +{% if afi_config.maximum_paths is defined and afi_config.maximum_paths.ibgp is defined and afi_config.maximum_paths.ibgp is not none %} + maximum-paths ibgp {{ afi_config.maximum_paths.ibgp }} +{% endif %} +{% if afi_config.redistribute is defined and afi_config.redistribute is not none %} +{% for protocol in afi_config.redistribute %} {% if protocol == 'table' %} - redistribute table {{ address_family[af].redistribute[protocol].table }} + redistribute table {{ afi_config.redistribute[protocol].table }} {% else %} - redistribute {{ protocol }}{% if address_family[af].redistribute[protocol].metric is defined %} metric {{ address_family[af].redistribute[protocol].metric }}{% endif %}{% if address_family[af].redistribute[protocol].route_map is defined %} route-map {{ address_family[af].redistribute[protocol].route_map }}{% endif %} +{% set redistribution_protocol = protocol %} +{% if protocol == 'ospfv3' %} +{% set redistribution_protocol = 'ospf6' %} +{% endif %} + redistribute {{ redistribution_protocol }}{% if afi_config.redistribute[protocol].metric is defined %} metric {{ afi_config.redistribute[protocol].metric }}{% endif %}{% if afi_config.redistribute[protocol].route_map is defined %} route-map {{ afi_config.redistribute[protocol].route_map }}{% endif %} {####### we need this blank line!! #######} {% endif %} {% endfor %} {% endif %} -{% if address_family[af].network is defined and address_family[af].network is not none %} -{% for network in address_family[af].network %} - network {{ network }}{% if address_family[af].network[network].route_map is defined %} route-map {{ address_family[af].network[network].route_map }}{% endif %}{% if address_family[af].network[network].backdoor is defined %} backdoor{% endif %} +{% if afi_config.network is defined and afi_config.network is not none %} +{% for network in afi_config.network %} + network {{ network }}{% if afi_config.network[network].route_map is defined %} route-map {{ afi_config.network[network].route_map }}{% endif %}{% if afi_config.network[network].backdoor is defined %} backdoor{% endif %} {####### we need this blank line!! #######} {% endfor %} {% endif %} +{% if afi_config.advertise_all_vni is defined %} + advertise-all-vni +{% endif %} +{% if afi_config.advertise_default_gw is defined %} + advertise-default-gw +{% endif %} +{% if afi_config.advertise_pip is defined and afi_config.advertise_pip is not none %} + advertise-pip ip {{ afi_config.advertise_pip }} +{% endif %} +{% if afi_config.advertise_svi_ip is defined %} + advertise-svi-ip +{% endif %} +{% if afi_config.rt_auto_derive is defined %} + autort rfc8365-compatible +{% endif %} +{% if afi_config.flooding is defined and afi_config.flooding.disable is defined %} + flooding disable +{% endif %} +{% if afi_config.flooding is defined and afi_config.flooding.head_end_replication is defined %} + flooding head-end-replication +{% endif %} +{% if afi_config.rd is defined and afi_config.rd is not none %} + rd {{ afi_config.rd }} +{% endif %} +{% if afi_config.route_target is defined and afi_config.route_target is not none %} +{% if afi_config.route_target.both is defined and afi_config.route_target.both is not none %} + route-target both {{ afi_config.route_target.both }} +{% endif %} +{% if afi_config.route_target.export is defined and afi_config.route_target.export is not none %} + route-target export {{ afi_config.route_target.export }} +{% endif %} +{% if afi_config.route_target.import is defined and afi_config.route_target.import is not none %} + route-target import {{ afi_config.route_target.import }} +{% endif %} +{% endif %} +{% if afi_config.vni is defined and afi_config.vni is not none %} +{% for vni, vni_config in afi_config.vni.items() %} + vni {{ vni }} +{% if vni_config.advertise_default_gw is defined %} + advertise-default-gw +{% endif %} +{% if vni_config.advertise_svi_ip is defined %} + advertise-svi-ip +{% endif %} +{% if vni_config.rd is defined and vni_config.rd is not none %} + rd {{ vni_config.rd }} +{% endif %} +{% if vni_config.route_target is defined and vni_config.route_target is not none %} +{% if vni_config.route_target.both is defined and vni_config.route_target.both is not none %} + route-target both {{ vni_config.route_target.both }} +{% endif %} +{% if vni_config.route_target.export is defined and vni_config.route_target.export is not none %} + route-target export {{ vni_config.route_target.export }} +{% endif %} +{% if vni_config.route_target.import is defined and vni_config.route_target.import is not none %} + route-target import {{ vni_config.route_target.import }} +{% endif %} +{% endif %} + exit-vni +{% endfor %} +{% endif %} exit-address-family {% endfor %} {% endif %} ! -{# set protocols bgp xxxx maximum-paths ibgp x, Generated by default for afi_4 #} -{# We don't have this parameter in afi_6. But this is supported in FRR #} -{% if maximum_paths is defined and maximum_paths is not none %} -{% if maximum_paths.ebgp is defined and maximum_paths.ebgp is not none %} - ! - address-family ipv4 unicast - maximum-paths {{ maximum_paths.ebgp }} - exit-address-family - ! -{% endif %} -{% if maximum_paths.ibgp is defined and maximum_paths.ibgp is not none %} - ! - address-family ipv4 unicast - maximum-paths ibgp {{ maximum_paths.ibgp }} - exit-address-family - ! -{% endif %} -{% endif %} - ! {% if peer_group is defined and peer_group is not none %} {% for peer, config in peer_group.items() %} {{ bgp_neighbor(peer, config, true) }} @@ -195,11 +308,21 @@ router bgp {{ asn }} {% endif %} ! {% if neighbor is defined and neighbor is not none %} -{% for n, config in neighbor.items() %} -{{ bgp_neighbor(n, config) }} +{% for peer, config in neighbor.items() %} +{{ bgp_neighbor(peer, config) }} {% endfor %} {% endif %} ! +{% if listen is defined %} +{% if listen.limit is defined and listen.limit is not none %} + bgp listen limit {{ listen.limit }} +{% endif %} +{% for prefix, options in listen.range.items() %} +{% if options.peer_group is defined and options.peer_group is not none %} + bgp listen range {{ prefix }} peer-group {{ options.peer_group }} +{% endif %} +{% endfor %} +{% endif %} {% if parameters is defined %} {% if parameters.always_compare_med is defined %} bgp always-compare-med @@ -237,7 +360,6 @@ router bgp {{ asn }} bgp default local-preference {{ parameters.default.local_pref }} {% endif %} {% if parameters.default.no_ipv4_unicast is defined %} -{# We use this is parameter as default in template (5-th string) #} no bgp default ipv4-unicast {% endif %} {% endif %} @@ -261,6 +383,9 @@ router bgp {{ asn }} {% if parameters.graceful_restart is defined %} bgp graceful-restart {{ 'stalepath-time ' + parameters.graceful_restart.stalepath_time if parameters.graceful_restart.stalepath_time is defined }} {% endif %} +{% if parameters.graceful_shutdown is defined %} + bgp graceful-shutdown +{% endif %} {% if parameters.log_neighbor_changes is defined %} bgp log-neighbor-changes {% endif %} @@ -280,8 +405,8 @@ router bgp {{ asn }} {% if timers is defined and timers.keepalive is defined and timers.holdtime is defined %} timers bgp {{ timers.keepalive }} {{ timers.holdtime }} {% endif %} - ! +! {% if route_map is defined and route_map is not none %} - ip protocol bgp route-map {{ route_map }} +ip protocol bgp route-map {{ route_map }} {% endif %} - ! +! diff --git a/data/templates/frr/isis.frr.tmpl b/data/templates/frr/isis.frr.tmpl index 0477f2599..7f996b134 100644 --- a/data/templates/frr/isis.frr.tmpl +++ b/data/templates/frr/isis.frr.tmpl @@ -1,5 +1,5 @@ ! -router isis {{ process }} +router isis VyOS {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} net {{ net }} {% if dynamic_hostname is defined %} hostname dynamic @@ -133,8 +133,8 @@ router isis {{ process }} ! {% if interface is defined and interface is not none %} {% for iface, iface_config in interface.items() %} -interface {{ iface }} - ip router isis {{ process }} +interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} + ip router isis VyOS {% if iface_config.bfd is defined %} isis bfd {% endif %} @@ -168,8 +168,8 @@ interface {{ iface }} {% if iface_config.psnp_interval is defined and iface_config.psnp_interval is not none %} isis psnp-interval {{ iface_config.psnp_interval }} {% endif %} -{% if iface_config.three_way_handshake is defined %} - isis three-way-handshake +{% if iface_config.no_three_way_handshake is defined %} + no isis three-way-handshake {% endif %} {% endfor %} {% endif %} diff --git a/data/templates/frr/ospf.frr.tmpl b/data/templates/frr/ospf.frr.tmpl new file mode 100644 index 000000000..a47c64c89 --- /dev/null +++ b/data/templates/frr/ospf.frr.tmpl @@ -0,0 +1,183 @@ +! +{% if interface is defined and interface is not none %} +{% for iface, iface_config in interface.items() %} +interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} +{% if iface_config.authentication is defined and iface_config.authentication is not none %} +{% if iface_config.authentication.plaintext_password is defined and iface_config.authentication.plaintext_password is not none %} + ip ospf authentication-key {{ iface_config.authentication.plaintext_password }} +{% elif iface_config.authentication.md5 is defined %} + ip ospf authentication message-digest +{% if iface_config.authentication.md5.key_id is defined and iface_config.authentication.md5.key_id is not none %} +{% for key, key_config in iface_config.authentication.md5.key_id.items() %} + ip ospf message-digest-key {{ key }} md5 {{ key_config.md5_key }} +{% endfor %} +{% endif %} +{% endif %} +{% endif %} +{% if iface_config.cost is defined and iface_config.cost is not none %} + ip ospf cost {{ iface_config.cost }} +{% endif %} +{% if iface_config.priority is defined and iface_config.priority is not none %} + ip ospf priority {{ iface_config.priority }} +{% endif %} +{% if iface_config.hello_interval is defined and iface_config.hello_interval is not none %} + ip ospf hello-interval {{ iface_config.hello_interval }} +{% endif %} +{% if iface_config.retransmit_interval is defined and iface_config.retransmit_interval is not none %} + ip ospf retransmit-interval {{ iface_config.retransmit_interval }} +{% endif %} +{% if iface_config.transmit_delay is defined and iface_config.transmit_delay is not none %} + ip ospf transmit-delay {{ iface_config.transmit_delay }} +{% endif %} +{% if iface_config.dead_interval is defined and iface_config.dead_interval is not none %} + ip ospf dead-interval {{ iface_config.dead_interval }} +{% elif iface_config.hello_multiplier is defined and iface_config.hello_multiplier is not none %} + ip ospf dead-interval minimal hello-multiplier {{ iface_config.hello_multiplier }} +{% endif %} +{% if iface_config.bfd is defined %} + ip ospf bfd +{% endif %} +{% if iface_config.mtu_ignore is defined %} + ip ospf mtu-ignore +{% endif %} +{% if iface_config.network is defined and iface_config.network is not none %} + ip ospf network {{ iface_config.network }} +{% endif %} +{% if iface_config.bandwidth is defined and iface_config.bandwidth is not none %} + bandwidth {{ iface_config.bandwidth }} +{% endif %} +! +{% endfor %} +{% endif %} +! +router ospf {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} +{% if access_list is defined and access_list is not none %} +{% for acl, acl_config in access_list.items() %} +{% for protocol in acl_config.export if acl_config.export is defined %} + distribute-list {{ acl }} out {{ protocol }} +{% endfor %} +{% endfor %} +{% endif %} +{% if area is defined and area is not none %} +{% for area_id, area_config in area.items() %} +{% if area_config.area_type is defined and area_config.area_type is not none %} +{% for type, type_config in area_config.area_type.items() if type != 'normal' %} + area {{ area_id }} {{ type }} {{ 'no-summary' if type_config.no_summary is defined }} +{% if type_config.default_cost is defined and type_config.default_cost is not none %} + area {{ area_id }} default-cost {{ type_config.default_cost }} +{% endif %} +{% endfor %} +{% endif %} +{% if area_config.authentication is defined and area_config.authentication is not none %} + area {{ area_id }} authentication {{ 'message-digest' if area_config.authentication == 'md5' }} +{% endif %} +{% for network in area_config.network if area_config.network is defined %} + network {{ network }} area {{ area_id }} +{% endfor %} +{% if area_config.range is defined and area_config.range is not none %} +{% for range, range_config in area_config.range.items() %} +{% if range_config.cost is defined and range_config.cost is not none %} + area {{ area_id }} range {{ range }} cost {{ range_config.cost }} +{% endif %} +{% if range_config.not_advertise is defined %} + area {{ area_id }} range {{ range }} not-advertise +{% endif %} +{% if range_config.substitute is defined and range_config.substitute is not none %} + area {{ area_id }} range {{ range }} substitute {{ range_config.substitute }} +{% endif %} +{% endfor %} +{% endif %} +{% if area_config.shortcut is defined and area_config.shortcut is not none %} + area {{ area_id }} shortcut {{ area_config.shortcut }} +{% endif %} +{% if area_config.virtual_link is defined and area_config.virtual_link is not none %} +{% for link, link_config in area_config.virtual_link.items() %} +{% if link_config.authentication is defined and link_config.authentication is not none %} +{% if link_config.authentication.plaintext_password is defined and link_config.authentication.plaintext_password is not none %} + area {{ area_id }} virtual-link {{ link }} authentication-key {{ link_config.authentication.plaintext_password }} +{% elif link_config.authentication.md5 is defined and link_config.authentication.md5.key_id is defined and link_config.authentication.md5.key_id is not none %} +{% for key, key_config in link_config.authentication.md5.key_id.items() %} + area {{ area_id }} virtual-link {{ link }} message-digest-key {{ key }} md5 {{ key_config.md5_key }} +{% endfor %} +{% endif %} +{% endif %} +{# The following values are default values #} + area {{ area_id }} virtual-link {{ link }} hello-interval {{ link_config.hello_interval }} retransmit-interval {{ link_config.retransmit_interval }} transmit-delay {{ link_config.transmit_delay }} dead-interval {{ link_config.dead_interval }} +{% endfor %} +{% endif %} +{% endfor %} +{% endif %} +{% if auto_cost is defined and auto_cost.reference_bandwidth is defined and auto_cost.reference_bandwidth is not none %} + auto-cost reference-bandwidth {{ auto_cost.reference_bandwidth }} +{% endif %} +{% if default_information is defined and default_information.originate is defined and default_information.originate is not none %} + default-information originate {{ 'always' if default_information.originate.always is defined }} {{ 'metric ' + default_information.originate.metric if default_information.originate.metric is defined }} {{ 'metric-type ' + default_information.originate.metric_type if default_information.originate.metric_type is defined }} {{ 'route-map ' + default_information.originate.route_map if default_information.originate.route_map is defined }} +{% endif %} +{% if default_metric is defined and default_metric is not none %} + default-metric {{ default_metric }} +{% endif %} +{% if distance is defined and distance is not none %} +{% if distance.global is defined and distance.global is not none %} + distance {{ distance.global }} +{% endif %} +{% if distance.ospf is defined and distance.ospf is not none %} + distance ospf {{ 'intra-area ' + distance.ospf.intra_area if distance.ospf.intra_area is defined }} {{ 'inter-area ' + distance.ospf.inter_area if distance.ospf.inter_area is defined }} {{ 'external ' + distance.ospf.external if distance.ospf.external is defined }} +{% endif %} +{% endif %} +{% if log_adjacency_changes is defined %} + log-adjacency-changes {{ "detail" if log_adjacency_changes.detail is defined }} +{% endif %} +{% if max_metric is defined and max_metric.router_lsa is defined and max_metric.router_lsa is not none %} +{% if max_metric.router_lsa.administrative is defined %} + max-metric router-lsa administrative +{% endif %} +{% if max_metric.router_lsa.on_shutdown is defined and max_metric.router_lsa.on_shutdown is not none %} + max-metric router-lsa on-shutdown {{ max_metric.router_lsa.on_shutdown }} +{% endif %} +{% if max_metric.router_lsa.on_startup is defined and max_metric.router_lsa.on_startup is not none %} + max-metric router-lsa on-startup {{ max_metric.router_lsa.on_startup }} +{% endif %} +{% endif %} +{% if mpls_te is defined and mpls_te.enable is defined %} + mpls-te on + mpls-te router-address {{ mpls_te.router_address }} +{% endif %} +{% if neighbor is defined and neighbor is not none%} +{% for address, address_config in neighbor.items() %} + neighbor {{ address }} {{ 'priority ' + address_config.priority if address_config.priority is defined }} {{ 'poll-interval ' + address_config.poll_interval if address_config.poll_interval is defined }} +{% endfor %} +{% endif %} +{% if parameters is defined and parameters is not none %} +{% if parameters.abr_type is defined and parameters.abr_type is not none %} + ospf abr-type {{ parameters.abr_type }} +{% endif %} +{% if parameters.router_id is defined and parameters.router_id is not none %} + ospf router-id {{ parameters.router_id }} +{% endif %} +{% endif %} +{% for interface in passive_interface if passive_interface is defined %} + passive-interface {{ interface }} +{% endfor %} +{% for interface in passive_interface_exclude if passive_interface_exclude is defined %} +{% if interface.startswith('vlink') %} +{% set interface = interface.upper() %} +{% endif %} + no passive-interface {{ interface }} +{% endfor %} +{% if redistribute is defined and redistribute is not none %} +{% for protocol, options in redistribute.items() %} + redistribute {{ protocol }} {{ 'metric ' + options.metric if options.metric is defined }} {{ 'metric-type ' + options.metric_type if options.metric_type is defined }} {{ 'route-map ' + options.route_map if options.route_map is defined }} +{% endfor %} +{% endif %} +{% if refresh is defined and refresh.timers is defined and refresh.timers is not none %} + refresh timer {{ refresh.timers }} +{% endif %} +{% if timers is defined and timers.throttle is defined and timers.throttle.spf is defined and timers.throttle.spf is not none %} +{# Timer values have default values #} + timers throttle spf {{ timers.throttle.spf.delay }} {{ timers.throttle.spf.initial_holdtime }} {{ timers.throttle.spf.max_holdtime }} +{% endif %} +! +{% if route_map is defined and route_map is not none %} +ip protocol ospf route-map {{ route_map }} +{% endif %} +! diff --git a/data/templates/frr/ospfv3.frr.tmpl b/data/templates/frr/ospfv3.frr.tmpl new file mode 100644 index 000000000..d08972a80 --- /dev/null +++ b/data/templates/frr/ospfv3.frr.tmpl @@ -0,0 +1,84 @@ +! +{% if interface is defined and interface is not none %} +{% for iface, iface_config in interface.items() %} +interface {{ iface }} +{% if iface_config.cost is defined and iface_config.cost is not none %} + ipv6 ospf6 cost {{ iface_config.cost }} +{% endif %} +{% if iface_config.priority is defined and iface_config.priority is not none %} + ipv6 ospf6 priority {{ iface_config.priority }} +{% endif %} +{% if iface_config.hello_interval is defined and iface_config.hello_interval is not none %} + ipv6 ospf6 hello-interval {{ iface_config.hello_interval }} +{% endif %} +{% if iface_config.retransmit_interval is defined and iface_config.retransmit_interval is not none %} + ipv6 ospf6 retransmit-interval {{ iface_config.retransmit_interval }} +{% endif %} +{% if iface_config.transmit_delay is defined and iface_config.transmit_delay is not none %} + ipv6 ospf6 transmit-delay {{ iface_config.transmit_delay }} +{% endif %} +{% if iface_config.dead_interval is defined and iface_config.dead_interval is not none %} + ipv6 ospf6 dead-interval {{ iface_config.dead_interval }} +{% endif %} +{% if iface_config.bfd is defined %} + ipv6 ospf6 bfd +{% endif %} +{% if iface_config.mtu_ignore is defined %} + ipv6 ospf6 mtu-ignore +{% endif %} +{% if iface_config.ifmtu is defined and iface_config.ifmtu is not none %} + ipv6 ospf6 ifmtu {{ iface_config.ifmtu }} +{% endif %} +{% if iface_config.network is defined and iface_config.network is not none %} + ipv6 ospf6 network {{ iface_config.network }} +{% endif %} +{% if iface_config.instance_id is defined and iface_config.instance_id is not none %} + ipv6 ospf6 instance-id {{ iface_config.instance_id }} +{% endif %} +{% if iface_config.passive is defined %} + ipv6 ospf6 passive +{% endif %} +! +{% endfor %} +{% endif %} +! +router ospf6 +{% if area is defined and area is not none %} +{% for area_id, area_config in area.items() %} +{% if area_config.interface is defined and area_config.interface is not none %} +{% for interface in area_config.interface %} + interface {{ interface }} area {{ area_id }} +{% endfor %} +{% endif %} +{% if area_config.range is defined and area_config.range is not none %} +{% for prefix, prefix_config in area_config.range.items() %} + area {{ area_id }} range {{ prefix }} {{ 'advertise' if prefix_config.advertise is defined }} {{ 'not-advertise' if prefix_config.not_advertise is defined }} +{% endfor %} +{% endif %} +{% if area_config.export_list is defined and area_config.export_list is not none %} + area {{ area_id }} export-list {{ area_config.export_list }} +{% endif %} +{% if area_config.import_list is defined and area_config.import_list is not none %} + area {{ area_id }} import-list {{ area_config.import_list }} +{% endif %} +{% endfor %} +{% endif %} +{% if distance is defined and distance is not none %} +{% if distance.global is defined and distance.global is not none %} + distance {{ distance.global }} +{% endif %} +{% if distance.ospfv3 is defined and distance.ospfv3 is not none %} + distance ospf6 {{ 'intra-area ' + distance.ospfv3.intra_area if distance.ospfv3.intra_area is defined }} {{ 'inter-area ' + distance.ospfv3.inter_area if distance.ospfv3.inter_area is defined }} {{ 'external ' + distance.ospfv3.external if distance.ospfv3.external is defined }} +{% endif %} +{% endif %} +{% if parameters is defined and parameters is not none %} +{% if parameters.router_id is defined and parameters.router_id is not none %} + ospf6 router-id {{ parameters.router_id }} +{% endif %} +{% endif %} +{% if redistribute is defined and redistribute is not none %} +{% for protocol, options in redistribute.items() %} + redistribute {{ protocol }} {{ 'route-map ' + options.route_map if options.route_map is defined }} +{% endfor %} +{% endif %} +! diff --git a/data/templates/frr/rip.frr.tmpl b/data/templates/frr/rip.frr.tmpl index 83df4e203..bc92bddf9 100644 --- a/data/templates/frr/rip.frr.tmpl +++ b/data/templates/frr/rip.frr.tmpl @@ -1,143 +1,92 @@ ! -{% if rip_conf %} -router rip -{% if old_default_distance %} -no distance {{old_default_distance}} -{% endif %} -{% if default_distance %} -distance {{default_distance}} -{% endif %} -{% if old_default_originate %} -no default-information originate -{% endif %} -{% if default_originate %} -default-information originate -{% endif %} -{% if old_rip.default_metric %} -no default-metric {{old_rip.default_metric}} -{% endif %} -{% if rip.default_metric %} -default-metric {{rip.default_metric}} -{% endif %} -{% for protocol in old_rip.redist %} -{% if old_rip.redist[protocol]['metric'] and old_rip.redist[protocol]['route_map'] %} -no redistribute {{protocol}} metric {{rip.redist[protocol]['metric']}} route-map {{rip.redist[protocol]['route_map']}} -{% elif old_rip.redist[protocol]['metric'] %} -no redistribute {{protocol}} metric {{old_rip.redist[protocol]['metric']}} -{% elif old_rip.redist[protocol]['route_map'] %} -no redistribute {{protocol}} route-map {{old_rip.redist[protocol]['route_map']}} -{% else %} -no redistribute {{protocol}} -{% endif %} -{% endfor %} -{% for protocol in rip.redist %} -{% if rip.redist[protocol]['metric'] and rip.redist[protocol]['route_map'] %} -redistribute {{protocol}} metric {{rip.redist[protocol]['metric']}} route-map {{rip.redist[protocol]['route_map']}} -{% elif rip.redist[protocol]['metric'] %} -redistribute {{protocol}} metric {{rip.redist[protocol]['metric']}} -{% elif rip.redist[protocol]['route_map'] %} -redistribute {{protocol}} route-map {{rip.redist[protocol]['route_map']}} -{% else %} -redistribute {{protocol}} -{% endif %} -{% endfor %} -{% for iface in old_rip.distribute %} -{% if old_rip.distribute[iface].iface_access_list_in %} -no distribute-list {{old_rip.distribute[iface].iface_access_list_in}} in {{iface}} -{% endif %} -{% if old_rip.distribute[iface].iface_access_list_out %} -no distribute-list {{old_rip.distribute[iface].iface_access_list_out}} out {{iface}} -{% endif %} -{% if old_rip.distribute[iface].iface_prefix_list_in %} -no distribute-list prefix {{old_rip.distribute[iface].iface_prefix_list_in}} in {{iface}} -{% endif %} -{% if old_rip.distribute[iface].iface_prefix_list_out %} -no distribute-list prefix {{old_rip.distribute[iface].iface_prefix_list_out}} out {{iface}} -{% endif %} -{% endfor %} -{% for iface in rip.distribute %} -{% if rip.distribute[iface].iface_access_list_in %} -distribute-list {{rip.distribute[iface].iface_access_list_in}} in {{iface}} -{% endif %} -{% if rip.distribute[iface].iface_access_list_out %} -distribute-list {{rip.distribute[iface].iface_access_list_out}} out {{iface}} -{% endif %} -{% if rip.distribute[iface].iface_prefix_list_in %} -distribute-list prefix {{rip.distribute[iface].iface_prefix_list_in}} in {{iface}} +{# RIP key-chain definition #} +{% if interface is defined and interface is not none %} +{% for iface, iface_config in interface.items() %} +{% if iface_config.authentication is defined and iface_config.authentication.md5 is defined and iface_config.authentication.md5 is not none %} +key chain {{ iface }}-rip +{% for key_id, key_options in iface_config.authentication.md5.items() %} + key {{ key_id }} +{% if key_options.password is defined and key_options.password is not none %} + key-string {{ key_options.password }} +{% endif %} +{% endfor %} +{% endif %} +{% endfor %} {% endif %} -{% if rip.distribute[iface].iface_prefix_list_out %} -distribute-list prefix {{rip.distribute[iface].iface_prefix_list_out}} out {{iface}} -{% endif %} -{% endfor %} -{% if old_rip.dist_acl_in %} -no distribute-list {{old_rip.dist_acl_in}} in -{% endif %} -{% if rip.dist_acl_in %} -distribute-list {{rip.dist_acl_in}} in -{% endif %} -{% if old_rip.dist_acl_out %} -no distribute-list {{old_rip.dist_acl_out}} out -{% endif %} -{% if rip.dist_acl_out %} -distribute-list {{rip.dist_acl_out}} out -{% endif %} -{% if old_rip.dist_prfx_in %} -no distribute-list prefix {{old_rip.dist_prfx_in}} in -{% endif %} -{% if rip.dist_prfx_in %} -distribute-list prefix {{rip.dist_prfx_in}} in -{% endif %} -{% if old_rip.dist_prfx_out %} -no distribute-list prefix {{old_rip.dist_prfx_out}} out -{% endif %} -{% if rip.dist_prfx_out %} -distribute-list prefix {{rip.dist_prfx_out}} out -{% endif %} -{% for network in old_rip.networks %} -no network {{network}} -{% endfor %} -{% for network in rip.networks %} -network {{network}} -{% endfor %} -{% for iface in old_rip.ifaces %} -no network {{iface}} -{% endfor %} -{% for iface in rip.ifaces %} -network {{iface}} -{% endfor %} -{% for neighbor in old_rip.neighbors %} -no neighbor {{neighbor}} -{% endfor %} -{% for neighbor in rip.neighbors %} -neighbor {{neighbor}} -{% endfor %} -{% for net in rip.net_distance %} -{% if rip.net_distance[net].access_list and rip.net_distance[net].distance %} -distance {{rip.net_distance[net].distance}} {{net}} {{rip.net_distance[net].access_list}} -{% else %} -distance {{rip.net_distance[net].distance}} {{net}} -{% endif %} -{% endfor %} -{% for passive_iface in old_rip.passive_iface %} -no passive-interface {{passive_iface}} -{% endfor %} -{% for passive_iface in rip.passive_iface %} -passive-interface {{passive_iface}} -{% endfor %} -{% for route in old_rip.route %} -no route {{route}} -{% endfor %} -{% for route in rip.route %} -route {{route}} -{% endfor %} -{% if old_rip.timer_update or old_rip.timer_timeout or old_rip.timer_garbage %} -no timers basic -{% endif %} -{% if rip.timer_update or rip.timer_timeout or rip.timer_garbage %} -timers basic {{rip.timer_update}} {{rip.timer_timeout}} {{rip.timer_garbage}} +! +{# Interface specific configuration #} +{% if interface is defined and interface is not none %} +{% for iface, iface_config in interface.items() %} +interface {{ iface }} +{% if iface_config.authentication is defined and iface_config.authentication.plaintext_password is defined and iface_config.authentication.plaintext_password is not none %} + ip rip authentication mode text + ip rip authentication string {{ iface_config.authentication.plaintext_password }} +{% elif iface_config.authentication is defined and iface_config.authentication.md5 is defined and iface_config.authentication.md5 is not none %} + ip rip authentication key-chain {{ iface }}-rip + ip rip authentication mode md5 +{% endif %} +{% if iface_config.split_horizon is defined and iface_config.split_horizon.disable is defined %} + no ip rip split-horizon +{% endif %} +{% if iface_config.split_horizon is defined and iface_config.split_horizon.poison_reverse is defined %} + ip rip split-horizon poisoned-reverse +{% endif %} +{% endfor %} {% endif %} ! -{% else %} -no router rip +router rip +{% if default_distance is defined and default_distance is not none %} + distance {{ default_distance }} +{% endif %} +{% if network_distance is defined and network_distance is not none %} +{% for network, network_config in network_distance.items() %} +{% if network_config.distance is defined and network_config.distance is not none %} + distance {{ network_config.distance }} {{ network }} +{% endif %} +{% endfor %} +{% endif %} +{% if neighbor is defined and neighbor is not none %} +{% for address in neighbor %} + neighbor {{ address }} +{% endfor %} +{% endif %} +{% if distribute_list is defined and distribute_list is not none %} +{% if distribute_list.access_list is defined and distribute_list.access_list is not none %} +{% if distribute_list.access_list.in is defined and distribute_list.access_list.in is not none %} + distribute-list {{ distribute_list.access_list.in }} in +{% endif %} +{% if distribute_list.access_list.out is defined and distribute_list.access_list.out is not none %} + distribute-list {{ distribute_list.access_list.out }} out +{% endif %} +{% endif %} +{% if distribute_list.interface is defined and distribute_list.interface is not none %} +{% for interface, interface_config in distribute_list.interface.items() %} +{% if interface_config.access_list is defined and interface_config.access_list is not none %} +{% if interface_config.access_list.in is defined and interface_config.access_list.in is not none %} + distribute-list {{ interface_config.access_list.in }} in {{ interface }} +{% endif %} +{% if interface_config.access_list.out is defined and interface_config.access_list.out is not none %} + distribute-list {{ interface_config.access_list.out }} out {{ interface }} +{% endif %} +{% endif %} +{% if interface_config.prefix_list is defined and interface_config.prefix_list is not none %} +{% if interface_config.prefix_list.in is defined and interface_config.prefix_list.in is not none %} + distribute-list prefix {{ interface_config.prefix_list.in }} in {{ interface }} +{% endif %} +{% if interface_config.prefix_list.out is defined and interface_config.prefix_list.out is not none %} + distribute-list prefix {{ interface_config.prefix_list.out }} out {{ interface }} +{% endif %} +{% endif %} +{% endfor %} +{% endif %} +{% if distribute_list.prefix_list is defined and distribute_list.prefix_list is not none %} +{% if distribute_list.prefix_list.in is defined and distribute_list.prefix_list.in is not none %} + distribute-list prefix {{ distribute_list.prefix_list.in }} in +{% endif %} +{% if distribute_list.prefix_list.out is defined and distribute_list.prefix_list.out is not none %} + distribute-list prefix {{ distribute_list.prefix_list.out }} out +{% endif %} +{% endif %} +{% endif %} +{% include 'frr/rip_ripng.frr.j2' %} ! -{% endif %} diff --git a/data/templates/frr/rip_ripng.frr.j2 b/data/templates/frr/rip_ripng.frr.j2 new file mode 100644 index 000000000..de180ee6b --- /dev/null +++ b/data/templates/frr/rip_ripng.frr.j2 @@ -0,0 +1,36 @@ +{% if default_information is defined and default_information.originate is defined %} + default-information originate +{% endif %} +{% if default_metric is defined and default_metric is not none %} + default-metric {{ default_metric }} +{% endif %} +{% if passive_interface is defined and passive_interface is not none %} +{% for interface in passive_interface %} + passive-interface {{ interface }} +{% endfor %} +{% endif %} +{% if network is defined and network is not none %} +{% for prefix in network %} + network {{ prefix }} +{% endfor %} +{% endif %} +{% if interface is defined and interface is not none %} +{% for ifname in interface %} + network {{ ifname }} +{% endfor %} +{% endif %} +{% if route is defined and route is not none %} +{% for prefix in route %} + route {{ prefix }} +{% endfor %} +{% endif %} +{# timers have default values #} + timers basic {{ timers['update'] }} {{ timers.timeout }} {{ timers.garbage_collection }} +{% if redistribute is defined and redistribute is not none %} +{% for protocol, protocol_config in redistribute.items() %} +{% if protocol == 'ospfv3' %} +{% set protocol = 'ospf6' %} +{% endif %} + redistribute {{ protocol }} {{ 'metric ' + protocol_config.metric if protocol_config.metric is defined }} {{ 'route-map ' + protocol_config.route_map if protocol_config.route_map is defined }} +{% endfor %} +{% endif %} diff --git a/data/templates/frr/ripng.frr.tmpl b/data/templates/frr/ripng.frr.tmpl new file mode 100644 index 000000000..25df15121 --- /dev/null +++ b/data/templates/frr/ripng.frr.tmpl @@ -0,0 +1,60 @@ +! +{# Interface specific configuration #} +{% if interface is defined and interface is not none %} +{% for iface, iface_config in interface.items() %} +interface {{ iface }} +{% if iface_config.split_horizon is defined and iface_config.split_horizon.disable is defined %} + no ipv6 rip split-horizon +{% endif %} +{% if iface_config.split_horizon is defined and iface_config.split_horizon.poison_reverse is defined %} + ipv6 rip split-horizon poisoned-reverse +{% endif %} +{% endfor %} +{% endif %} +! +router ripng +{% if aggregate_address is defined and aggregate_address is not none %} +{% for prefix in aggregate_address %} + aggregate-address {{ prefix }} +{% endfor %} +{% endif %} +{% if distribute_list is defined and distribute_list is not none %} +{% if distribute_list.access_list is defined and distribute_list.access_list is not none %} +{% if distribute_list.access_list.in is defined and distribute_list.access_list.in is not none %} + ipv6 distribute-list {{ distribute_list.access_list.in }} in +{% endif %} +{% if distribute_list.access_list.out is defined and distribute_list.access_list.out is not none %} + ipv6 distribute-list {{ distribute_list.access_list.out }} out +{% endif %} +{% endif %} +{% if distribute_list.interface is defined and distribute_list.interface is not none %} +{% for interface, interface_config in distribute_list.interface.items() %} +{% if interface_config.access_list is defined and interface_config.access_list is not none %} +{% if interface_config.access_list.in is defined and interface_config.access_list.in is not none %} + ipv6 distribute-list {{ interface_config.access_list.in }} in {{ interface }} +{% endif %} +{% if interface_config.access_list.out is defined and interface_config.access_list.out is not none %} + ipv6 distribute-list {{ interface_config.access_list.out }} out {{ interface }} +{% endif %} +{% endif %} +{% if interface_config.prefix_list is defined and interface_config.prefix_list is not none %} +{% if interface_config.prefix_list.in is defined and interface_config.prefix_list.in is not none %} + ipv6 distribute-list prefix {{ interface_config.prefix_list.in }} in {{ interface }} +{% endif %} +{% if interface_config.prefix_list.out is defined and interface_config.prefix_list.out is not none %} + ipv6 distribute-list prefix {{ interface_config.prefix_list.out }} out {{ interface }} +{% endif %} +{% endif %} +{% endfor %} +{% endif %} +{% if distribute_list.prefix_list is defined and distribute_list.prefix_list is not none %} +{% if distribute_list.prefix_list.in is defined and distribute_list.prefix_list.in is not none %} + ipv6 distribute-list prefix {{ distribute_list.prefix_list.in }} in +{% endif %} +{% if distribute_list.prefix_list.out is defined and distribute_list.prefix_list.out is not none %} + ipv6 distribute-list prefix {{ distribute_list.prefix_list.out }} out +{% endif %} +{% endif %} +{% endif %} +{% include 'frr/rip_ripng.frr.j2' %} +! diff --git a/data/templates/frr/rpki.frr.tmpl b/data/templates/frr/rpki.frr.tmpl new file mode 100644 index 000000000..fbdfa27c3 --- /dev/null +++ b/data/templates/frr/rpki.frr.tmpl @@ -0,0 +1,17 @@ +! +{# as FRR does not support deleting the entire rpki section we leave it in place even when it's empty #} +rpki +{% if cache is defined and cache is not none %} +{% for peer, peer_config in cache.items() %} +{# port is mandatory and preference uses a default value #} +{% if peer_config.ssh is defined and peer_config.ssh.username is defined and peer_config.ssh.username is not none %} + rpki cache {{ peer | replace('_', '-') }} {{ peer_config.port }} {{ peer_config.ssh.username }} {{ peer_config.ssh.private_key_file }} {{ peer_config.ssh.public_key_file }} {{ peer_config.ssh.known_hosts_file }} preference {{ peer_config.preference }} +{% else %} + rpki cache {{ peer | replace('_', '-') }} {{ peer_config.port }} preference {{ peer_config.preference }} +{% endif %} +{% endfor %} +{% endif %} +{% if polling_period is defined and polling_period is not none %} + rpki polling_period {{ polling_period }} +{% endif %} +! diff --git a/data/templates/frr/static.frr.tmpl b/data/templates/frr/static.frr.tmpl new file mode 100644 index 000000000..db59a44c2 --- /dev/null +++ b/data/templates/frr/static.frr.tmpl @@ -0,0 +1,49 @@ +{% from 'frr/static_routes_macro.j2' import static_routes %} +! +{% set ip_prefix = 'ip' %} +{% set ipv6_prefix = 'ipv6' %} +{% if vrf is defined and vrf is not none %} +{# We need to add an additional whitespace in front of the prefix #} +{# when VRFs are in use, thus we use a variable for prefix handling #} +{% set ip_prefix = ' ip' %} +{% set ipv6_prefix = ' ipv6' %} +vrf {{ vrf }} +{% endif %} +{# IPv4 routing #} +{% if route is defined and route is not none %} +{% for prefix, prefix_config in route.items() %} +{{ static_routes(ip_prefix, prefix, prefix_config) }} +{%- endfor -%} +{% endif %} +{# IPv6 routing #} +{% if route6 is defined and route6 is not none %} +{% for prefix, prefix_config in route6.items() %} +{{ static_routes(ipv6_prefix, prefix, prefix_config) }} +{%- endfor -%} +{% endif %} +{% if vrf is defined and vrf is not none %} + exit-vrf +{% endif %} +! +{# Policy route tables #} +{% if table is defined and table is not none %} +{% for table_id, table_config in table.items() %} +{% if table_config.route is defined and table_config.route is not none %} +{% for prefix, prefix_config in table_config.route.items() %} +{{ static_routes('ip', prefix, prefix_config, table_id) }} +{%- endfor -%} +{% endif %} +! +{% if table_config.route6 is defined and table_config.route6 is not none %} +{% for prefix, prefix_config in table_config.route6.items() %} +{{ static_routes('ipv6', prefix, prefix_config, table_id) }} +{%- endfor -%} +{% endif %} +! +{% endfor %} +{% endif %} +! +{% if route_map is defined and route_map is not none %} +ip protocol static route-map {{ route_map }} +! +{% endif %} diff --git a/data/templates/frr/static_routes_macro.j2 b/data/templates/frr/static_routes_macro.j2 new file mode 100644 index 000000000..f10b58047 --- /dev/null +++ b/data/templates/frr/static_routes_macro.j2 @@ -0,0 +1,21 @@ +{% macro static_routes(ip_ipv6, prefix, prefix_config, table=None) %} +{% if prefix_config.blackhole is defined %} +{{ ip_ipv6 }} route {{ prefix }} blackhole {{ prefix_config.blackhole.distance if prefix_config.blackhole.distance is defined }} {{ 'tag ' + prefix_config.blackhole.tag if prefix_config.blackhole.tag is defined }} {{ 'table ' + table if table is defined and table is not none }} +{% endif %} +{% if prefix_config.dhcp_interface is defined and prefix_config.dhcp_interface is not none %} +{% set next_hop = prefix_config.dhcp_interface | get_dhcp_router %} +{% if next_hop is defined and next_hop is not none %} +{{ ip_ipv6 }} route {{ prefix }} {{ next_hop }} +{% endif %} +{% endif %} +{% if prefix_config.interface is defined and prefix_config.interface is not none %} +{% for interface, interface_config in prefix_config.interface.items() if interface_config.disable is not defined %} +{{ ip_ipv6 }} route {{ prefix }} {{ interface }} {{ interface_config.distance if interface_config.distance is defined }} {{ 'nexthop-vrf ' + interface_config.vrf if interface_config.vrf is defined }} {{ 'table ' + table if table is defined and table is not none }} +{% endfor %} +{% endif %} +{% if prefix_config.next_hop is defined and prefix_config.next_hop is not none %} +{% for next_hop, next_hop_config in prefix_config.next_hop.items() if next_hop_config.disable is not defined %} +{{ ip_ipv6 }} route {{ prefix }} {{ next_hop }} {{ next_hop_config.interface if next_hop_config.interface is defined }} {{ next_hop_config.distance if next_hop_config.distance is defined }} {{ 'nexthop-vrf ' + next_hop_config.vrf if next_hop_config.vrf is defined }} {{ 'table ' + table if table is defined and table is not none }} +{% endfor %} +{% endif %} +{% endmacro %} diff --git a/data/templates/https/nginx.default.tmpl b/data/templates/https/nginx.default.tmpl index 855ebff4f..916764410 100644 --- a/data/templates/https/nginx.default.tmpl +++ b/data/templates/https/nginx.default.tmpl @@ -40,9 +40,11 @@ server { {% endif %} # proxy settings for HTTP API, if enabled; 503, if not - location ~ /(retrieve|configure|config-file|image|generate|show) { + location ~ /(retrieve|configure|config-file|image|generate|show|docs|openapi.json|redoc) { {% if server.api %} proxy_pass http://localhost:{{ server.api.port }}; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 600; proxy_buffering off; {% else %} @@ -50,6 +52,7 @@ server { {% endif %} } + error_page 497 =301 https://$host:{{ server.port }}$request_uri; error_page 501 502 503 =200 @50*_json; {% if api_set %} diff --git a/data/templates/login/authorized_keys.tmpl b/data/templates/login/authorized_keys.tmpl new file mode 100644 index 000000000..639a80e1d --- /dev/null +++ b/data/templates/login/authorized_keys.tmpl @@ -0,0 +1,9 @@ +### Automatically generated by system-login.py ### + +{% if authentication is defined and authentication.public_keys is defined and authentication.public_keys is not none %} +{% for key, key_options in authentication.public_keys.items() %} +{# The whitespace after options is wisely chosen #} +{{ key_options.options + ' ' if key_options.options is defined }}{{ key_options.type }} {{ key_options.key }} {{ key }} +{% endfor %} +{% endif %} + diff --git a/data/templates/login/pam_radius_auth.conf.tmpl b/data/templates/login/pam_radius_auth.conf.tmpl new file mode 100644 index 000000000..fad8e7dcb --- /dev/null +++ b/data/templates/login/pam_radius_auth.conf.tmpl @@ -0,0 +1,36 @@ +# Automatically generated by system-login.py +# RADIUS configuration file + +{% if radius is defined and radius is not none %} +{# RADIUS IPv6 source address must be specified in [] notation #} +{% set source_address = namespace() %} +{% if radius.source_address is defined and radius.source_address is not none %} +{% for address in radius.source_address %} +{% if address | is_ipv4 %} +{% set source_address.ipv4 = address %} +{% elif address | is_ipv6 %} +{% set source_address.ipv6 = "[" + address + "]" %} +{% endif %} +{% endfor %} +{% endif %} +{% if radius.server is defined and radius.server is not none %} +# server[:port] shared_secret timeout source_ip +{# .items() returns a tuple of two elements: key and value. 1 relates to the 2nd element i.e. the value and .priority relates to the key from the internal dict #} +{% for server, options in radius.server.items() | sort(attribute='1.priority') if not options.disabled %} +{# RADIUS IPv6 servers must be specified in [] notation #} +{% if server | is_ipv4 %} +{{ server }}:{{ options.port }} {{ "%-25s" | format(options.key) }} {{ "%-10s" | format(options.timeout) }} {{ source_address.ipv4 if source_address.ipv4 is defined }} +{% else %} +[{{ server }}]:{{ options.port }} {{ "%-25s" | format(options.key) }} {{ "%-10s" | format(options.timeout) }} {{ source_address.ipv6 if source_address.ipv6 is defined }} +{% endif %} +{% endfor %} +{% endif %} + +priv-lvl 15 +mapped_priv_user radius_priv_user + +{% if radius.vrf is defined and radius.vrf is not none %} +vrf-name {{ radius.vrf }} +{% endif %} +{% endif %} + diff --git a/data/templates/ntp/ntp.conf.tmpl b/data/templates/ntp/ntpd.conf.tmpl index 3f319c89b..2b56b53c3 100644 --- a/data/templates/ntp/ntp.conf.tmpl +++ b/data/templates/ntp/ntpd.conf.tmpl @@ -36,10 +36,4 @@ interface ignore wildcard {% for address in listen_address %} interface listen {{ address }} {% endfor %} -interface listen 127.0.0.1 -interface listen ::1 -{% else %} -interface ignore wildcard -interface listen 127.0.0.1 -interface listen ::1 {% endif %} diff --git a/data/templates/ntp/override.conf.tmpl b/data/templates/ntp/override.conf.tmpl index 466638e5a..28eb61b21 100644 --- a/data/templates/ntp/override.conf.tmpl +++ b/data/templates/ntp/override.conf.tmpl @@ -1,11 +1,14 @@ -{% set vrf_command = '/sbin/ip vrf exec ' + vrf + ' ' if vrf is defined else '' %} +{% set vrf_command = 'ip vrf exec ' + vrf + ' ' if vrf is defined else '' %} [Unit] StartLimitIntervalSec=0 +ConditionPathExists={{config_file}} After=vyos-router.service [Service] ExecStart= -ExecStart={{vrf_command}}/usr/lib/ntp/ntp-systemd-wrapper -Restart=on-failure +ExecStart={{vrf_command}}/usr/sbin/ntpd -g -p {{config_file | replace('.conf', '.pid') }} -c {{config_file}} -u ntp:ntp +PIDFile= +PIDFile={{config_file | replace('.conf', '.pid') }} +Restart=always RestartSec=10 diff --git a/data/templates/openvpn/client.conf.tmpl b/data/templates/openvpn/client.conf.tmpl index 62387ef7c..e6e15b6ad 100644 --- a/data/templates/openvpn/client.conf.tmpl +++ b/data/templates/openvpn/client.conf.tmpl @@ -23,7 +23,7 @@ ifconfig-ipv6-push {{ ipv6_ip[0] }} {{ ipv6_remote }} push "route-ipv6 {{ route6 }}" {% endfor %} {% for net6 in ipv6_subnet %} -iroute {{ net6 }} +iroute-ipv6 {{ net6 }} {% endfor %} {% endif %} {% if disable is defined %} diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl index b3b0c936a..79288e40f 100644 --- a/data/templates/openvpn/server.conf.tmpl +++ b/data/templates/openvpn/server.conf.tmpl @@ -281,6 +281,10 @@ compat-names # Custom options added by user (not validated) # {% for option in openvpn_option %} -{{ option }} +{% for argument in option.split('--') %} +{% if argument is defined and argument != '' %} +--{{ argument }} +{% endif %} +{% endfor %} {% endfor %} {% endif %} diff --git a/data/templates/pppoe/ip-down.script.tmpl b/data/templates/pppoe/ip-down.script.tmpl index 5e119f796..bac4155d6 100644 --- a/data/templates/pppoe/ip-down.script.tmpl +++ b/data/templates/pppoe/ip-down.script.tmpl @@ -23,10 +23,12 @@ if [ -d /sys/class/net/{{ ifname }}/upper_* ]; then VRF_NAME="vrf ${VRF_NAME}" fi -# Always delete default route when interface goes down +{% if default_route != 'none' %} +# Always delete default route when interface goes down if we installed it vtysh -c "conf t" ${VRF_NAME} -c "no ip route 0.0.0.0/0 {{ ifname }} ${VRF_NAME}" -{% if ipv6 is defined and ipv6.address is defined and ipv6.address.autoconf is defined %} +{% if ipv6 is defined and ipv6.address is defined and ipv6.address.autoconf is defined %} vtysh -c "conf t" ${VRF_NAME} -c "no ipv6 route ::/0 {{ ifname }} ${VRF_NAME}" +{% endif %} {% endif %} {% endif %} diff --git a/data/templates/pppoe/peer.tmpl b/data/templates/pppoe/peer.tmpl index db2cc6188..0f78f9384 100644 --- a/data/templates/pppoe/peer.tmpl +++ b/data/templates/pppoe/peer.tmpl @@ -47,8 +47,8 @@ mtu {{ mtu }} mru {{ mtu }} {% if authentication is defined %} -{{ "user " + authentication.user if authentication.user is defined }} -{{ "password " + authentication.password if authentication.password is defined }} +{{ 'user "' + authentication.user + '"' if authentication.user is defined }} +{{ 'password "' + authentication.password + '"' if authentication.password is defined }} {% endif %} {{ "usepeerdns" if no_peer_dns is not defined }} diff --git a/data/templates/proxy-ndp/ndppd.conf.tmpl b/data/templates/proxy-ndp/ndppd.conf.tmpl new file mode 100644 index 000000000..0137d8135 --- /dev/null +++ b/data/templates/proxy-ndp/ndppd.conf.tmpl @@ -0,0 +1,44 @@ +######################################################## +# +# autogenerated by nat66.py +# +# The configuration file must define one upstream +# interface. +# +# For some services, such as nat66, because it runs +# stateless, it needs to rely on NDP Proxy to respond +# to NDP requests. +# +# When using nat66 source rules, NDP Proxy needs +# to be enabled +# +######################################################## + +{% set global = namespace(ndppd_interfaces = [],ndppd_prefixs = []) %} +{% if source is defined and source.rule is defined and source.rule is not none %} +{% for rule, config in source.rule.items() if config.disable is not defined %} +{% if config.outbound_interface is defined %} +{% if config.outbound_interface not in global.ndppd_interfaces %} +{% set global.ndppd_interfaces = global.ndppd_interfaces + [config.outbound_interface] %} +{% endif %} +{% if config.translation.prefix is defined %} +{% set global.ndppd_prefixs = global.ndppd_prefixs + [{'interface':config.outbound_interface,'rule':config.translation.prefix}] %} +{% endif %} +{% endif %} +{% endfor %} +{% endif %} + +{% for interface in global.ndppd_interfaces %} +proxy {{ interface }} { + router yes + timeout 500 + ttl 30000 +{% for map in global.ndppd_prefixs %} +{% if map.interface == interface %} + rule {{ map.rule }} { + static + } +{% endif %} +{% endfor %} +} +{% endfor %} diff --git a/data/templates/salt-minion/minion.tmpl b/data/templates/salt-minion/minion.tmpl index 405fb9131..99749b57a 100644 --- a/data/templates/salt-minion/minion.tmpl +++ b/data/templates/salt-minion/minion.tmpl @@ -21,7 +21,9 @@ hash_type: {{ hash }} # location. Remote logging works best when configured to use rsyslogd(8) (e.g.: # ``file:///dev/log``), with rsyslogd(8) configured for network logging. The URI # format is: <file|udp|tcp>://<host|socketpath>:<port-if-required>/<log-facility> -log_file: file:///dev/log +# log_file: file:///dev/log +# +log_file: /var/log/salt/minion # The level of messages to send to the console. # One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. diff --git a/data/templates/snmp/etc.snmpd.conf.tmpl b/data/templates/snmp/etc.snmpd.conf.tmpl index 278506350..db2114fa1 100644 --- a/data/templates/snmp/etc.snmpd.conf.tmpl +++ b/data/templates/snmp/etc.snmpd.conf.tmpl @@ -22,6 +22,10 @@ notificationEvent linkDownTrap linkDown ifIndex ifDescr ifType ifAdminStatus i monitor -r 10 -e linkUpTrap "Generate linkUp" ifOperStatus != 2 monitor -r 10 -e linkDownTrap "Generate linkDown" ifOperStatus == 2 +# Remove all old ifTable entries with the same ifName as newly appeared +# interface (with different ifIndex) - this is the case on e.g. ppp interfaces +interface_replace_old yes + ######################## # configurable section # ######################## diff --git a/data/templates/snmp/override.conf.tmpl b/data/templates/snmp/override.conf.tmpl index e6302a9e1..68f5fd931 100644 --- a/data/templates/snmp/override.conf.tmpl +++ b/data/templates/snmp/override.conf.tmpl @@ -1,4 +1,4 @@ -{% set vrf_command = '/sbin/ip vrf exec ' + vrf + ' ' if vrf is defined else '' %} +{% set vrf_command = 'ip vrf exec ' + vrf + ' ' if vrf is defined else '' %} [Unit] StartLimitIntervalSec=0 After=vyos-router.service @@ -8,6 +8,6 @@ Environment= Environment="MIBSDIR=/usr/share/snmp/mibs:/usr/share/snmp/mibs/iana:/usr/share/snmp/mibs/ietf:/usr/share/mibs/site:/usr/share/snmp/mibs:/usr/share/mibs/iana:/usr/share/mibs/ietf:/usr/share/mibs/netsnmp" ExecStart= ExecStart={{vrf_command}}/usr/sbin/snmpd -LS0-5d -Lf /dev/null -u Debian-snmp -g Debian-snmp -I -ipCidrRouteTable,inetCidrRouteTable -f -p /run/snmpd.pid -Restart=on-failure +Restart=always RestartSec=10 diff --git a/data/templates/squid/sg_acl.conf.tmpl b/data/templates/squid/sg_acl.conf.tmpl index cb1c3ccb0..ce72b173a 100644 --- a/data/templates/squid/sg_acl.conf.tmpl +++ b/data/templates/squid/sg_acl.conf.tmpl @@ -1,18 +1,18 @@ -### generated by service_webproxy.py ###
-dbhome {{ squidguard_db_dir }}
-
-dest {{ category }}-{{ rule }} {
-{% if list_type == 'domains' %}
- domainlist {{ category }}/domains
-{% elif list_type == 'urls' %}
- urllist {{ category }}/urls
-{% elif list_type == 'expressions' %}
- expressionlist {{ category }}/expressions
-{% endif %}
-}
-
-acl {
- default {
- pass all
- }
-}
+### generated by service_webproxy.py ### +dbhome {{ squidguard_db_dir }} + +dest {{ category }}-{{ rule }} { +{% if list_type == 'domains' %} + domainlist {{ category }}/domains +{% elif list_type == 'urls' %} + urllist {{ category }}/urls +{% elif list_type == 'expressions' %} + expressionlist {{ category }}/expressions +{% endif %} +} + +acl { + default { + pass all + } +} diff --git a/data/templates/squid/squidGuard.conf.tmpl b/data/templates/squid/squidGuard.conf.tmpl index 74de3a651..f530d1072 100644 --- a/data/templates/squid/squidGuard.conf.tmpl +++ b/data/templates/squid/squidGuard.conf.tmpl @@ -1,91 +1,91 @@ -### generated by service_webproxy.py ###
-
-{% macro sg_rule(category, log, db_dir) %}
-{% set expressions = db_dir + '/' + category + '/expressions' %}
-dest {{ category }}-default {
- domainlist {{ category }}/domains
- urllist {{ category }}/urls
-{% if expressions | is_file %}
- expressionlist {{ category }}/expressions
-{% endif %}
-{% if log is defined %}
- log blacklist.log
-{% endif %}
-}
-{% endmacro %}
-
-{% if url_filtering is defined and url_filtering.disable is not defined %}
-{% if url_filtering.squidguard is defined and url_filtering.squidguard is not none %}
-{% set sg_config = url_filtering.squidguard %}
-{% set acl = namespace(value='local-ok-default') %}
-{% set acl.value = acl.value + ' !in-addr' if sg_config.allow_ipaddr_url is not defined else acl.value %}
-dbhome {{ squidguard_db_dir }}
-logdir /var/log/squid
-
-rewrite safesearch {
- s@(.*\.google\..*/(custom|search|images|groups|news)?.*q=.*)@\1\&safe=active@i
- s@(.*\..*/yandsearch?.*text=.*)@\1\&fyandex=1@i
- s@(.*\.yahoo\..*/search.*p=.*)@\1\&vm=r@i
- s@(.*\.live\..*/.*q=.*)@\1\&adlt=strict@i
- s@(.*\.msn\..*/.*q=.*)@\1\&adlt=strict@i
- s@(.*\.bing\..*/search.*q=.*)@\1\&adlt=strict@i
- log rewrite.log
-}
-
-{% if sg_config.local_ok is defined and sg_config.local_ok is not none %}
-{% set acl.value = acl.value + ' local-ok-default' %}
-dest local-ok-default {
- domainlist local-ok-default/domains
-}
-{% endif %}
-{% if sg_config.local_ok_url is defined and sg_config.local_ok_url is not none %}
-{% set acl.value = acl.value + ' local-ok-url-default' %}
-dest local-ok-url-default {
- urllist local-ok-url-default/urls
-}
-{% endif %}
-{% if sg_config.local_block is defined and sg_config.local_block is not none %}
-{% set acl.value = acl.value + ' !local-block-default' %}
-dest local-block-default {
- domainlist local-block-default/domains
-}
-{% endif %}
-{% if sg_config.local_block_url is defined and sg_config.local_block_url is not none %}
-{% set acl.value = acl.value + ' !local-block-url-default' %}
-dest local-block-url-default {
- urllist local-block-url-default/urls
-}
-{% endif %}
-{% if sg_config.local_block_keyword is defined and sg_config.local_block_keyword is not none %}
-{% set acl.value = acl.value + ' !local-block-keyword-default' %}
-dest local-block-keyword-default {
- expressionlist local-block-keyword-default/expressions
-}
-{% endif %}
-
-{% if sg_config.block_category is defined and sg_config.block_category is not none %}
-{% for category in sg_config.block_category %}
-{{ sg_rule(category, sg_config.log, squidguard_db_dir) }}
-{% set acl.value = acl.value + ' !' + category + '-default' %}
-{% endfor %}
-{% endif %}
-{% if sg_config.allow_category is defined and sg_config.allow_category is not none %}
-{% for category in sg_config.allow_category %}
-{{ sg_rule(category, False, squidguard_db_dir) }}
-{% set acl.value = acl.value + ' ' + category + '-default' %}
-{% endfor %}
-{% endif %}
-acl {
- default {
-{% if sg_config.enable_safe_search is defined %}
- rewrite safesearch
-{% endif %}
- pass {{ acl.value }} {{ 'none' if sg_config.default_action is defined and sg_config.default_action == 'block' else 'allow' }}
- redirect 302:http://{{ sg_config.redirect_url }}
-{% if sg_config.log is defined and sg_config.log is not none %}
- log blacklist.log
-{% endif %}
- }
-}
-{% endif %}
-{% endif %}
+### generated by service_webproxy.py ### + +{% macro sg_rule(category, log, db_dir) %} +{% set expressions = db_dir + '/' + category + '/expressions' %} +dest {{ category }}-default { + domainlist {{ category }}/domains + urllist {{ category }}/urls +{% if expressions | is_file %} + expressionlist {{ category }}/expressions +{% endif %} +{% if log is defined %} + log blacklist.log +{% endif %} +} +{% endmacro %} + +{% if url_filtering is defined and url_filtering.disable is not defined %} +{% if url_filtering.squidguard is defined and url_filtering.squidguard is not none %} +{% set sg_config = url_filtering.squidguard %} +{% set acl = namespace(value='local-ok-default') %} +{% set acl.value = acl.value + ' !in-addr' if sg_config.allow_ipaddr_url is not defined else acl.value %} +dbhome {{ squidguard_db_dir }} +logdir /var/log/squid + +rewrite safesearch { + s@(.*\.google\..*/(custom|search|images|groups|news)?.*q=.*)@\1\&safe=active@i + s@(.*\..*/yandsearch?.*text=.*)@\1\&fyandex=1@i + s@(.*\.yahoo\..*/search.*p=.*)@\1\&vm=r@i + s@(.*\.live\..*/.*q=.*)@\1\&adlt=strict@i + s@(.*\.msn\..*/.*q=.*)@\1\&adlt=strict@i + s@(.*\.bing\..*/search.*q=.*)@\1\&adlt=strict@i + log rewrite.log +} + +{% if sg_config.local_ok is defined and sg_config.local_ok is not none %} +{% set acl.value = acl.value + ' local-ok-default' %} +dest local-ok-default { + domainlist local-ok-default/domains +} +{% endif %} +{% if sg_config.local_ok_url is defined and sg_config.local_ok_url is not none %} +{% set acl.value = acl.value + ' local-ok-url-default' %} +dest local-ok-url-default { + urllist local-ok-url-default/urls +} +{% endif %} +{% if sg_config.local_block is defined and sg_config.local_block is not none %} +{% set acl.value = acl.value + ' !local-block-default' %} +dest local-block-default { + domainlist local-block-default/domains +} +{% endif %} +{% if sg_config.local_block_url is defined and sg_config.local_block_url is not none %} +{% set acl.value = acl.value + ' !local-block-url-default' %} +dest local-block-url-default { + urllist local-block-url-default/urls +} +{% endif %} +{% if sg_config.local_block_keyword is defined and sg_config.local_block_keyword is not none %} +{% set acl.value = acl.value + ' !local-block-keyword-default' %} +dest local-block-keyword-default { + expressionlist local-block-keyword-default/expressions +} +{% endif %} + +{% if sg_config.block_category is defined and sg_config.block_category is not none %} +{% for category in sg_config.block_category %} +{{ sg_rule(category, sg_config.log, squidguard_db_dir) }} +{% set acl.value = acl.value + ' !' + category + '-default' %} +{% endfor %} +{% endif %} +{% if sg_config.allow_category is defined and sg_config.allow_category is not none %} +{% for category in sg_config.allow_category %} +{{ sg_rule(category, False, squidguard_db_dir) }} +{% set acl.value = acl.value + ' ' + category + '-default' %} +{% endfor %} +{% endif %} +acl { + default { +{% if sg_config.enable_safe_search is defined %} + rewrite safesearch +{% endif %} + pass {{ acl.value }} {{ 'none' if sg_config.default_action is defined and sg_config.default_action == 'block' else 'allow' }} + redirect 302:http://{{ sg_config.redirect_url }} +{% if sg_config.log is defined and sg_config.log is not none %} + log blacklist.log +{% endif %} + } +} +{% endif %} +{% endif %} diff --git a/data/templates/ssh/override.conf.tmpl b/data/templates/ssh/override.conf.tmpl index 843aa927b..5f8f35e89 100644 --- a/data/templates/ssh/override.conf.tmpl +++ b/data/templates/ssh/override.conf.tmpl @@ -1,4 +1,4 @@ -{% set vrf_command = '/sbin/ip vrf exec ' + vrf + ' ' if vrf is defined else '' %} +{% set vrf_command = 'ip vrf exec ' + vrf + ' ' if vrf is defined else '' %} [Unit] StartLimitIntervalSec=0 After=vyos-router.service @@ -7,5 +7,7 @@ ConditionPathExists={{config_file}} [Service] ExecStart= ExecStart={{vrf_command}}/usr/sbin/sshd -f {{config_file}} -D $SSHD_OPTS +Restart=always +RestartPreventExitStatus= RestartSec=10 - +RuntimeDirectoryPreserve=yes diff --git a/data/templates/ssh/sshd_config.tmpl b/data/templates/ssh/sshd_config.tmpl index 52d537aca..2f2b78a66 100644 --- a/data/templates/ssh/sshd_config.tmpl +++ b/data/templates/ssh/sshd_config.tmpl @@ -27,6 +27,8 @@ Banner /etc/issue.net Subsystem sftp /usr/lib/openssh/sftp-server UsePAM yes PermitRootLogin no +PidFile /run/sshd/sshd.pid +AddressFamily any # # User configurable section @@ -47,59 +49,59 @@ LogLevel {{ loglevel | upper }} # Specifies whether password authentication is allowed PasswordAuthentication {{ "no" if disable_password_authentication is defined else "yes" }} -{% if listen_address %} +{% if listen_address is defined and listen_address is not none %} # Specifies the local addresses sshd should listen on {% for address in listen_address %} ListenAddress {{ address }} {% endfor %} {% endif %} -{% if ciphers %} +{% if ciphers is defined and ciphers is not none %} # Specifies the ciphers allowed for protocol version 2 -{% set value = ciphers if ciphers is string else ciphers | join(',') %} +{% set value = ciphers if ciphers is string else ciphers | join(',') %} Ciphers {{ value }} {% endif %} -{% if mac %} +{% if mac is defined and mac is not none %} # Specifies the available MAC (message authentication code) algorithms -{% set value = mac if mac is string else mac | join(',') %} +{% set value = mac if mac is string else mac | join(',') %} MACs {{ value }} {% endif %} -{% if key_exchange %} +{% if key_exchange is defined and key_exchange is not none %} # Specifies the available Key Exchange algorithms -{% set value = key_exchange if key_exchange is string else key_exchange | join(',') %} +{% set value = key_exchange if key_exchange is string else key_exchange | join(',') %} KexAlgorithms {{ value }} {% endif %} -{% if access_control is defined %} -{% if access_control.allow is defined %} +{% if access_control is defined and access_control is not none %} +{% if access_control.allow is defined and access_control.allow is not none %} {% if access_control.allow.user is defined %} # If specified, login is allowed only for user names that match -{% set value = access_control.allow.user if access_control.allow.user is string else access_control.allow.user | join(' ') %} +{% set value = access_control.allow.user if access_control.allow.user is string else access_control.allow.user | join(' ') %} AllowUsers {{ value }} {% endif %} {% if access_control.allow.group is defined %} # If specified, login is allowed only for users whose primary group or supplementary group list matches -{% set value = access_control.allow.group if access_control.allow.group is string else access_control.allow.group | join(' ') %} +{% set value = access_control.allow.group if access_control.allow.group is string else access_control.allow.group | join(' ') %} AllowGroups {{ value }} {% endif %} {% endif %} -{% if access_control.deny is defined %} +{% if access_control.deny is defined and access_control.deny is not none %} {% if access_control.deny.user is defined %} # Login is disallowed for user names that match -{% set value = access_control.deny.user if access_control.deny.user is string else access_control.deny.user | join(' ') %} +{% set value = access_control.deny.user if access_control.deny.user is string else access_control.deny.user | join(' ') %} DenyUsers {{ value }} {% endif %} {% if access_control.deny.group is defined %} # Login is disallowed for users whose primary group or supplementary group list matches -{% set value = access_control.deny.group if access_control.deny.group is string else access_control.deny.group | join(' ') %} +{% set value = access_control.deny.group if access_control.deny.group is string else access_control.deny.group | join(' ') %} DenyGroups {{ value }} {% endif %} {% endif %} {% endif %} -{% if client_keepalive_interval %} +{% if client_keepalive_interval is defined and client_keepalive_interval is not none %} # Sets a timeout interval in seconds after which if no data has been received from the client, # sshd(8) will send a message through the encrypted channel to request a response from the client ClientAliveInterval {{ client_keepalive_interval }} diff --git a/data/templates/syslog/rsyslog.conf.tmpl b/data/templates/syslog/rsyslog.conf.tmpl index 10fbb9d3c..e25ef48d4 100644 --- a/data/templates/syslog/rsyslog.conf.tmpl +++ b/data/templates/syslog/rsyslog.conf.tmpl @@ -2,47 +2,47 @@ ## file based logging {% if files['global']['marker'] %} $ModLoad immark -{% if files['global']['marker-interval'] %} +{% if files['global']['marker-interval'] %} $MarkMessagePeriod {{files['global']['marker-interval']}} -{% endif %} +{% endif %} {% endif %} {% if files['global']['preserver_fqdn'] %} $PreserveFQDN on {% endif %} -{% for file in files %} -$outchannel {{file}},{{files[file]['log-file']}},{{files[file]['max-size']}},{{files[file]['action-on-max-size']}} -{{files[file]['selectors']}} :omfile:${{file}} +{% for file, file_options in files.items() %} +$outchannel {{ file }},{{ file_options['log-file'] }},{{ file_options['max-size'] }},{{ file_options['action-on-max-size'] }} +{{ file_options['selectors'] }} :omfile:${{ file }} {% endfor %} -{% if console %} +{% if console is defined and console is not none %} ## console logging -{% for con in console %} -{{console[con]['selectors']}} /dev/console -{% endfor %} +{% for con, con_options in console.items() %} +{{ con_options['selectors'] }} /dev/console +{% endfor %} {% endif %} -{% if hosts %} +{% if hosts is defined and hosts is not none %} ## remote logging -{% for host in hosts %} -{% if hosts[host]['proto'] == 'tcp' %} -{% if hosts[host]['port'] %} -{% if hosts[host]['oct_count'] %} -{{hosts[host]['selectors']}} @@(o){{host}}:{{hosts[host]['port']}};RSYSLOG_SyslogProtocol23Format +{% for host, host_options in hosts.items() %} +{% if host_options.proto == 'tcp' %} +{% if host_options.port is defined %} +{% if host_options.oct_count is defined %} +{{ host_options.selectors }} @@(o){{ host }}:{{ host_options.port }};RSYSLOG_SyslogProtocol23Format +{% else %} +{{ host_options.selectors }} @@{{ host }}:{{ host_options.port }} +{% endif %} {% else %} -{{hosts[host]['selectors']}} @@{{host}}:{{hosts[host]['port']}} +{{ host_options.selectors }} @@{{ host }} {% endif %} {% else %} -{{hosts[host]['selectors']}} @@{{host}} -{% endif %} -{% else %} -{% if hosts[host]['port'] %} -{{hosts[host]['selectors']}} @{{host}}:{{hosts[host]['port']}} -{% else %} -{{hosts[host]['selectors']}} @{{host}} +{% if host_options['port'] %} +{{ host_options.selectors }} @{{ host | bracketize_ipv6 }}:{{ host_options.port }} +{% else %} +{{ host_options.selectors }} @{{ host | bracketize_ipv6 }} +{% endif %} {% endif %} -{% endif %} -{% endfor %} +{% endfor %} {% endif %} -{% if user %} -{% for u in user %} -{{user[u]['selectors']}} :omusrmsg:{{u}} -{% endfor %} +{% if user is defined and user is not none %} +{% for username, user_options in user.items() %} +{{ user_options.selectors }} :omusrmsg:{{ username }} +{% endfor %} {% endif %} diff --git a/data/templates/system-login/pam_radius_auth.conf.tmpl b/data/templates/system-login/pam_radius_auth.conf.tmpl deleted file mode 100644 index ec2d6df95..000000000 --- a/data/templates/system-login/pam_radius_auth.conf.tmpl +++ /dev/null @@ -1,16 +0,0 @@ -# Automatically generated by system-login.py -# RADIUS configuration file -{% if radius_server %} -# server[:port] shared_secret timeout source_ip -{% for s in radius_server|sort(attribute='priority') if not s.disabled %} -{% set addr_port = s.address + ":" + s.port %} -{{ "%-22s" | format(addr_port) }} {{ "%-25s" | format(s.key) }} {{ "%-10s" | format(s.timeout) }} {{ radius_source_address if radius_source_address }} -{% endfor %} - -priv-lvl 15 -mapped_priv_user radius_priv_user - -{% if radius_vrf %} -vrf-name {{ radius_vrf }} -{% endif %} -{% endif %} diff --git a/data/templates/system/ssh_config.tmpl b/data/templates/system/ssh_config.tmpl index 509bd5479..abc03f069 100644 --- a/data/templates/system/ssh_config.tmpl +++ b/data/templates/system/ssh_config.tmpl @@ -1,3 +1,3 @@ -{% if ssh_client is defined and ssh_client.source_address is defined and ssh_client.source_address is not none %}
-BindAddress {{ ssh_client.source_address }}
-{% endif %}
+{% if ssh_client is defined and ssh_client.source_address is defined and ssh_client.source_address is not none %} +BindAddress {{ ssh_client.source_address }} +{% endif %} diff --git a/data/templates/vrf/vrf.conf.tmpl b/data/templates/vrf/vrf.conf.tmpl index 6d01d2b89..29c0ba08d 100644 --- a/data/templates/vrf/vrf.conf.tmpl +++ b/data/templates/vrf/vrf.conf.tmpl @@ -1,8 +1,9 @@ ### Autogenerated by vrf.py ### # # Routing table ID to name mapping reference - # id vrf name comment -{% for vrf in vrf_add %} -{{ "%-10s" | format(vrf.table) }} {{ "%-16s" | format(vrf.name) }} # {{ vrf.description }} -{% endfor %} +{% if name is defined and name is not none %} +{% for vrf, vrf_config in name.items() %} +{{ "%-10s" | format(vrf_config.table) }} {{ "%-16s" | format(vrf) }} {{ '# ' + vrf_config.description if vrf_config.description is defined and vrf_config.description is not none }} +{% endfor %} +{% endif %} diff --git a/debian/changelog b/debian/changelog index 2b65b22c6..c9d925253 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -vyos-1x (1.3dev0) unstable; urgency=medium +vyos-1x (1.4dev0) unstable; urgency=medium * Dummy changelog entry for vyos-1x repository This is a internal VyOS package and the VyOS package process does not use @@ -7,4 +7,4 @@ vyos-1x (1.3dev0) unstable; urgency=medium The correct verion number of this package is auto-generated by GIT on build-time - -- VyOS maintainers and contributors <maintainers@vyos.io> Wed, 26 Aug 2020 19:07:24 +0000 + -- VyOS maintainers and contributors <maintainers@vyos.io> Mon, 11 Jan 2021 19:02:53 +0100 diff --git a/debian/control b/debian/control index ccdaa8492..24e43d4c8 100644 --- a/debian/control +++ b/debian/control @@ -18,7 +18,10 @@ Build-Depends: python3-lxml, python3-netifaces, python3-nose, + python3-jinja2, + python3-psutil, python3-setuptools, + python3-sphinx, python3-xmltodict, quilt, whois @@ -94,6 +97,7 @@ Depends: python3-jmespath, python3-netaddr, python3-netifaces, + python3-paramiko, python3-psutil, python3-pystache, python3-pyudev, @@ -122,11 +126,13 @@ Depends: udp-broadcast-relay, usb-modeswitch, usbutils, + vyos-http-api-tools, vyos-utils, wide-dhcpv6-client, wireguard-tools, wireless-regdb, - wpasupplicant (>= 0.6.7) + wpasupplicant (>= 0.6.7), + ndppd Description: VyOS configuration scripts and data VyOS configuration scripts, interface definitions, and everything diff --git a/debian/rules b/debian/rules index ab0df7201..8e5aee3e6 100755 --- a/debian/rules +++ b/debian/rules @@ -12,6 +12,8 @@ MIGRATION_SCRIPTS_DIR := opt/vyatta/etc/config-migrate/migrate SYSTEM_SCRIPTS_DIR := usr/libexec/vyos/system SERVICES_DIR := usr/libexec/vyos/services +DEB_TARGET_ARCH := $(shell dpkg-architecture -qDEB_TARGET_ARCH) + %: dh $@ --with python3, --with quilt @@ -20,6 +22,10 @@ override_dh_gencontrol: override_dh_auto_build: make all +ifeq ($(DEB_TARGET_ARCH),amd64) + # Only build XDP on amd64 systems + make vyxdp +endif override_dh_auto_install: dh_auto_install @@ -78,11 +84,6 @@ override_dh_auto_install: mkdir -p $(DIR)/$(VYOS_DATA_DIR) cp -r data/* $(DIR)/$(VYOS_DATA_DIR) - # Install XDP plugins - mkdir -p $(DIR)/$(VYOS_DATA_DIR)/xdp - cp -r src/xdp/xdp_prog_kern.o $(DIR)/$(VYOS_DATA_DIR)/xdp - find src/xdp -perm /a+x -exec cp {} $(DIR)/$(VYOS_SBIN_DIR) \; - # Install etc configuration files mkdir -p $(DIR)/etc cp -r src/etc/* $(DIR)/etc @@ -109,3 +110,10 @@ override_dh_auto_install: # Install system programs mkdir -p $(DIR)/$(VYOS_BIN_DIR) cp -r smoketest/bin/* $(DIR)/$(VYOS_BIN_DIR) + +ifeq ($(DEB_TARGET_ARCH),amd64) + # We only install XDP on amd64 systems + mkdir -p $(DIR)/$(VYOS_DATA_DIR)/xdp + cp -r src/xdp/xdp_prog_kern.o $(DIR)/$(VYOS_DATA_DIR)/xdp + find src/xdp -perm /a+x -exec cp {} $(DIR)/$(VYOS_SBIN_DIR) \; +endif diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst index 92948de12..5fadddc86 100644 --- a/debian/vyos-1x.postinst +++ b/debian/vyos-1x.postinst @@ -20,6 +20,34 @@ if ! grep -q '^minion' /etc/passwd; then adduser --quiet minion users fi +# OpenVPN should get its own user +if ! grep -q '^openvpn' /etc/passwd; then + adduser --quiet --firstuid 100 --system --group --shell /usr/sbin/nologin openvpn +fi + +# Add RADIUS operator user for RADIUS authenticated users to map to +if ! grep -q '^radius_user' /etc/passwd; then + adduser --quiet --firstuid 1001 --disabled-login --ingroup users --gecos "radius user" --shell /bin/vbash radius_user + adduser --quiet radius_user frrvty + adduser --quiet radius_user vyattaop + adduser --quiet radius_user operator + adduser --quiet radius_user adm + adduser --quiet radius_user dip + adduser --quiet radius_user users +fi + +# Add RADIUS admin user for RADIUS authenticated users to map to +if ! grep -q '^radius_priv_user' /etc/passwd; then + adduser --quiet --firstuid 1001 --disabled-login --ingroup vyattacfg --gecos "radius privileged user" --shell /bin/vbash radius_priv_user + adduser --quiet radius_priv_user frrvty + adduser --quiet radius_priv_user vyattacfg + adduser --quiet radius_priv_user sudo + adduser --quiet radius_priv_user adm + adduser --quiet radius_priv_user dip + adduser --quiet radius_priv_user disk + adduser --quiet radius_priv_user users +fi + # add hostsd group for vyos-hostsd if ! grep -q '^hostsd' /etc/group; then addgroup --quiet --system hostsd diff --git a/interface-definitions/arp.xml.in b/interface-definitions/arp.xml.in deleted file mode 100644 index b72f025a8..000000000 --- a/interface-definitions/arp.xml.in +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0"?> -<interfaceDefinition> - <node name="protocols"> - <children> - <node name="static"> - <children> - <tagNode name="arp" owner="${vyos_conf_scripts_dir}/arp.py"> - <properties> - <help>Static ARP translation</help> - <valueHelp> - <format>ipv4</format> - <description>IPv4 destination address</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - </constraint> - </properties> - <children> - <leafNode name="hwaddr"> - <properties> - <help>mac address to translate to</help> - <valueHelp> - <format>h:h:h:h:h:h</format> - <description>Hardware (MAC) address</description> - </valueHelp> - <constraint> - <validator name="mac-address"/> - </constraint> - </properties> - </leafNode> - </children> - </tagNode> - </children> - </node> - </children> - </node> -</interfaceDefinition> diff --git a/interface-definitions/bcast-relay.xml.in b/interface-definitions/bcast-relay.xml.in index b691f79fa..1b354d885 100644 --- a/interface-definitions/bcast-relay.xml.in +++ b/interface-definitions/bcast-relay.xml.in @@ -9,12 +9,7 @@ <priority>990</priority> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Globally disable broadcast relay service</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <tagNode name="id"> <properties> <help>Unique ID for each UDP port to forward</help> @@ -27,12 +22,7 @@ </constraint> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Disable broadcast relay service instance</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="address"> <properties> <help>Set source IP of forwarded packets, otherwise original senders address is used</help> diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in index 2c1609d94..28b61e92d 100644 --- a/interface-definitions/dhcp-server.xml.in +++ b/interface-definitions/dhcp-server.xml.in @@ -9,12 +9,7 @@ <priority>911</priority> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Disable DHCP server</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="dynamic-dns-update"> <properties> <help>Dynamically update Domain Name System (RFC4702)</help> @@ -63,12 +58,7 @@ <help>Shared-network-name description</help> </properties> </leafNode> - <leafNode name="disable"> - <properties> - <help>Option to disable DHCP configuration for shared-network</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="shared-network-parameters"> <properties> <help>Additional shared-network parameters for DHCP server. @@ -330,12 +320,7 @@ <constraintErrorMessage>Invalid static mapping name. May only contain letters, numbers and .-_</constraintErrorMessage> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Option to disable static mapping</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="ip-address"> <properties> <help>Fixed IP address of static mapping</help> @@ -350,11 +335,14 @@ </leafNode> <leafNode name="mac-address"> <properties> - <help>MAC address of static mapping [REQUIRED]</help> + <help>Media Access Control (MAC) address</help> <valueHelp> - <format>h:h:h:h:h:h</format> - <description>MAC address used in static mapping [REQUIRED]</description> + <format>macaddr</format> + <description>Hardware (MAC) address</description> </valueHelp> + <constraint> + <validator name="mac-address"/> + </constraint> </properties> </leafNode> <leafNode name="static-mapping-parameters"> diff --git a/interface-definitions/dhcpv6-server.xml.in b/interface-definitions/dhcpv6-server.xml.in index 37bc7e03e..a3cca06da 100644 --- a/interface-definitions/dhcpv6-server.xml.in +++ b/interface-definitions/dhcpv6-server.xml.in @@ -9,12 +9,27 @@ <priority>900</priority> </properties> <children> - <leafNode name="disable"> + #include <include/generic-disable-node.xml.i> + <node name="global-parameters"> <properties> - <help>Option to disable DHCPv6 server</help> - <valueless/> + <help>Additional global parameters for DHCPv6 server</help> </properties> - </leafNode> + <children> + <leafNode name="name-server"> + <properties> + <help>IPv6 address of a Recursive DNS Server</help> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address of DNS name server</description> + </valueHelp> + <constraint> + <validator name="ipv6-address"/> + </constraint> + <multi/> + </properties> + </leafNode> + </children> + </node> <leafNode name="preference"> <properties> <help>Preference of this DHCPv6 server compared with others</help> @@ -37,12 +52,7 @@ <constraintErrorMessage>Invalid DHCPv6 shared network name. May only contain letters, numbers and .-_</constraintErrorMessage> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Option to disable DHCPv6 configuration for shared-network</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <node name="common-options"> <properties> <help>Common options to distribute to all clients, including stateless clients</help> @@ -324,12 +334,7 @@ <constraintErrorMessage>Invalid static mapping name. May only contain letters, numbers and .-_</constraintErrorMessage> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Option to disable static mapping</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="identifier"> <properties> <help>Client identifier (DUID) for this static mapping</help> diff --git a/interface-definitions/dns-domain-name.xml.in b/interface-definitions/dns-domain-name.xml.in index 3b5843b53..ff632e1d1 100644 --- a/interface-definitions/dns-domain-name.xml.in +++ b/interface-definitions/dns-domain-name.xml.in @@ -44,7 +44,7 @@ <properties> <help>System domain name</help> <constraint> - <regex>[A-Za-z0-9][-.A-Za-z0-9]*</regex> + <validator name="fqdn"/> </constraint> </properties> </leafNode> diff --git a/interface-definitions/firewall-options.xml.in b/interface-definitions/firewall-options.xml.in index defd44f06..8d9225a9a 100644 --- a/interface-definitions/firewall-options.xml.in +++ b/interface-definitions/firewall-options.xml.in @@ -16,12 +16,7 @@ </completionHelp> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Disable this rule</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="adjust-mss"> <properties> <help>Adjust MSS for IPv4 transit packets</help> diff --git a/interface-definitions/igmp-proxy.xml.in b/interface-definitions/igmp-proxy.xml.in index b9c52794f..d0f44eada 100644 --- a/interface-definitions/igmp-proxy.xml.in +++ b/interface-definitions/igmp-proxy.xml.in @@ -9,12 +9,7 @@ <priority>740</priority> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Option to disable IGMP proxy</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="disable-quickleave"> <properties> <help>Option to disable "quickleave"</help> diff --git a/interface-definitions/include/accel-mtu-128-16384.xml.i b/interface-definitions/include/accel-mtu-128-16384.xml.i deleted file mode 100644 index 7ee483056..000000000 --- a/interface-definitions/include/accel-mtu-128-16384.xml.i +++ /dev/null @@ -1,9 +0,0 @@ - <leafNode name="mtu"> - <properties> - <help>Maximum Transmission Unit (MTU) - default 1492</help> - <constraint> - <validator name="numeric" argument="--range 128-16384"/> - </constraint> - </properties> - <defaultValue>1492</defaultValue> - </leafNode> diff --git a/interface-definitions/include/accel-auth-local-users.xml.i b/interface-definitions/include/accel-ppp/auth-local-users.xml.i index 0d66b8135..308d6510d 100644 --- a/interface-definitions/include/accel-auth-local-users.xml.i +++ b/interface-definitions/include/accel-ppp/auth-local-users.xml.i @@ -1,4 +1,4 @@ -<!-- included start from accel-auth-local-users.xml.i --> +<!-- include start from accel-ppp/auth-local-users.xml.i --> <node name="local-users"> <properties> <help>Local user authentication for PPPoE server</help> @@ -9,12 +9,7 @@ <help>User name for authentication</help> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Option to disable a PPPoE Server user</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="password"> <properties> <help>Password for authentication</help> @@ -53,4 +48,4 @@ </tagNode> </children> </node> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/accel-auth-mode.xml.i b/interface-definitions/include/accel-ppp/auth-mode.xml.i index 85c3c5e82..c1a87cfe3 100644 --- a/interface-definitions/include/accel-auth-mode.xml.i +++ b/interface-definitions/include/accel-ppp/auth-mode.xml.i @@ -1,4 +1,4 @@ -<!-- included start from accel-auth-mode.xml.i --> +<!-- include start from accel-ppp/auth-mode.xml.i --> <leafNode name="mode"> <properties> <help>Authentication mode used by this server</help> @@ -19,4 +19,4 @@ </properties> <defaultValue>local</defaultValue> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/accel-auth-protocols.xml.i b/interface-definitions/include/accel-ppp/auth-protocols.xml.i index a6899a4d8..d43266152 100644 --- a/interface-definitions/include/accel-auth-protocols.xml.i +++ b/interface-definitions/include/accel-ppp/auth-protocols.xml.i @@ -1,4 +1,4 @@ -<!-- included start from accel-auth-protocols.xml.i --> +<!-- include start from accel-ppp/auth-protocols.xml.i --> <leafNode name="protocols"> <properties> <help>Authentication protocol for remote access peer SSTP VPN</help> @@ -28,4 +28,4 @@ </properties> <defaultValue>pap chap mschap mschap-v2</defaultValue> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/accel-client-ip-pool-start-stop.xml.i b/interface-definitions/include/accel-ppp/client-ip-pool-start-stop.xml.i index b578f2b2c..5f4132d13 100644 --- a/interface-definitions/include/accel-client-ip-pool-start-stop.xml.i +++ b/interface-definitions/include/accel-ppp/client-ip-pool-start-stop.xml.i @@ -1,4 +1,4 @@ -<!-- included start from accel-client-ip-pool-start-stop.xml.i --> +<!-- include start from accel-ppp/client-ip-pool-start-stop.xml.i --> <leafNode name="start"> <properties> <help>First IP address in the pool</help> @@ -15,4 +15,4 @@ </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/accel-client-ip-pool-subnet.xml.i b/interface-definitions/include/accel-ppp/client-ip-pool-subnet.xml.i index 8e9ca0e92..2dc71d3f9 100644 --- a/interface-definitions/include/accel-client-ip-pool-subnet.xml.i +++ b/interface-definitions/include/accel-ppp/client-ip-pool-subnet.xml.i @@ -1,4 +1,4 @@ -<!-- included start from accel-client-ip-pool-subnet.xml.i --> +<!-- include start from accel-ppp/client-ip-pool-subnet.xml.i --> <leafNode name="subnet"> <properties> <help>Client IP subnet (CIDR notation)</help> @@ -13,4 +13,4 @@ <multi /> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/accel-client-ipv6-pool.xml.i b/interface-definitions/include/accel-ppp/client-ipv6-pool.xml.i index d15ff35d4..bd3dadf8d 100644 --- a/interface-definitions/include/accel-client-ipv6-pool.xml.i +++ b/interface-definitions/include/accel-ppp/client-ipv6-pool.xml.i @@ -1,4 +1,4 @@ -<!-- included start from accel-client-ipv6-pool.xml.i --> +<!-- include start from accel-ppp/client-ipv6-pool.xml.i --> <node name="client-ipv6-pool"> <properties> <help>Pool of client IPv6 addresses</help> @@ -58,4 +58,4 @@ </tagNode> </children> </node> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/accel-gateway-address.xml.i b/interface-definitions/include/accel-ppp/gateway-address.xml.i index c45c8b532..59f8b5023 100644 --- a/interface-definitions/include/accel-gateway-address.xml.i +++ b/interface-definitions/include/accel-ppp/gateway-address.xml.i @@ -1,4 +1,4 @@ -<!-- included start from accel-gateway-address.xml.i --> +<!-- include start from accel-ppp/gateway-address.xml.i --> <leafNode name="gateway-address"> <properties> <help>Gateway IP address</help> @@ -12,4 +12,4 @@ </valueHelp> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/accel-lcp-echo-interval-failure.xml.i b/interface-definitions/include/accel-ppp/lcp-echo-interval-failure.xml.i index cccf4b4f2..dd7ae1276 100644 --- a/interface-definitions/include/accel-lcp-echo-interval-failure.xml.i +++ b/interface-definitions/include/accel-ppp/lcp-echo-interval-failure.xml.i @@ -1,4 +1,4 @@ -<!-- included start from accel-lcp-echo-interval-failure.xml.i --> +<!-- include start from accel-ppp/lcp-echo-interval-failure.xml.i --> <leafNode name="lcp-echo-interval"> <properties> <help>LCP echo-requests/sec</help> @@ -17,4 +17,4 @@ </properties> <defaultValue>3</defaultValue> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/accel-lcp-echo-timeout.xml.i b/interface-definitions/include/accel-ppp/lcp-echo-timeout.xml.i index 888fa9d41..a630bec32 100644 --- a/interface-definitions/include/accel-lcp-echo-timeout.xml.i +++ b/interface-definitions/include/accel-ppp/lcp-echo-timeout.xml.i @@ -1,4 +1,4 @@ -<!-- included start from accel-lcp-echo-timeout.xml.i --> +<!-- include start from accel-ppp/lcp-echo-timeout.xml.i --> <leafNode name="lcp-echo-timeout"> <properties> <help>Timeout in seconds to wait for any peer activity. If this option specified it turns on adaptive lcp echo functionality and "lcp-echo-failure" is not used.</help> @@ -8,4 +8,4 @@ </properties> <defaultValue>0</defaultValue> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/accel-ppp/mtu-128-16384.xml.i b/interface-definitions/include/accel-ppp/mtu-128-16384.xml.i new file mode 100644 index 000000000..b4008a63b --- /dev/null +++ b/interface-definitions/include/accel-ppp/mtu-128-16384.xml.i @@ -0,0 +1,11 @@ +<!-- include start from accel-ppp/mtu-128-16384.xml.i --> +<leafNode name="mtu"> + <properties> + <help>Maximum Transmission Unit (MTU) - default 1492</help> + <constraint> + <validator name="numeric" argument="--range 128-16384"/> + </constraint> + </properties> + <defaultValue>1492</defaultValue> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/accel-name-server.xml.i b/interface-definitions/include/accel-ppp/name-server.xml.i index e46c75b52..e744b384f 100644 --- a/interface-definitions/include/accel-name-server.xml.i +++ b/interface-definitions/include/accel-ppp/name-server.xml.i @@ -1,4 +1,4 @@ -<!-- included start from accel-name-server.xml.i --> +<!-- include start from accel-ppp/name-server.xml.i --> <leafNode name="name-server"> <properties> <help>Domain Name Server (DNS) propagated to client</help> @@ -17,4 +17,4 @@ <multi/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/accel-ppp-mppe.xml.i b/interface-definitions/include/accel-ppp/ppp-mppe.xml.i index b7f9cfd92..e8370180b 100644 --- a/interface-definitions/include/accel-ppp-mppe.xml.i +++ b/interface-definitions/include/accel-ppp/ppp-mppe.xml.i @@ -1,4 +1,4 @@ -<!-- included start from accel-ppp-mppe.xml.i --> +<!-- include start from accel-ppp/ppp-mppe.xml.i --> <leafNode name="mppe"> <properties> <help>Specifies mppe negotiation preferences</help> @@ -23,4 +23,4 @@ </properties> <defaultValue>prefer</defaultValue> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/accel-radius-additions-disable-accounting.xlm.in b/interface-definitions/include/accel-ppp/radius-additions-disable-accounting.xml.i index 026f67453..c723c3174 100644 --- a/interface-definitions/include/accel-radius-additions-disable-accounting.xlm.in +++ b/interface-definitions/include/accel-ppp/radius-additions-disable-accounting.xml.i @@ -1,7 +1,8 @@ +<!-- include start from accel-ppp/radius-additions-disable-accounting.xml.i --> <leafNode name="disable-accounting"> <properties> <help>Disable accounting</help> <valueless/> </properties> </leafNode> - +<!-- include end --> diff --git a/interface-definitions/include/accel-radius-additions-rate-limit.xml.i b/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i index 23a4a51cf..be49fce5a 100644 --- a/interface-definitions/include/accel-radius-additions-rate-limit.xml.i +++ b/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i @@ -1,4 +1,4 @@ -<!-- included start from accel-radius-additions-rate-limit.xml.i --> +<!-- include start from accel-ppp/radius-additions-rate-limit.xml.i --> <node name="rate-limit"> <properties> <help>Upload/Download speed limits</help> @@ -23,4 +23,4 @@ </leafNode> </children> </node> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/accel-radius-additions.xml.i b/interface-definitions/include/accel-ppp/radius-additions.xml.i index 3ec0d587e..e65088c43 100644 --- a/interface-definitions/include/accel-radius-additions.xml.i +++ b/interface-definitions/include/accel-ppp/radius-additions.xml.i @@ -1,4 +1,4 @@ -<!-- included start from accel-radius-additions.xml.i --> +<!-- include start from accel-ppp/radius-additions.xml.i --> <node name="radius"> <children> <leafNode name="acct-interim-jitter"> @@ -29,7 +29,7 @@ </properties> <defaultValue>1813</defaultValue> </leafNode> - #include <include/accel-radius-additions-disable-accounting.xlm.in> + #include <include/accel-ppp/radius-additions-disable-accounting.xml.i> <leafNode name="fail-time"> <properties> <help>Mark server unavailable for <n> seconds on failure</help> @@ -150,4 +150,4 @@ </node> </children> </node> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/accel-wins-server.xml.i b/interface-definitions/include/accel-ppp/wins-server.xml.i index 6de032981..f7f483f59 100644 --- a/interface-definitions/include/accel-wins-server.xml.i +++ b/interface-definitions/include/accel-ppp/wins-server.xml.i @@ -1,4 +1,4 @@ -<!-- included start from accel-wins-server.xml.i --> +<!-- include start from accel-ppp/wins-server.xml.i --> <leafNode name="wins-server"> <properties> <help>Windows Internet Name Service (WINS) servers propagated to client</help> @@ -12,4 +12,4 @@ <multi/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/bfd-common.xml.i b/interface-definitions/include/bfd-common.xml.i new file mode 100644 index 000000000..b47b47612 --- /dev/null +++ b/interface-definitions/include/bfd-common.xml.i @@ -0,0 +1,72 @@ +<!-- include start from bfd-common.xml.i --> +<leafNode name="echo-mode"> + <properties> + <help>Enables the echo transmission mode</help> + <valueless/> + </properties> +</leafNode> +<node name="interval"> + <properties> + <help>Configure timer intervals</help> + </properties> + <children> + <leafNode name="receive"> + <properties> + <help>Minimum interval of receiving control packets</help> + <valueHelp> + <format>10-60000</format> + <description>Interval in milliseconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 10-60000"/> + </constraint> + </properties> + <defaultValue>300</defaultValue> + </leafNode> + <leafNode name="transmit"> + <properties> + <help>Minimum interval of transmitting control packets</help> + <valueHelp> + <format>10-60000</format> + <description>Interval in milliseconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 10-60000"/> + </constraint> + </properties> + <defaultValue>300</defaultValue> + </leafNode> + <leafNode name="multiplier"> + <properties> + <help>Multiplier to determine packet loss</help> + <valueHelp> + <format>2-255</format> + <description>Remote transmission interval will be multiplied by this value</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 2-255"/> + </constraint> + </properties> + <defaultValue>3</defaultValue> + </leafNode> + <leafNode name="echo-interval"> + <properties> + <help>Echo receive transmission interval</help> + <valueHelp> + <format>10-60000</format> + <description>The minimal echo receive transmission interval that this system is capable of handling</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 10-60000"/> + </constraint> + </properties> + </leafNode> + </children> +</node> +<leafNode name="shutdown"> + <properties> + <help>Disable this peer</help> + <valueless/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bfd.xml.i b/interface-definitions/include/bfd.xml.i new file mode 100644 index 000000000..2bc3664e1 --- /dev/null +++ b/interface-definitions/include/bfd.xml.i @@ -0,0 +1,8 @@ +<!-- include start from bfd.xml.i --> +<leafNode name="bfd"> + <properties> + <help>Enable Bidirectional Forwarding Detection (BFD)</help> + <valueless/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp-afi-redistribute-metric-route-map.xml.i b/interface-definitions/include/bgp-afi-redistribute-metric-route-map.xml.i deleted file mode 100644 index afd56eff3..000000000 --- a/interface-definitions/include/bgp-afi-redistribute-metric-route-map.xml.i +++ /dev/null @@ -1,19 +0,0 @@ -<!-- included start from bgp-afi-redistribute-metric-route-map.xml.i --> -<leafNode name="metric"> - <properties> - <help>Metric for redistributed routes</help> - <valueHelp> - <format>u32:1-4294967295</format> - <description>Metric for redistributed routes</description> - </valueHelp> - </properties> -</leafNode> -<leafNode name="route-map"> - <properties> - <help>Route map to filter redistributed routes</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> -</leafNode> -<!-- included end --> diff --git a/interface-definitions/include/bgp-neighbor-afi-ipv4-unicast.xml.i b/interface-definitions/include/bgp-neighbor-afi-ipv4-unicast.xml.i deleted file mode 100644 index c5a83f045..000000000 --- a/interface-definitions/include/bgp-neighbor-afi-ipv4-unicast.xml.i +++ /dev/null @@ -1,286 +0,0 @@ -<!-- included start from bgp-neighbor-afi-ipv4-unicast.xml.i --> -<node name="ipv4-unicast"> - <properties> - <help>IPv4 BGP neighbor parameters</help> - </properties> - <children> - <node name="allowas-in"> - <properties> - <help>Accept a IPv4-route that contains the local-AS in the as-path</help> - </properties> - <children> - <leafNode name="number"> - <properties> - <help>Number of occurrences of AS number</help> - <valueHelp> - <format>u32:1-10</format> - <description>Number of times AS is allowed in path</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-10"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <leafNode name="as-override"> - <properties> - <help>AS for routes sent to this neighbor to be the local AS</help> - <valueless/> - </properties> - </leafNode> - <node name="attribute-unchanged"> - <properties> - <help>BGP attributes are sent unchanged (IPv4)</help> - </properties> - <children> - <leafNode name="as-path"> - <properties> - <help>Send AS path unchanged (IPv4)</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="med"> - <properties> - <help>Send multi-exit discriminator unchanged (IPv4)</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="next-hop"> - <properties> - <help>Send nexthop unchanged (IPv4)</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <node name="capability"> - <properties> - <help>Advertise capabilities to this neighbor (IPv4)</help> - </properties> - <children> - <node name="orf"> - <properties> - <help>Advertise ORF capability to this neighbor</help> - </properties> - <children> - <node name="prefix-list"> - <properties> - <help>Advertise prefix-list ORF capability to this neighbor</help> - </properties> - <children> - <leafNode name="receive"> - <properties> - <help>Capability to receive the ORF</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="send"> - <properties> - <help>Capability to send the ORF</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - </children> - </node> - </children> - </node> - <node name="default-originate"> - <properties> - <help>Send default IPv4-route to this neighbor</help> - </properties> - <children> - <leafNode name="route-map"> - <properties> - <help>IPv4-Route-map to specify criteria of the default</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - <node name="distribute-list"> - <properties> - <help>Access-list to filter IPv4-route updates to/from this neighbor</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>Access-list to filter outgoing IPv4-route updates to this neighbor</help> - <completionHelp> - <path>policy access-list</path> - </completionHelp> - <valueHelp> - <format>u32:1-65535</format> - <description>Access-list to filter outgoing IPv4-route updates to this neighbor</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>Access-list to filter incoming IPv4-route updates from this neighbor</help> - <completionHelp> - <path>policy access-list</path> - </completionHelp> - <valueHelp> - <format>u32:1-65535</format> - <description>Access-list to filter incoming IPv4-route updates from this neighbor</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <node name="filter-list"> - <properties> - <help>As-path-list to filter IPv4-route updates to/from this neighbor</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>As-path-list to filter outgoing IPv4-route updates to this neighbor</help> - <completionHelp> - <path>policy as-path-list</path> - </completionHelp> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>As-path-list to filter incoming IPv4-route updates from this neighbor</help> - <completionHelp> - <path>policy as-path-list</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - <leafNode name="maximum-prefix"> - <properties> - <help>Maximum number of IPv4-prefixes to accept from this neighbor</help> - <valueHelp> - <format>u32:1-4294967295</format> - <description>Prefix limit</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967295"/> - </constraint> - </properties> - </leafNode> - <node name="nexthop-self"> - <properties> - <help>Nexthop for IPv4-routes sent to this neighbor to be the local router</help> - </properties> - <children> - <leafNode name="force"> - <properties> - <help>Set the next hop to self for reflected routes</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <node name="prefix-list"> - <properties> - <help>IPv4-Prefix-list to filter route updates to/from this neighbor</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>IPv4-Prefix-list to filter outgoing route updates to this neighbor</help> - <completionHelp> - <path>policy prefix-list</path> - </completionHelp> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>IPv4-Prefix-list to filter incoming route updates from this neighbor</help> - <completionHelp> - <path>policy prefix-list</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - <leafNode name="remove-private-as"> - <properties> - <help>Remove private AS numbers from AS path in outbound IPv4-route updates</help> - <valueless/> - </properties> - </leafNode> - <node name="route-map"> - <properties> - <help>Route-map to filter IPv4-route updates to/from this neighbor</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>IPv4-Route-map to filter outgoing route updates to this neighbor</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>IPv4-Route-map to filter incoming route updates from this neighbor</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - <leafNode name="route-reflector-client"> - <properties> - <help>Neighbor as a IPv4-route reflector client</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="route-server-client"> - <properties> - <help>Neighbor is IPv4-route server client</help> - <valueless/> - </properties> - </leafNode> - <node name="soft-reconfiguration"> - <properties> - <help>Soft reconfiguration for neighbor (IPv4)</help> - </properties> - <children> - <leafNode name="inbound"> - <properties> - <help>Inbound soft reconfiguration for this neighbor [REQUIRED]</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <leafNode name="unsuppress-map"> - <properties> - <help>Route-map to selectively unsuppress suppressed IPv4-routes</help> - </properties> - </leafNode> - <leafNode name="weight"> - <properties> - <help>Default weight for routes from this neighbor</help> - <valueHelp> - <format>u32:1-65535</format> - <description>Weight for routes from this neighbor</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - </children> -</node> -<!-- included end --> diff --git a/interface-definitions/include/bgp-neighbor-afi-ipv6-unicast.xml.i b/interface-definitions/include/bgp-neighbor-afi-ipv6-unicast.xml.i deleted file mode 100644 index 61cdc6a1c..000000000 --- a/interface-definitions/include/bgp-neighbor-afi-ipv6-unicast.xml.i +++ /dev/null @@ -1,331 +0,0 @@ -<!-- included start from bgp-neighbor-afi-ipv6-unicast.xml.i --> -<node name="ipv6-unicast"> - <properties> - <help>IPv6 BGP neighbor parameters</help> - </properties> - <children> - <node name="allowas-in"> - <properties> - <help>Accept a IPv6-route that contains the local-AS in the as-path</help> - </properties> - <children> - <leafNode name="number"> - <properties> - <help>Number of occurrences of AS number</help> - <valueHelp> - <format>u32:1-10</format> - <description>Number of times AS is allowed in path</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-10"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <leafNode name="as-override"> - <properties> - <help>AS for routes sent to this neighbor to be the local AS</help> - <valueless/> - </properties> - </leafNode> - <node name="attribute-unchanged"> - <properties> - <help>BGP attributes are sent unchanged</help> - </properties> - <children> - <leafNode name="as-path"> - <properties> - <help>Send AS path unchanged</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="med"> - <properties> - <help>Send multi-exit discriminator unchanged</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="next-hop"> - <properties> - <help>Send nexthop unchanged</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <node name="capability"> - <properties> - <help>Advertise capabilities to this neighbor (IPv6)</help> - </properties> - <children> - <!-- Capability dynamic in the afi ipv6 does nothing T3037 --> - <leafNode name="dynamic"> - <properties> - <help>Advertise dynamic capability to this neighbor</help> - <valueless/> - </properties> - </leafNode> - <node name="orf"> - <properties> - <help>Advertise ORF capability to this neighbor</help> - </properties> - <children> - <node name="prefix-list"> - <properties> - <help>Advertise prefix-list ORF capability to this neighbor</help> - </properties> - <children> - <leafNode name="receive"> - <properties> - <help>Capability to receive the ORF</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="send"> - <properties> - <help>Capability to send the ORF</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - </children> - </node> - </children> - </node> - <node name="default-originate"> - <properties> - <help>Send default IPv6-route to this neighbor</help> - </properties> - <children> - <leafNode name="route-map"> - <properties> - <help>Route-map to specify criteria of the default</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - <node name="disable-send-community"> - <properties> - <help>Disable sending community attributes to this neighbor</help> - </properties> - <children> - <leafNode name="extended"> - <properties> - <help>Disable sending extended community attributes to this neighbor</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="standard"> - <properties> - <help>Disable sending standard community attributes to this neighbor</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <node name="distribute-list"> - <properties> - <help>Access-list to filter route updates to/from this neighbor</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>Access-list to filter outgoing route updates to this neighbor</help> - <completionHelp> - <path>policy access-list6</path> - </completionHelp> - <valueHelp> - <format>u32:1-65535</format> - <description>Access-list to filter outgoing route updates to this neighbor</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>Access-list to filter incoming route updates from this neighbor</help> - <completionHelp> - <path>policy access-list6</path> - </completionHelp> - <valueHelp> - <format>u32:1-65535</format> - <description>Access-list to filter incoming route updates from this neighbor</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <node name="filter-list"> - <properties> - <help>As-path-list to filter route updates to/from this neighbor</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>As-path-list to filter outgoing route updates to this neighbor</help> - <completionHelp> - <path>policy as-path-list</path> - </completionHelp> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>As-path-list to filter incoming route updates from this neighbor</help> - <completionHelp> - <path>policy as-path-list</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - <leafNode name="maximum-prefix"> - <properties> - <help>Maximum number of prefixes to accept from this neighbor</help> - <valueHelp> - <format>u32:1-4294967295</format> - <description>Prefix limit</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967295"/> - </constraint> - </properties> - </leafNode> - <node name="nexthop-local"> - <properties> - <help>Nexthop attributes</help> - </properties> - <children> - <leafNode name="unchanged"> - <properties> - <help>Leave link-local nexthop unchanged for this peer</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <node name="nexthop-self"> - <properties> - <help>Nexthop for IPv6-routes sent to this neighbor to be the local router</help> - </properties> - <children> - <leafNode name="force"> - <properties> - <help>Set the next hop to self for reflected routes</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <leafNode name="peer-group"> - <properties> - <help>IPv6 peer group for this peer</help> - </properties> - </leafNode> - <node name="prefix-list"> - <properties> - <help>Prefix-list to filter route updates to/from this neighbor</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>Prefix-list to filter outgoing route updates to this neighbor</help> - <completionHelp> - <path>policy prefix-list6</path> - </completionHelp> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>Prefix-list to filter incoming route updates from this neighbor</help> - <completionHelp> - <path>policy prefix-list6</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - <leafNode name="remove-private-as"> - <properties> - <help>Remove private AS numbers from AS path in outbound route updates</help> - <valueless/> - </properties> - </leafNode> - <node name="route-map"> - <properties> - <help>Route-map to filter route updates to/from this neighbor</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>Route-map to filter outgoing route updates to this neighbor</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>Route-map to filter incoming route updates from this neighbor</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - <leafNode name="route-reflector-client"> - <properties> - <help>Neighbor as a IPv6-route reflector client</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="route-server-client"> - <properties> - <help>Neighbor is IPv6-route server client</help> - <valueless/> - </properties> - </leafNode> - <node name="soft-reconfiguration"> - <properties> - <help>Soft reconfiguration for neighbor (IPv6)</help> - </properties> - <children> - <leafNode name="inbound"> - <properties> - <help>Inbound soft reconfiguration for this neighbor [REQUIRED]</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <leafNode name="unsuppress-map"> - <properties> - <help>Route-map to selectively unsuppress suppressed IPv6-routes</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="weight"> - <properties> - <help>Default weight for routes from this neighbor</help> - <valueHelp> - <format>u32:1-65535</format> - <description>Weight for routes from this neighbor</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - </children> -</node> -<!-- included end --> diff --git a/interface-definitions/include/bgp-peer-group-afi-ipv4-unicast.xml.i b/interface-definitions/include/bgp-peer-group-afi-ipv4-unicast.xml.i deleted file mode 100644 index e34d9f774..000000000 --- a/interface-definitions/include/bgp-peer-group-afi-ipv4-unicast.xml.i +++ /dev/null @@ -1,303 +0,0 @@ -<!-- included start from bgp-peer-group-afi-ipv4-unicast.xml.i --> -<node name="ipv4-unicast"> - <properties> - <help>IPv4 BGP peer group parameters</help> - </properties> - <children> - <node name="allowas-in"> - <properties> - <help>Accept a route that contains the local-AS in the as-path</help> - </properties> - <children> - <leafNode name="number"> - <properties> - <help>Number of occurrences of AS number</help> - <valueHelp> - <format>u32:1-10</format> - <description>Number of times AS is allowed in path</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-10"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <node name="attribute-unchanged"> - <properties> - <help>BGP attributes are sent unchanged</help> - </properties> - <children> - <leafNode name="as-path"> - <properties> - <help>Send AS path unchanged</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="med"> - <properties> - <help>Send multi-exit discriminator unchanged</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="next-hop"> - <properties> - <help>Send nexthop unchanged</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <node name="capability"> - <properties> - <help>Advertise capabilities to this peer-group</help> - </properties> - <children> - <leafNode name="dynamic"> - <properties> - <help>Advertise dynamic capability to this peer-group</help> - <valueless/> - </properties> - </leafNode> - <node name="orf"> - <properties> - <help>Advertise ORF capability to this peer-group</help> - </properties> - <children> - <node name="prefix-list"> - <properties> - <help>Advertise prefix-list ORF capability to this peer-group</help> - </properties> - <children> - <leafNode name="receive"> - <properties> - <help>Capability to receive the ORF</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="send"> - <properties> - <help>Capability to send the ORF</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - </children> - </node> - </children> - </node> - <node name="default-originate"> - <properties> - <help>Send default route to this peer-group</help> - </properties> - <children> - <leafNode name="route-map"> - <properties> - <help>Route-map to specify criteria of the default</help> - </properties> - </leafNode> - </children> - </node> - <node name="disable-send-community"> - <properties> - <help>Disable sending community attributes to this peer-group</help> - </properties> - <children> - <leafNode name="extended"> - <properties> - <help>Disable sending extended community attributes to this peer-group</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="standard"> - <properties> - <help>Disable sending standard community attributes to this peer-group</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <node name="distribute-list"> - <properties> - <help>Access-list to filter route updates to/from this peer-group</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>Access-list to filter outgoing route updates to this peer-group</help> - <completionHelp> - <path>policy access-list</path> - </completionHelp> - <valueHelp> - <format>u32:1-65535</format> - <description>Access-list to filter outgoing route updates to this peer-group</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>Access-list to filter incoming route updates from this peer-group</help> - <completionHelp> - <path>policy access-list</path> - </completionHelp> - <valueHelp> - <format>u32:1-65535</format> - <description>Access-list to filter incoming route updates from this peer-group</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <node name="filter-list"> - <properties> - <help>As-path-list to filter route updates to/from this peer-group</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>As-path-list to filter outgoing route updates to this peer-group</help> - <completionHelp> - <path>policy as-path-list</path> - </completionHelp> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>As-path-list to filter incoming route updates from this peer-group</help> - <completionHelp> - <path>policy as-path-list</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - <leafNode name="maximum-prefix"> - <properties> - <help>Maximum number of prefixes to accept from this peer-group</help> - <valueHelp> - <format>u32:1-4294967295</format> - <description>Prefix limit</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967295"/> - </constraint> - </properties> - </leafNode> - <node name="nexthop-self"> - <properties> - <help>Nexthop for routes sent to this peer-group to be the local router</help> - </properties> - <children> - <leafNode name="force"> - <properties> - <help>Set the next hop to self for reflected routes</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <node name="prefix-list"> - <properties> - <help>Prefix-list to filter route updates to/from this peer-group</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>Prefix-list to filter outgoing route updates to this peer-group</help> - <completionHelp> - <path>policy prefix-list</path> - </completionHelp> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>Prefix-list to filter incoming route updates from this peer-group</help> - <completionHelp> - <path>policy prefix-list</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - <leafNode name="remove-private-as"> - <properties> - <help>Remove private AS numbers from AS path in outbound route updates</help> - <valueless/> - </properties> - </leafNode> - <node name="route-map"> - <properties> - <help>Route-map to filter route updates to/from this peer-group</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>Route-map to filter outgoing route updates to this peer-group</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>Route-map to filter incoming route updates from this peer-group</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - <leafNode name="route-reflector-client"> - <properties> - <help>Peer-group as a route reflector client</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="route-server-client"> - <properties> - <help>Peer-group as route server client</help> - <valueless/> - </properties> - </leafNode> - <node name="soft-reconfiguration"> - <properties> - <help>Soft reconfiguration for peer-group</help> - </properties> - <children> - <leafNode name="inbound"> - <properties> - <help>Inbound soft reconfiguration for this peer-group [REQUIRED]</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <leafNode name="unsuppress-map"> - <properties> - <help>Route-map to selectively unsuppress suppressed routes</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="weight"> - <properties> - <help>Default weight for routes from this peer-group</help> - <valueHelp> - <format>u32:1-65535</format> - <description>Weight for routes from this peer-group</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - </children> -</node> -<!-- included end --> diff --git a/interface-definitions/include/bgp-peer-group-afi-ipv6-unicast.xml.i b/interface-definitions/include/bgp-peer-group-afi-ipv6-unicast.xml.i deleted file mode 100644 index 400193b7b..000000000 --- a/interface-definitions/include/bgp-peer-group-afi-ipv6-unicast.xml.i +++ /dev/null @@ -1,319 +0,0 @@ -<!-- included start from bgp-peer-group-afi-ipv6-unicast.xml.i --> -<node name="ipv6-unicast"> - <properties> - <help>IPv6 BGP neighbor parameters</help> - </properties> - <children> - <node name="allowas-in"> - <properties> - <help>Accept a IPv6-route that contains the local-AS in the as-path</help> - </properties> - <children> - <leafNode name="number"> - <properties> - <help>Number of occurrences of AS number</help> - <valueHelp> - <format>u32:1-10</format> - <description>Number of times AS is allowed in path</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-10"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <node name="attribute-unchanged"> - <properties> - <help>BGP attributes are sent unchanged</help> - </properties> - <children> - <leafNode name="as-path"> - <properties> - <help>Send AS path unchanged</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="med"> - <properties> - <help>Send multi-exit discriminator unchanged</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="next-hop"> - <properties> - <help>Send nexthop unchanged</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <node name="capability"> - <properties> - <help>Advertise capabilities to this peer-group</help> - </properties> - <children> - <leafNode name="dynamic"> - <properties> - <help>Advertise dynamic capability to this peer-group</help> - <valueless/> - </properties> - </leafNode> - <node name="orf"> - <properties> - <help>Advertise ORF capability to this peer-group</help> - </properties> - <children> - <node name="prefix-list"> - <properties> - <help>Advertise prefix-list ORF capability to this peer-group</help> - </properties> - <children> - <leafNode name="receive"> - <properties> - <help>Capability to receive the ORF</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="send"> - <properties> - <help>Capability to send the ORF</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - </children> - </node> - </children> - </node> - <node name="default-originate"> - <properties> - <help>Send default route to this peer-group</help> - </properties> - <children> - <leafNode name="route-map"> - <properties> - <help>Route-map to specify criteria of the default</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - <node name="disable-send-community"> - <properties> - <help>Disable sending community attributes to this peer-group</help> - </properties> - <children> - <leafNode name="extended"> - <properties> - <help>Disable sending extended community attributes to this peer-group</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="standard"> - <properties> - <help>Disable sending standard community attributes to this peer-group</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <node name="distribute-list"> - <properties> - <help>Access-list to filter route updates to/from this peer-group</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>Access-list to filter outgoing route updates to this peer-group</help> - <completionHelp> - <path>policy access-list6</path> - </completionHelp> - <valueHelp> - <format>u32:1-65535</format> - <description>Access-list to filter outgoing route updates to this peer-group</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>Access-list to filter incoming route updates from this peer-group</help> - <completionHelp> - <path>policy access-list6</path> - </completionHelp> - <valueHelp> - <format>u32:1-65535</format> - <description>Access-list to filter incoming route updates from this peer-group</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <node name="filter-list"> - <properties> - <help>As-path-list to filter route updates to/from this peer-group</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>As-path-list to filter outgoing route updates to this peer-group</help> - <completionHelp> - <path>policy as-path-list</path> - </completionHelp> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>As-path-list to filter incoming route updates from this peer-group</help> - <completionHelp> - <path>policy as-path-list</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - <leafNode name="maximum-prefix"> - <properties> - <help>Maximum number of prefixes to accept from this peer-group</help> - <valueHelp> - <format>u32:1-4294967295</format> - <description>Prefix limit</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967295"/> - </constraint> - </properties> - </leafNode> - <node name="nexthop-local"> - <properties> - <help>Nexthop attributes</help> - </properties> - <children> - <leafNode name="unchanged"> - <properties> - <help>Leave link-local nexthop unchanged for this peer</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <node name="nexthop-self"> - <properties> - <help>Nexthop for routes sent to this peer-group to be the local router</help> - </properties> - <children> - <leafNode name="force"> - <properties> - <help>Set the next hop to self for reflected routes</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <node name="prefix-list"> - <properties> - <help>Prefix-list to filter route updates to/from this peer-group</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>Prefix-list to filter outgoing route updates to this peer-group</help> - <completionHelp> - <path>policy prefix-list6</path> - </completionHelp> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>Prefix-list to filter incoming route updates from this peer-group</help> - <completionHelp> - <path>policy prefix-list6</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - <leafNode name="remove-private-as"> - <properties> - <help>Remove private AS numbers from AS path in outbound route updates</help> - <valueless/> - </properties> - </leafNode> - <node name="route-map"> - <properties> - <help>Route-map to filter route updates to/from this peer-group</help> - </properties> - <children> - <leafNode name="export"> - <properties> - <help>Route-map to filter outgoing route updates to this peer-group</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> - </leafNode> - <leafNode name="import"> - <properties> - <help>Route-map to filter incoming route updates from this peer-group</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - <leafNode name="route-reflector-client"> - <properties> - <help>Peer-group as a route reflector client</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="route-server-client"> - <properties> - <help>Peer-group as route server client</help> - <valueless/> - </properties> - </leafNode> - <node name="soft-reconfiguration"> - <properties> - <help>Soft reconfiguration for peer-group</help> - </properties> - <children> - <leafNode name="inbound"> - <properties> - <help>Inbound soft reconfiguration for this peer-group [REQUIRED]</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <leafNode name="unsuppress-map"> - <properties> - <help>Route-map to selectively unsuppress suppressed routes</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="weight"> - <properties> - <help>Default weight for routes from this peer-group</help> - <valueHelp> - <format>u32:1-65535</format> - <description>Weight for routes from this peer-group</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - </children> -</node> -<!-- included end --> diff --git a/interface-definitions/include/bgp-afi-aggregate-address.xml.i b/interface-definitions/include/bgp/bgp-afi-aggregate-address.xml.i index c33d1097c..c731e970b 100644 --- a/interface-definitions/include/bgp-afi-aggregate-address.xml.i +++ b/interface-definitions/include/bgp/bgp-afi-aggregate-address.xml.i @@ -1,4 +1,4 @@ -<!-- included start from bgp-afi-aggregate-address.xml.i --> +<!-- include start from bgp-afi-aggregate-address.xml.i --> <leafNode name="as-set"> <properties> <help>Generate AS-set path information for this aggregate address</help> @@ -11,4 +11,4 @@ <valueless/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-afi-allowas-in.xml.i b/interface-definitions/include/bgp/bgp-afi-allowas-in.xml.i new file mode 100644 index 000000000..738bf0211 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-afi-allowas-in.xml.i @@ -0,0 +1,21 @@ +<!-- include start from bgp-afi-allowas-in.xml.i --> +<node name="allowas-in"> + <properties> + <help>Accept route that contains the local-as in the as-path</help> + </properties> + <children> + <leafNode name="number"> + <properties> + <help>Number of occurrences of AS number</help> + <valueHelp> + <format>u32:1-10</format> + <description>Number of times AS is allowed in path</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-10"/> + </constraint> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-afi-attribute-unchanged.xml.i b/interface-definitions/include/bgp/bgp-afi-attribute-unchanged.xml.i new file mode 100644 index 000000000..f407c3f74 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-afi-attribute-unchanged.xml.i @@ -0,0 +1,27 @@ +<!-- include start from bgp-afi-attribute-unchanged.xml.i --> +<node name="attribute-unchanged"> + <properties> + <help>BGP attributes are sent unchanged</help> + </properties> + <children> + <leafNode name="as-path"> + <properties> + <help>Send AS path unchanged</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="med"> + <properties> + <help>Send multi-exit discriminator unchanged</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="next-hop"> + <properties> + <help>Send nexthop unchanged</help> + <valueless/> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-afi-capability-orf.xml.i b/interface-definitions/include/bgp/bgp-afi-capability-orf.xml.i new file mode 100644 index 000000000..dd5c5f8b2 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-afi-capability-orf.xml.i @@ -0,0 +1,28 @@ +<!-- include start from bgp-afi-capability-orf.xml.i --> +<node name="orf"> + <properties> + <help>Advertise ORF capability to this peer</help> + </properties> + <children> + <node name="prefix-list"> + <properties> + <help>Advertise prefix-list ORF capability to this peer</help> + </properties> + <children> + <leafNode name="receive"> + <properties> + <help>Capability to receive the ORF</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="send"> + <properties> + <help>Capability to send the ORF</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-afi-common.xml.i b/interface-definitions/include/bgp/bgp-afi-common.xml.i new file mode 100644 index 000000000..7782e7ef2 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-afi-common.xml.i @@ -0,0 +1,152 @@ +<!-- include start from bgp-afi-common.xml.i --> +<leafNode name="addpath-tx-all"> + <properties> + <help>Use addpath to advertise all paths to a neighbor</help> + <valueless/> + </properties> +</leafNode> +<leafNode name="addpath-tx-per-as"> + <properties> + <help>Use addpath to advertise the bestpath per each neighboring AS</help> + <valueless/> + </properties> +</leafNode> +#include <include/bgp/bgp-afi-allowas-in.xml.i> +<leafNode name="as-override"> + <properties> + <help>AS for routes sent to this peer to be the local AS</help> + <valueless/> + </properties> +</leafNode> +#include <include/bgp/bgp-afi-attribute-unchanged.xml.i> +<node name="disable-send-community"> + <properties> + <help>Disable sending community attributes to this peer</help> + </properties> + <children> + <leafNode name="extended"> + <properties> + <help>Disable sending extended community attributes to this peer</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="standard"> + <properties> + <help>Disable sending standard community attributes to this peer</help> + <valueless/> + </properties> + </leafNode> + </children> +</node> +<node name="default-originate"> + <properties> + <help>Originate default route to this peer</help> + </properties> + <children> + #include <include/route-map.xml.i> + </children> +</node> +<node name="distribute-list"> + <properties> + <help>Access-list to filter route updates to/from this peer-group</help> + </properties> + <children> + <leafNode name="export"> + <properties> + <help>Access-list to filter outgoing route updates to this peer-group</help> + <completionHelp> + <path>policy access-list</path> + </completionHelp> + <valueHelp> + <format>u32:1-65535</format> + <description>Access-list to filter outgoing route updates to this peer-group</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + </leafNode> + <leafNode name="import"> + <properties> + <help>Access-list to filter incoming route updates from this peer-group</help> + <completionHelp> + <path>policy access-list</path> + </completionHelp> + <valueHelp> + <format>u32:1-65535</format> + <description>Access-list to filter incoming route updates from this peer-group</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + </leafNode> + </children> +</node> +<node name="filter-list"> + <properties> + <help>as-path-list to filter route updates to/from this peer</help> + </properties> + <children> + <leafNode name="export"> + <properties> + <help>As-path-list to filter outgoing route updates to this peer</help> + <completionHelp> + <path>policy as-path-list</path> + </completionHelp> + </properties> + </leafNode> + <leafNode name="import"> + <properties> + <help>As-path-list to filter incoming route updates from this peer</help> + <completionHelp> + <path>policy as-path-list</path> + </completionHelp> + </properties> + </leafNode> + </children> +</node> +<leafNode name="maximum-prefix"> + <properties> + <help>Maximum number of prefixes to accept from this peer</help> + <valueHelp> + <format>u32:1-4294967295</format> + <description>Prefix limit</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967295"/> + </constraint> + </properties> +</leafNode> +#include <include/bgp/bgp-afi-nexthop-self.xml.i> +<leafNode name="remove-private-as"> + <properties> + <help>Remove private AS numbers from AS path in outbound route updates</help> + <valueless/> + </properties> +</leafNode> +#include <include/bgp/bgp-afi-route-map.xml.i> +#include <include/bgp/bgp-afi-route-reflector-client.xml.i> +#include <include/bgp/bgp-afi-route-server-client.xml.i> +#include <include/bgp/bgp-afi-soft-reconfiguration.xml.i> +<leafNode name="unsuppress-map"> + <properties> + <help>Route-map to selectively unsuppress suppressed routes</help> + <completionHelp> + <path>policy route-map</path> + </completionHelp> + </properties> +</leafNode> +<leafNode name="weight"> + <properties> + <help>Default weight for routes from this peer</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Default weight</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-afi-ipv4-prefix-list.xml.i b/interface-definitions/include/bgp/bgp-afi-ipv4-prefix-list.xml.i new file mode 100644 index 000000000..133b5da28 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-afi-ipv4-prefix-list.xml.i @@ -0,0 +1,25 @@ +<!-- include start from bgp-afi-ipv4-prefix-list.xml.i --> +<node name="prefix-list"> + <properties> + <help>IPv4-Prefix-list to filter route updates to/from this peer</help> + </properties> + <children> + <leafNode name="export"> + <properties> + <help>IPv4-Prefix-list to filter outgoing route updates to this peer</help> + <completionHelp> + <path>policy prefix-list</path> + </completionHelp> + </properties> + </leafNode> + <leafNode name="import"> + <properties> + <help>IPv4-Prefix-list to filter incoming route updates from this peer</help> + <completionHelp> + <path>policy prefix-list</path> + </completionHelp> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-afi-ipv6-nexthop-local.xml.i b/interface-definitions/include/bgp/bgp-afi-ipv6-nexthop-local.xml.i new file mode 100644 index 000000000..c74d81b1f --- /dev/null +++ b/interface-definitions/include/bgp/bgp-afi-ipv6-nexthop-local.xml.i @@ -0,0 +1,15 @@ +<!-- include start from bgp-afi-ipv6-nexthop-local.xml.i --> + <node name="nexthop-local"> + <properties> + <help>Nexthop attributes</help> + </properties> + <children> + <leafNode name="unchanged"> + <properties> + <help>Leave link-local nexthop unchanged for this peer</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-afi-ipv6-prefix-list.xml.i b/interface-definitions/include/bgp/bgp-afi-ipv6-prefix-list.xml.i new file mode 100644 index 000000000..d597b7c99 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-afi-ipv6-prefix-list.xml.i @@ -0,0 +1,25 @@ +<!-- include start from bgp-afi-ipv6-prefix-list.xml.i --> +<node name="prefix-list"> + <properties> + <help>Prefix-list to filter route updates to/from this peer</help> + </properties> + <children> + <leafNode name="export"> + <properties> + <help>Prefix-list to filter outgoing route updates to this peer</help> + <completionHelp> + <path>policy prefix-list6</path> + </completionHelp> + </properties> + </leafNode> + <leafNode name="import"> + <properties> + <help>Prefix-list to filter incoming route updates from this peer</help> + <completionHelp> + <path>policy prefix-list6</path> + </completionHelp> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-afi-l2vpn-common.xml.i b/interface-definitions/include/bgp/bgp-afi-l2vpn-common.xml.i new file mode 100644 index 000000000..3e7e4ef78 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-afi-l2vpn-common.xml.i @@ -0,0 +1,27 @@ +<!-- include start from bgp-afi-l2vpn-common.xml.i --> +<leafNode name="advertise-default-gw"> + <properties> + <help>Advertise All default g/w mac-ip routes in EVPN</help> + <valueless/> + </properties> +</leafNode> +<leafNode name="advertise-svi-ip"> + <properties> + <help>Advertise svi mac-ip routes in EVPN</help> + <valueless/> + </properties> +</leafNode> +<leafNode name="rd"> + <properties> + <help>Route Distinguisher</help> + <valueHelp> + <format>txt</format> + <description>Route Distinguisher, (x.x.x.x:yyy|xxxx:yyyy)</description> + </valueHelp> + <constraint> + <regex>^((25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)(\.(25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)){3}|[0-9]{1,10}):[0-9]{1,5}$</regex> + </constraint> + </properties> +</leafNode> +#include <include/bgp/bgp-route-target.xml.i> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-afi-maximum-paths.xml.i b/interface-definitions/include/bgp/bgp-afi-maximum-paths.xml.i new file mode 100644 index 000000000..6b220caa5 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-afi-maximum-paths.xml.i @@ -0,0 +1,33 @@ +<!-- include start from bgp-afi-maximum-paths.xml.i --> +<node name="maximum-paths"> + <properties> + <help>Forward packets over multiple paths</help> + </properties> + <children> + <leafNode name="ebgp"> + <properties> + <help>eBGP maximum paths</help> + <valueHelp> + <format>u32:1-256</format> + <description>Number of paths to consider</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-256"/> + </constraint> + </properties> + </leafNode> + <leafNode name="ibgp"> + <properties> + <help>iBGP maximum paths</help> + <valueHelp> + <format>u32:1-256</format> + <description>Number of paths to consider</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-256"/> + </constraint> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-afi-nexthop-self.xml.i b/interface-definitions/include/bgp/bgp-afi-nexthop-self.xml.i new file mode 100644 index 000000000..a299f561e --- /dev/null +++ b/interface-definitions/include/bgp/bgp-afi-nexthop-self.xml.i @@ -0,0 +1,15 @@ +<!-- include start from bgp-afi-nexthop-self.xml.i --> +<node name="nexthop-self"> + <properties> + <help>Disable the next hop calculation for this peer</help> + </properties> + <children> + <leafNode name="force"> + <properties> + <help>Set the next hop to self for reflected routes</help> + <valueless/> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-afi-redistribute-metric-route-map.xml.i b/interface-definitions/include/bgp/bgp-afi-redistribute-metric-route-map.xml.i new file mode 100644 index 000000000..4382901c8 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-afi-redistribute-metric-route-map.xml.i @@ -0,0 +1,12 @@ +<!-- include start from bgp-afi-redistribute-metric-route-map.xml.i --> +<leafNode name="metric"> + <properties> + <help>Metric for redistributed routes</help> + <valueHelp> + <format>u32:1-4294967295</format> + <description>Metric for redistributed routes</description> + </valueHelp> + </properties> +</leafNode> +#include <include/route-map.xml.i> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-afi-route-map.xml.i b/interface-definitions/include/bgp/bgp-afi-route-map.xml.i new file mode 100644 index 000000000..7fac98586 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-afi-route-map.xml.i @@ -0,0 +1,25 @@ +<!-- include start from bgp-afi-route-map.xml.i --> +<node name="route-map"> + <properties> + <help>Route-map to filter route updates to/from this peer</help> + </properties> + <children> + <leafNode name="export"> + <properties> + <help>Route-map to filter outgoing route updates</help> + <completionHelp> + <path>policy route-map</path> + </completionHelp> + </properties> + </leafNode> + <leafNode name="import"> + <properties> + <help>Route-map to filter incoming route updates</help> + <completionHelp> + <path>policy route-map</path> + </completionHelp> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-afi-route-reflector-client.xml.i b/interface-definitions/include/bgp/bgp-afi-route-reflector-client.xml.i new file mode 100644 index 000000000..70fee40f0 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-afi-route-reflector-client.xml.i @@ -0,0 +1,8 @@ +<!-- include start from bgp-afi-route-reflector-client.xml.i --> +<leafNode name="route-reflector-client"> + <properties> + <help>Peer is a route reflector client</help> + <valueless/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-afi-route-server-client.xml.i b/interface-definitions/include/bgp/bgp-afi-route-server-client.xml.i new file mode 100644 index 000000000..29719c463 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-afi-route-server-client.xml.i @@ -0,0 +1,8 @@ +<!-- include start from bgp-afi-route-server-client.xml.i --> +<leafNode name="route-server-client"> + <properties> + <help>Peer is a route server client</help> + <valueless/> + </properties> +</leafNode> +<!-- include end -->
\ No newline at end of file diff --git a/interface-definitions/include/bgp/bgp-afi-soft-reconfiguration.xml.i b/interface-definitions/include/bgp/bgp-afi-soft-reconfiguration.xml.i new file mode 100644 index 000000000..c3f050bb8 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-afi-soft-reconfiguration.xml.i @@ -0,0 +1,15 @@ +<!-- include start from bgp-afi-soft-reconfiguration.xml.i --> +<node name="soft-reconfiguration"> + <properties> + <help>Soft reconfiguration for peer</help> + </properties> + <children> + <leafNode name="inbound"> + <properties> + <help>Enable inbound soft reconfiguration</help> + <valueless/> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-bfd.xml.i b/interface-definitions/include/bgp/bgp-bfd.xml.i new file mode 100644 index 000000000..d918fd673 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-bfd.xml.i @@ -0,0 +1,15 @@ +<!-- include start from bgp-bfd.xml.i --> +<node name="bfd"> + <properties> + <help>Enable Bidirectional Forwarding Detection (BFD) support</help> + </properties> + <children> + <leafNode name="check-control-plane-failure"> + <properties> + <help>Allow to write CBIT independence in BFD outgoing packets and read both C-BIT value of BFD and lookup BGP peer status</help> + <valueless/> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-capability.xml.i b/interface-definitions/include/bgp/bgp-capability.xml.i new file mode 100644 index 000000000..89ce19ca6 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-capability.xml.i @@ -0,0 +1,21 @@ +<!-- include start from bgp-capability.xml.i --> +<node name="capability"> + <properties> + <help>Advertise capabilities to this peer-group</help> + </properties> + <children> + <leafNode name="dynamic"> + <properties> + <help>Advertise dynamic capability to this neighbor</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="extended-nexthop"> + <properties> + <help>Advertise extended-nexthop capability to this neighbor</help> + <valueless/> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-common-config.xml.i b/interface-definitions/include/bgp/bgp-common-config.xml.i new file mode 100644 index 000000000..c89e2288e --- /dev/null +++ b/interface-definitions/include/bgp/bgp-common-config.xml.i @@ -0,0 +1,837 @@ +<!-- include start from bgp/bgp-common-config.xml.i -->
+<node name="address-family">
+ <properties>
+ <help>BGP address-family parameters</help>
+ </properties>
+ <children>
+ <node name="ipv4-unicast">
+ <properties>
+ <help>IPv4 BGP settings</help>
+ </properties>
+ <children>
+ <tagNode name="aggregate-address">
+ <properties>
+ <help>BGP aggregate network</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>BGP aggregate network</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-afi-aggregate-address.xml.i>
+ </children>
+ </tagNode>
+ <tagNode name="network">
+ <properties>
+ <help>BGP network</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>BGP network</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="backdoor">
+ <properties>
+ <help>Network as a backdoor route</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ #include <include/route-map.xml.i>
+ </children>
+ </tagNode>
+ #include <include/bgp/bgp-afi-maximum-paths.xml.i>
+ <node name="redistribute">
+ <properties>
+ <help>Redistribute routes from other protocols into BGP</help>
+ </properties>
+ <children>
+ <node name="connected">
+ <properties>
+ <help>Redistribute connected routes into BGP</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-afi-redistribute-metric-route-map.xml.i>
+ </children>
+ </node>
+ <node name="isis">
+ <properties>
+ <help>Redistribute IS-IS routes into BGP</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-afi-redistribute-metric-route-map.xml.i>
+ </children>
+ </node>
+ <node name="kernel">
+ <properties>
+ <help>Redistribute kernel routes into BGP</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-afi-redistribute-metric-route-map.xml.i>
+ </children>
+ </node>
+ <node name="ospf">
+ <properties>
+ <help>Redistribute OSPF routes into BGP</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-afi-redistribute-metric-route-map.xml.i>
+ </children>
+ </node>
+ <node name="rip">
+ <properties>
+ <help>Redistribute RIP routes into BGP</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-afi-redistribute-metric-route-map.xml.i>
+ </children>
+ </node>
+ <node name="static">
+ <properties>
+ <help>Redistribute static routes into BGP</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-afi-redistribute-metric-route-map.xml.i>
+ </children>
+ </node>
+ <leafNode name="table">
+ <properties>
+ <help>Redistribute non-main Kernel Routing Table</help>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="ipv6-unicast">
+ <properties>
+ <help>IPv6 BGP settings</help>
+ </properties>
+ <children>
+ <tagNode name="aggregate-address">
+ <properties>
+ <help>BGP aggregate network</help>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>Aggregate network</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6-prefix"/>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-afi-aggregate-address.xml.i>
+ </children>
+ </tagNode>
+ <tagNode name="network">
+ <properties>
+ <help>BGP network</help>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>Aggregate network</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6-prefix"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="path-limit">
+ <properties>
+ <help>AS-path hopcount limit</help>
+ <valueHelp>
+ <format>u32:0-255</format>
+ <description>AS path hop count limit</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/route-map.xml.i>
+ </children>
+ </tagNode>
+ #include <include/bgp/bgp-afi-maximum-paths.xml.i>
+ <node name="redistribute">
+ <properties>
+ <help>Redistribute routes from other protocols into BGP</help>
+ </properties>
+ <children>
+ <node name="connected">
+ <properties>
+ <help>Redistribute connected routes into BGP</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-afi-redistribute-metric-route-map.xml.i>
+ </children>
+ </node>
+ <node name="kernel">
+ <properties>
+ <help>Redistribute kernel routes into BGP</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-afi-redistribute-metric-route-map.xml.i>
+ </children>
+ </node>
+ <node name="ospfv3">
+ <properties>
+ <help>Redistribute OSPFv3 routes into BGP</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-afi-redistribute-metric-route-map.xml.i>
+ </children>
+ </node>
+ <node name="ripng">
+ <properties>
+ <help>Redistribute RIPng routes into BGP</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-afi-redistribute-metric-route-map.xml.i>
+ </children>
+ </node>
+ <node name="static">
+ <properties>
+ <help>Redistribute static routes into BGP</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-afi-redistribute-metric-route-map.xml.i>
+ </children>
+ </node>
+ <leafNode name="table">
+ <properties>
+ <help>Redistribute non-main Kernel Routing Table</help>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="l2vpn-evpn">
+ <properties>
+ <help>L2VPN EVPN BGP settings</help>
+ </properties>
+ <children>
+ <leafNode name="advertise-all-vni">
+ <properties>
+ <help>Advertise All local VNIs</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ #include <include/bgp/bgp-afi-l2vpn-common.xml.i>
+ <leafNode name="advertise-pip">
+ <properties>
+ <help>EVPN system primary IP</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IP address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="rt-auto-derive">
+ <properties>
+ <help>Auto derivation of Route Target (RFC8365)</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="flooding">
+ <properties>
+ <help>Specify handling for BUM packets</help>
+ </properties>
+ <children>
+ <leafNode name="disable">
+ <properties>
+ <help>Do not flood any BUM packets</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="head-end-replication">
+ <properties>
+ <help>Flood BUM packets using head-end replication</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <tagNode name="vni">
+ <properties>
+ <help>VXLAN Network Identifier</help>
+ <valueHelp>
+ <format>u32:1-16777215</format>
+ <description>VNI number</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-16777215"/>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-afi-l2vpn-common.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+</node>
+<node name="listen">
+ <properties>
+ <help>BGP dynamic neighbors listen commands</help>
+ </properties>
+ <children>
+ <leafNode name="limit">
+ <properties>
+ <help>Maximum number of dynamic neighbors that can be created</help>
+ <valueHelp>
+ <format>u32:1-5000</format>
+ <description>BGP neighbor limit</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-5000"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <tagNode name="range">
+ <properties>
+ <help>Dynamic neighbors listen range</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 dynamic neighbors listen range</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 dynamic neighbors listen range</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ <validator name="ipv6-prefix"/>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-peer-group.xml.i>
+ </children>
+ </tagNode>
+ </children>
+</node>
+<leafNode name="local-as">
+ <properties>
+ <help>Autonomous System Number (ASN)</help>
+ <valueHelp>
+ <format>u32:1-4294967294</format>
+ <description>Autonomous System Number</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967294"/>
+ </constraint>
+ </properties>
+</leafNode>
+<tagNode name="neighbor">
+ <properties>
+ <help>BGP neighbor</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>BGP neighbor IP address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>BGP neighbor IPv6 address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface name</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
+ <validator name="interface-name"/>
+ </constraint>
+ </properties>
+ <children>
+ <node name="address-family">
+ <properties>
+ <help>Parameters relating to IPv4 or IPv6 routes</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-neighbor-afi-ipv4-unicast.xml.i>
+ #include <include/bgp/bgp-neighbor-afi-ipv6-unicast.xml.i>
+ #include <include/bgp/bgp-neighbor-afi-l2vpn-evpn.xml.i>
+ </children>
+ </node>
+ <leafNode name="advertisement-interval">
+ <properties>
+ <help>Minimum interval for sending routing updates</help>
+ <valueHelp>
+ <format>u32:0-600</format>
+ <description>Advertisement interval in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-600"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/bgp/bgp-bfd.xml.i>
+ #include <include/bgp/bgp-capability.xml.i>
+ #include <include/bgp/bgp-description.xml.i>
+ #include <include/bgp/bgp-disable-capability-negotiation.xml.i>
+ #include <include/bgp/bgp-disable-connected-check.xml.i>
+ #include <include/bgp/bgp-ebgp-multihop.xml.i>
+ <node name="interface">
+ <properties>
+ <help>Interface parameters</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-peer-group.xml.i>
+ #include <include/bgp/bgp-remote-as.xml.i>
+ <node name="v6only">
+ <properties>
+ <help>Enable BGP with v6 link-local only</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-peer-group.xml.i>
+ #include <include/bgp/bgp-remote-as.xml.i>
+ </children>
+ </node>
+ </children>
+ </node>
+ #include <include/bgp/bgp-local-as.xml.i>
+ #include <include/bgp/bgp-override-capability.xml.i>
+ #include <include/bgp/bgp-passive.xml.i>
+ #include <include/bgp/bgp-password.xml.i>
+ #include <include/bgp/bgp-peer-group.xml.i>
+ <leafNode name="port">
+ <properties>
+ <help>Neighbor BGP port</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Neighbor BGP port number</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/bgp/bgp-remote-as.xml.i>
+ #include <include/bgp/bgp-shutdown.xml.i>
+ <leafNode name="strict-capability-match">
+ <properties>
+ <help>Enable strict capability negotiation</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="timers">
+ <properties>
+ <help>Neighbor timers</help>
+ </properties>
+ <children>
+ <leafNode name="connect">
+ <properties>
+ <help>BGP connect timer for this neighbor</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Connect timer in seconds</description>
+ </valueHelp>
+ <valueHelp>
+ <format>0</format>
+ <description>Disable connect timer</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/bgp/bgp-timers-holdtime.xml.i>
+ #include <include/bgp/bgp-timers-keepalive.xml.i>
+ </children>
+ </node>
+ #include <include/bgp/bgp-ttl-security.xml.i>
+ #include <include/bgp/bgp-update-source.xml.i>
+ </children>
+</tagNode>
+<node name="parameters">
+ <properties>
+ <help>BGP parameters</help>
+ </properties>
+ <children>
+ <leafNode name="always-compare-med">
+ <properties>
+ <help>Always compare MEDs from different neighbors</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="bestpath">
+ <properties>
+ <help>Default bestpath selection mechanism</help>
+ </properties>
+ <children>
+ <node name="as-path">
+ <properties>
+ <help>AS-path attribute comparison parameters</help>
+ </properties>
+ <children>
+ <leafNode name="confed">
+ <properties>
+ <help>Compare AS-path lengths including confederation sets and sequences</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="ignore">
+ <properties>
+ <help>Ignore AS-path length in selecting a route</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="multipath-relax">
+ <properties>
+ <help>Allow load sharing across routes that have different AS paths (but same length)</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="compare-routerid">
+ <properties>
+ <help>Compare the router-id for identical EBGP paths</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="med">
+ <properties>
+ <help>MED attribute comparison parameters</help>
+ </properties>
+ <children>
+ <leafNode name="confed">
+ <properties>
+ <help>Compare MEDs among confederation paths</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="missing-as-worst">
+ <properties>
+ <help>Treat missing route as a MED as the least preferred one</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <leafNode name="cluster-id">
+ <properties>
+ <help>Route-reflector cluster-id</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Route-reflector cluster-id</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <node name="confederation">
+ <properties>
+ <help>AS confederation parameters</help>
+ </properties>
+ <children>
+ <leafNode name="identifier">
+ <properties>
+ <help>Confederation AS identifier [REQUIRED]</help>
+ <valueHelp>
+ <format>u32:1-4294967294</format>
+ <description>Confederation AS id</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967294"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="peers">
+ <properties>
+ <help>Peer ASs in the BGP confederation</help>
+ <valueHelp>
+ <format>u32:1-4294967294</format>
+ <description>Peer AS number</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967294"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="dampening">
+ <properties>
+ <help>Enable route-flap dampening</help>
+ </properties>
+ <children>
+ <leafNode name="half-life">
+ <properties>
+ <help>Half-life time for dampening [REQUIRED]</help>
+ <valueHelp>
+ <format>u32:1-45</format>
+ <description>Half-life penalty in minutes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-45"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="max-suppress-time">
+ <properties>
+ <help>Maximum duration to suppress a stable route [REQUIRED]</help>
+ <valueHelp>
+ <format>u32:1-255</format>
+ <description>Maximum suppress duration in minutes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="re-use">
+ <properties>
+ <help>Threshold to start reusing a route [REQUIRED]</help>
+ <valueHelp>
+ <format>u32:1-20000</format>
+ <description>Re-use penalty points</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-20000"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="start-suppress-time">
+ <properties>
+ <help>When to start suppressing a route [REQUIRED]</help>
+ <valueHelp>
+ <format>u32:1-20000</format>
+ <description>Start-suppress penalty points</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-20000"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="default">
+ <properties>
+ <help>BGP defaults</help>
+ </properties>
+ <children>
+ <leafNode name="local-pref">
+ <properties>
+ <help>Default local preference</help>
+ <valueHelp>
+ <format>u32</format>
+ <description>Local preference</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967295"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="no-ipv4-unicast">
+ <properties>
+ <help>Deactivate IPv4 unicast for a peer by default</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="deterministic-med">
+ <properties>
+ <help>Compare MEDs between different peers in the same AS</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="distance">
+ <properties>
+ <help>Administratives distances for BGP routes</help>
+ </properties>
+ <children>
+ <node name="global">
+ <properties>
+ <help>Global administratives distances for BGP routes</help>
+ </properties>
+ <children>
+ <leafNode name="external">
+ <properties>
+ <help>Administrative distance for external BGP routes</help>
+ <valueHelp>
+ <format>u32:1-255</format>
+ <description>Administrative distance for external BGP routes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="internal">
+ <properties>
+ <help>Administrative distance for internal BGP routes</help>
+ <valueHelp>
+ <format>u32:1-255</format>
+ <description>Administrative distance for internal BGP routes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="local">
+ <properties>
+ <help>Administrative distance for local BGP routes</help>
+ <valueHelp>
+ <format>u32:1-255</format>
+ <description>Administrative distance for internal BGP routes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <tagNode name="prefix">
+ <properties>
+ <help>Administrative distance for a specific BGP prefix</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>Administrative distance for a specific BGP prefix</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="distance">
+ <properties>
+ <help>Administrative distance for prefix</help>
+ <valueHelp>
+ <format>u32:1-255</format>
+ <description>Administrative distance for external BGP routes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ <leafNode name="ebgp-requires-policy">
+ <properties>
+ <help>Require in and out policy for eBGP peers (RFC8212)</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="graceful-restart">
+ <properties>
+ <help>Graceful restart capability parameters</help>
+ </properties>
+ <children>
+ <leafNode name="stalepath-time">
+ <properties>
+ <help>Maximum time to hold onto restarting neighbors stale paths</help>
+ <valueHelp>
+ <format>u32:1-3600</format>
+ <description>Hold time in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-3600"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="graceful-shutdown">
+ <properties>
+ <help>Graceful shutdown</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="log-neighbor-changes">
+ <properties>
+ <help>Log neighbor up/down changes and reset reason</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="network-import-check">
+ <properties>
+ <help>Enable IGP route check for network statements</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="no-client-to-client-reflection">
+ <properties>
+ <help>Disable client to client route reflection</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="no-fast-external-failover">
+ <properties>
+ <help>Disable immediate session reset on peer link down event</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="router-id">
+ <properties>
+ <help>BGP router id</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>BGP router id</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<tagNode name="peer-group">
+ <properties>
+ <help>BGP peer-group</help>
+ </properties>
+ <children>
+ <node name="address-family">
+ <properties>
+ <help>BGP peer-group address-family parameters</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-neighbor-afi-ipv4-unicast.xml.i>
+ #include <include/bgp/bgp-neighbor-afi-ipv6-unicast.xml.i>
+ #include <include/bgp/bgp-neighbor-afi-l2vpn-evpn.xml.i>
+ </children>
+ </node>
+ #include <include/bgp/bgp-bfd.xml.i>
+ #include <include/bgp/bgp-capability.xml.i>
+ #include <include/bgp/bgp-description.xml.i>
+ #include <include/bgp/bgp-disable-capability-negotiation.xml.i>
+ #include <include/bgp/bgp-disable-connected-check.xml.i>
+ #include <include/bgp/bgp-ebgp-multihop.xml.i>
+ #include <include/bgp/bgp-local-as.xml.i>
+ #include <include/bgp/bgp-override-capability.xml.i>
+ #include <include/bgp/bgp-passive.xml.i>
+ #include <include/bgp/bgp-password.xml.i>
+ #include <include/bgp/bgp-remote-as.xml.i>
+ #include <include/bgp/bgp-shutdown.xml.i>
+ #include <include/bgp/bgp-ttl-security.xml.i>
+ #include <include/bgp/bgp-update-source.xml.i>
+ </children>
+</tagNode>
+#include <include/route-map.xml.i>
+<node name="timers">
+ <properties>
+ <help>BGP protocol timers</help>
+ </properties>
+ <children>
+ #include <include/bgp/bgp-timers-holdtime.xml.i>
+ #include <include/bgp/bgp-timers-keepalive.xml.i>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/bgp/bgp-description.xml.i b/interface-definitions/include/bgp/bgp-description.xml.i new file mode 100644 index 000000000..308bbec12 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-description.xml.i @@ -0,0 +1,7 @@ +<!-- include start from bgp-description.xml.i --> +<leafNode name="description"> + <properties> + <help>Neighbor specific description</help> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-disable-capability-negotiation.xml.i b/interface-definitions/include/bgp/bgp-disable-capability-negotiation.xml.i new file mode 100644 index 000000000..74c3321d9 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-disable-capability-negotiation.xml.i @@ -0,0 +1,8 @@ +<!-- include start from bgp-disable-capability-negotiation.xml.i --> +<leafNode name="disable-capability-negotiation"> + <properties> + <help>Disable capability negotiation with this neighbor</help> + <valueless/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-disable-connected-check.xml.i b/interface-definitions/include/bgp/bgp-disable-connected-check.xml.i new file mode 100644 index 000000000..15142b0ac --- /dev/null +++ b/interface-definitions/include/bgp/bgp-disable-connected-check.xml.i @@ -0,0 +1,8 @@ +<!-- include start from bgp-disable-connected-check.xml.i --> +<leafNode name="disable-connected-check"> + <properties> + <help>Disable check to see if eBGP peer address is a connected route</help> + <valueless/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-ebgp-multihop.xml.i b/interface-definitions/include/bgp/bgp-ebgp-multihop.xml.i new file mode 100644 index 000000000..48580af3c --- /dev/null +++ b/interface-definitions/include/bgp/bgp-ebgp-multihop.xml.i @@ -0,0 +1,14 @@ +<!-- include start from bgp-ebgp-multihop.xml.i --> +<leafNode name="ebgp-multihop"> + <properties> + <help>Allow this EBGP neighbor to not be on a directly connected network</help> + <valueHelp> + <format>u32:1-255</format> + <description>Number of hops</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-local-as.xml.i b/interface-definitions/include/bgp/bgp-local-as.xml.i new file mode 100644 index 000000000..7fc896a31 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-local-as.xml.i @@ -0,0 +1,22 @@ +<!-- include start from bgp-local-as.xml.i --> +<tagNode name="local-as"> + <properties> + <help>Local AS number [REQUIRED]</help> + <valueHelp> + <format>u32:1-4294967294</format> + <description>Local AS number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967294"/> + </constraint> + </properties> + <children> + <leafNode name="no-prepend"> + <properties> + <help>Disable prepending local-as to updates from EBGP peers</help> + <valueless/> + </properties> + </leafNode> + </children> +</tagNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-neighbor-afi-ipv4-unicast.xml.i b/interface-definitions/include/bgp/bgp-neighbor-afi-ipv4-unicast.xml.i new file mode 100644 index 000000000..945483276 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-neighbor-afi-ipv4-unicast.xml.i @@ -0,0 +1,19 @@ +<!-- include start from bgp-neighbor-afi-ipv4-unicast.xml.i --> +<node name="ipv4-unicast"> + <properties> + <help>IPv4 BGP neighbor parameters</help> + </properties> + <children> + <node name="capability"> + <properties> + <help>Advertise capabilities to this neighbor (IPv4)</help> + </properties> + <children> + #include <include/bgp/bgp-afi-capability-orf.xml.i> + </children> + </node> + #include <include/bgp/bgp-afi-ipv4-prefix-list.xml.i> + #include <include/bgp/bgp-afi-common.xml.i> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-neighbor-afi-ipv6-unicast.xml.i b/interface-definitions/include/bgp/bgp-neighbor-afi-ipv6-unicast.xml.i new file mode 100644 index 000000000..4cd676cb4 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-neighbor-afi-ipv6-unicast.xml.i @@ -0,0 +1,20 @@ +<!-- include start from bgp-neighbor-afi-ipv6-unicast.xml.i --> +<node name="ipv6-unicast"> + <properties> + <help>IPv6 BGP neighbor parameters</help> + </properties> + <children> + <node name="capability"> + <properties> + <help>Advertise capabilities to this neighbor (IPv6)</help> + </properties> + <children> + #include <include/bgp/bgp-afi-capability-orf.xml.i> + </children> + </node> + #include <include/bgp/bgp-afi-ipv6-nexthop-local.xml.i> + #include <include/bgp/bgp-afi-ipv6-prefix-list.xml.i> + #include <include/bgp/bgp-afi-common.xml.i> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-neighbor-afi-l2vpn-evpn.xml.i b/interface-definitions/include/bgp/bgp-neighbor-afi-l2vpn-evpn.xml.i new file mode 100644 index 000000000..0a9c599fa --- /dev/null +++ b/interface-definitions/include/bgp/bgp-neighbor-afi-l2vpn-evpn.xml.i @@ -0,0 +1,16 @@ +<!-- include start from bgp-neighbor-afi-l2vpn-evpn.xml.i --> +<node name="l2vpn-evpn"> + <properties> + <help>L2VPN EVPN BGP settings</help> + </properties> + <children> + #include <include/bgp/bgp-afi-allowas-in.xml.i> + #include <include/bgp/bgp-afi-attribute-unchanged.xml.i> + #include <include/bgp/bgp-afi-nexthop-self.xml.i> + #include <include/bgp/bgp-afi-route-map.xml.i> + #include <include/bgp/bgp-afi-route-reflector-client.xml.i> + #include <include/bgp/bgp-afi-route-server-client.xml.i> + #include <include/bgp/bgp-afi-soft-reconfiguration.xml.i> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-override-capability.xml.i b/interface-definitions/include/bgp/bgp-override-capability.xml.i new file mode 100644 index 000000000..1e51a49d5 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-override-capability.xml.i @@ -0,0 +1,8 @@ +<!-- include start from bgp-override-capability.xml.i --> +<leafNode name="override-capability"> + <properties> + <help>Ignore capability negotiation with specified neighbor</help> + <valueless/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-passive.xml.i b/interface-definitions/include/bgp/bgp-passive.xml.i new file mode 100644 index 000000000..033cf8231 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-passive.xml.i @@ -0,0 +1,8 @@ +<!-- include start from bgp-passive.xml.i --> +<leafNode name="passive"> + <properties> + <help>Do not initiate a session with this neighbor</help> + <valueless/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-password.xml.i b/interface-definitions/include/bgp/bgp-password.xml.i new file mode 100644 index 000000000..f5878cce9 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-password.xml.i @@ -0,0 +1,7 @@ +<!-- include start from bgp-password.xml.i --> +<leafNode name="password"> + <properties> + <help>BGP MD5 password</help> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-peer-group.xml.i b/interface-definitions/include/bgp/bgp-peer-group.xml.i new file mode 100644 index 000000000..8fc50794d --- /dev/null +++ b/interface-definitions/include/bgp/bgp-peer-group.xml.i @@ -0,0 +1,14 @@ +<!-- include start from bgp-peer-group.xml.i --> +<leafNode name="peer-group"> + <properties> + <help>Peer group for this peer</help> + <completionHelp> + <script>${vyos_completion_dir}/list_bgp_peer_groups.sh</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Peer-group name</description> + </valueHelp> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-remote-as.xml.i b/interface-definitions/include/bgp/bgp-remote-as.xml.i new file mode 100644 index 000000000..f036fe13d --- /dev/null +++ b/interface-definitions/include/bgp/bgp-remote-as.xml.i @@ -0,0 +1,27 @@ +<!-- include start from bgp-remote-as.xml.i --> +<leafNode name="remote-as"> + <properties> + <help>Neighbor BGP AS number [REQUIRED]</help> + <completionHelp> + <list>external internal</list> + </completionHelp> + <valueHelp> + <format>u32:1-4294967294</format> + <description>Neighbor AS number</description> + </valueHelp> + <valueHelp> + <format>external</format> + <description>Any AS different from the local AS</description> + </valueHelp> + <valueHelp> + <format>internal</format> + <description>Neighbor AS number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967294"/> + <regex>^(external|internal)$</regex> + </constraint> + <constraintErrorMessage>Invalid AS number</constraintErrorMessage> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-route-target.xml.i b/interface-definitions/include/bgp/bgp-route-target.xml.i new file mode 100644 index 000000000..c05ac5dc2 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-route-target.xml.i @@ -0,0 +1,45 @@ +<!-- include start from bgp-route-target.xml.i --> +<node name="route-target"> + <properties> + <help>Route Target</help> + </properties> + <children> + <leafNode name="both"> + <properties> + <help>Route Target both import and export</help> + <valueHelp> + <format>txt</format> + <description>Route target (x.x.x.x:yyy|xxxx:yyyy)</description> + </valueHelp> + <constraint> + <regex>^((25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)(\.(25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)){3}|[0-9]{1,10}):[0-9]{1,5}$</regex> + </constraint> + </properties> + </leafNode> + <leafNode name="export"> + <properties> + <help>Route Target export</help> + <valueHelp> + <format>txt</format> + <description>Route target (x.x.x.x:yyy|xxxx:yyyy)</description> + </valueHelp> + <constraint> + <regex>^((25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)(\.(25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)){3}|[0-9]{1,10}):[0-9]{1,5}$</regex> + </constraint> + </properties> + </leafNode> + <leafNode name="import"> + <properties> + <help>Route Target import</help> + <valueHelp> + <format>txt</format> + <description>Route target (x.x.x.x:yyy|xxxx:yyyy)</description> + </valueHelp> + <constraint> + <regex>^((25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)(\.(25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)){3}|[0-9]{1,10}):[0-9]{1,5}$</regex> + </constraint> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-shutdown.xml.i b/interface-definitions/include/bgp/bgp-shutdown.xml.i new file mode 100644 index 000000000..f920e9579 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-shutdown.xml.i @@ -0,0 +1,8 @@ +<!-- include start from bgp-shutdown.xml.i --> +<leafNode name="shutdown"> + <properties> + <help>Administratively shut down this neighbor</help> + <valueless/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-timers-holdtime.xml.i b/interface-definitions/include/bgp/bgp-timers-holdtime.xml.i new file mode 100644 index 000000000..9c16127b5 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-timers-holdtime.xml.i @@ -0,0 +1,18 @@ +<!-- include start from bgp-timers-holdtime.xml.i --> +<leafNode name="holdtime"> + <properties> + <help>BGP hold timer for this neighbor</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Hold timer in seconds</description> + </valueHelp> + <valueHelp> + <format>0</format> + <description>Hold timer disabled</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-65535"/> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-timers-keepalive.xml.i b/interface-definitions/include/bgp/bgp-timers-keepalive.xml.i new file mode 100644 index 000000000..8c3e66c6a --- /dev/null +++ b/interface-definitions/include/bgp/bgp-timers-keepalive.xml.i @@ -0,0 +1,14 @@ +<!-- include start from bgp-timers-keepalive.xml.i --> +<leafNode name="keepalive"> + <properties> + <help>BGP keepalive interval for this neighbor</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Keepalive interval in seconds (default 60)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-ttl-security.xml.i b/interface-definitions/include/bgp/bgp-ttl-security.xml.i new file mode 100644 index 000000000..1fb1c2c55 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-ttl-security.xml.i @@ -0,0 +1,21 @@ +<!-- include start from bgp-ttl-security.xml.i --> +<node name="ttl-security"> + <properties> + <help>Ttl security mechanism</help> + </properties> + <children> + <leafNode name="hops"> + <properties> + <help>Number of the maximum number of hops to the BGP peer</help> + <valueHelp> + <format>u32:1-254</format> + <description>Number of hops</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-254"/> + </constraint> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/bgp-update-source.xml.i b/interface-definitions/include/bgp/bgp-update-source.xml.i new file mode 100644 index 000000000..f4ccc3553 --- /dev/null +++ b/interface-definitions/include/bgp/bgp-update-source.xml.i @@ -0,0 +1,29 @@ +<!-- include start from bgp-update-source.xml.i --> +<leafNode name="update-source"> + <!-- Need to check format interfaces --> + <properties> + <help>Source IP of routing updates</help> + <completionHelp> + <script>${vyos_completion_dir}/list_local_ips.sh --both</script> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <valueHelp> + <format>ipv4</format> + <description>IPv4 address of route source</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address of route source</description> + </valueHelp> + <valueHelp> + <format>txt</format> + <description>Interface as route source</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + <validator name="ipv6-address"/> + <validator name="interface-name"/> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/certificate-ca.xml.i b/interface-definitions/include/certificate-ca.xml.i index df12746aa..b97378658 100644 --- a/interface-definitions/include/certificate-ca.xml.i +++ b/interface-definitions/include/certificate-ca.xml.i @@ -1,4 +1,4 @@ -<!-- included start from certificate-ca.xml.i --> +<!-- include start from certificate-ca.xml.i --> <leafNode name="ca-cert-file"> <properties> <help>Certificate Authority in x509 PEM format</help> @@ -11,4 +11,4 @@ </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/certificate-key.xml.i b/interface-definitions/include/certificate-key.xml.i index 924823c76..1db9dd069 100644 --- a/interface-definitions/include/certificate-key.xml.i +++ b/interface-definitions/include/certificate-key.xml.i @@ -1,4 +1,4 @@ -<!-- included start from certificate-key.xml.i --> +<!-- include start from certificate-key.xml.i --> <leafNode name="key-file"> <properties> <help>Certificate private key in x509 PEM format</help> @@ -11,4 +11,4 @@ </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/certificate.xml.i b/interface-definitions/include/certificate.xml.i index 724a8a27f..fb5be45cc 100644 --- a/interface-definitions/include/certificate.xml.i +++ b/interface-definitions/include/certificate.xml.i @@ -1,4 +1,4 @@ -<!-- included start from certificate.xml.i --> +<!-- include start from certificate.xml.i --> <leafNode name="cert-file"> <properties> <help>Certificate public key in x509 PEM format</help> @@ -11,4 +11,4 @@ </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/dhcp-server-domain-search.xml.i b/interface-definitions/include/dhcp-server-domain-search.xml.i index 9b3568b72..4fc55097b 100644 --- a/interface-definitions/include/dhcp-server-domain-search.xml.i +++ b/interface-definitions/include/dhcp-server-domain-search.xml.i @@ -1,4 +1,4 @@ -<!-- included start from dhcp-server-domain-search.xml.i --> +<!-- include start from dhcp-server-domain-search.xml.i --> <leafNode name="domain-search"> <properties> <help>Client Domain Name search list</help> @@ -9,4 +9,4 @@ <multi/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/generic-disable-node.xml.i b/interface-definitions/include/generic-disable-node.xml.i new file mode 100644 index 000000000..bb4fa5c4b --- /dev/null +++ b/interface-definitions/include/generic-disable-node.xml.i @@ -0,0 +1,8 @@ +<!-- include start from generic-disable-node.xml.i --> +<leafNode name="disable"> + <properties> + <help>Temporary disable</help> + <valueless/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/interface-ipv4-options.xml.i b/interface-definitions/include/interface-ipv4-options.xml.i deleted file mode 100644 index c63f89890..000000000 --- a/interface-definitions/include/interface-ipv4-options.xml.i +++ /dev/null @@ -1,18 +0,0 @@ -<!-- included start from interface-ipv4-options.xml.i --> -<node name="ip"> - <properties> - <help>IPv4 routing parameters</help> - </properties> - <children> - #include <include/interface-arp-cache-timeout.xml.i> - #include <include/interface-disable-arp-filter.xml.i> - #include <include/interface-disable-forwarding.xml.i> - #include <include/interface-enable-arp-accept.xml.i> - #include <include/interface-enable-arp-announce.xml.i> - #include <include/interface-enable-arp-ignore.xml.i> - #include <include/interface-enable-proxy-arp.xml.i> - #include <include/interface-proxy-arp-pvlan.xml.i> - #include <include/interface-source-validation.xml.i> - </children> -</node> -<!-- included end --> diff --git a/interface-definitions/include/interface-ipv6-options.xml.i b/interface-definitions/include/interface-ipv6-options.xml.i deleted file mode 100644 index a94c6572b..000000000 --- a/interface-definitions/include/interface-ipv6-options.xml.i +++ /dev/null @@ -1,12 +0,0 @@ -<!-- included start from interface-ipv6-options.xml.i --> -<node name="ipv6"> - <properties> - <help>IPv6 routing parameters</help> - </properties> - <children> - #include <include/ipv6-address.xml.i> - #include <include/ipv6-disable-forwarding.xml.i> - #include <include/ipv6-dup-addr-detect-transmits.xml.i> - </children> -</node> -<!-- included end --> diff --git a/interface-definitions/include/address-ipv4-ipv6-dhcp.xml.i b/interface-definitions/include/interface/address-ipv4-ipv6-dhcp.xml.i index 7805110bc..b9dd59bea 100644 --- a/interface-definitions/include/address-ipv4-ipv6-dhcp.xml.i +++ b/interface-definitions/include/interface/address-ipv4-ipv6-dhcp.xml.i @@ -1,4 +1,4 @@ -<!-- included start from address-ipv4-ipv6-dhcp.xml.i --> +<!-- include start from address-ipv4-ipv6-dhcp.xml.i --> <leafNode name="address"> <properties> <help>IP address</help> @@ -28,4 +28,4 @@ <multi/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/address-ipv4-ipv6.xml.i b/interface-definitions/include/interface/address-ipv4-ipv6.xml.i index b11610104..519622050 100644 --- a/interface-definitions/include/address-ipv4-ipv6.xml.i +++ b/interface-definitions/include/interface/address-ipv4-ipv6.xml.i @@ -1,4 +1,4 @@ -<!-- included start from address-ipv4-ipv6.xml.i --> +<!-- include start from address-ipv4-ipv6.xml.i --> <leafNode name="address"> <properties> <help>IP address</help> @@ -16,4 +16,4 @@ <multi/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/dhcp-options.xml.i b/interface-definitions/include/interface/dhcp-options.xml.i index df9c7a97a..bd327da2d 100644 --- a/interface-definitions/include/dhcp-options.xml.i +++ b/interface-definitions/include/interface/dhcp-options.xml.i @@ -1,4 +1,4 @@ -<!-- included start from dhcp-options.xml.i --> +<!-- include start from interface/dhcp-options.xml.i --> <node name="dhcp-options"> <properties> <help>DHCP client settings/options</help> @@ -25,6 +25,18 @@ <valueless/> </properties> </leafNode> + <leafNode name="default-route-distance"> + <properties> + <help>Distance for the default route from DHCP server</help> + <valueHelp> + <format>u32:1-255</format> + <description>Distance for the default route from DHCP server (default 210)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> + </leafNode> </children> </node> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/dhcpv6-options.xml.i b/interface-definitions/include/interface/dhcpv6-options.xml.i index 9a1016956..ca478a3eb 100644 --- a/interface-definitions/include/dhcpv6-options.xml.i +++ b/interface-definitions/include/interface/dhcpv6-options.xml.i @@ -1,9 +1,21 @@ -<!-- included start from dhcpv6-options.xml.i --> +<!-- include start from interface/dhcpv6-options.xml.i --> <node name="dhcpv6-options"> <properties> <help>DHCPv6 client settings/options</help> </properties> <children> + <leafNode name="duid"> + <properties> + <help>DHCP unique identifier (DUID) to be sent by dhcpv6 client</help> + <valueHelp> + <format>duid</format> + <description>DHCP unique identifier (DUID)</description> + </valueHelp> + <constraint> + <validator name="ipv6-duid"/> + </constraint> + </properties> + </leafNode> <leafNode name="parameters-only"> <properties> <help>Acquire only config parameters, no address</help> @@ -85,4 +97,4 @@ </leafNode> </children> </node> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-arp-cache-timeout.xml.i b/interface-definitions/include/interface/interface-arp-cache-timeout.xml.i index 6dfebfee4..b269fecd8 100644 --- a/interface-definitions/include/interface-arp-cache-timeout.xml.i +++ b/interface-definitions/include/interface/interface-arp-cache-timeout.xml.i @@ -1,4 +1,4 @@ -<!-- included start from interface-arp-cache-timeout.xml.i --> +<!-- include start from interface/interface-arp-cache-timeout.xml.i --> <leafNode name="arp-cache-timeout"> <properties> <help>ARP cache entry timeout in seconds</help> @@ -13,4 +13,4 @@ </properties> <defaultValue>30</defaultValue> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-description.xml.i b/interface-definitions/include/interface/interface-description.xml.i index daf09d8bc..d618b50d2 100644 --- a/interface-definitions/include/interface-description.xml.i +++ b/interface-definitions/include/interface/interface-description.xml.i @@ -1,4 +1,4 @@ -<!-- included start from interface-description.xml.i --> +<!-- include start from interface/interface-description.xml.i --> <leafNode name="description"> <properties> <help>Interface specific description</help> @@ -8,4 +8,4 @@ <constraintErrorMessage>Description too long (limit 256 characters)</constraintErrorMessage> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-dial-on-demand.xml.i b/interface-definitions/include/interface/interface-dial-on-demand.xml.i index 8fba8099d..66edd9678 100644 --- a/interface-definitions/include/interface-dial-on-demand.xml.i +++ b/interface-definitions/include/interface/interface-dial-on-demand.xml.i @@ -1,8 +1,8 @@ -<!-- included start from interface-dial-on-demand.xml.i --> +<!-- include start from interface/interface-dial-on-demand.xml.i --> <leafNode name="connect-on-demand"> <properties> <help>Establishment connection automatically when traffic is sent</help> <valueless/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-disable-arp-filter.xml.i b/interface-definitions/include/interface/interface-disable-arp-filter.xml.i index 4de3ca893..49cddaf76 100644 --- a/interface-definitions/include/interface-disable-arp-filter.xml.i +++ b/interface-definitions/include/interface/interface-disable-arp-filter.xml.i @@ -1,8 +1,8 @@ -<!-- included start from interface-disable-arp-filter.xml.i --> +<!-- include start from interface/interface-disable-arp-filter.xml.i --> <leafNode name="disable-arp-filter"> <properties> <help>Disable ARP filter on this interface</help> <valueless/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-disable-forwarding.xml.i b/interface-definitions/include/interface/interface-disable-forwarding.xml.i index 7cbb726ec..cb6ef0475 100644 --- a/interface-definitions/include/interface-disable-forwarding.xml.i +++ b/interface-definitions/include/interface/interface-disable-forwarding.xml.i @@ -1,8 +1,8 @@ -<!-- included start from interface-disable-forwarding.xml.i --> +<!-- include start from interface/interface-disable-forwarding.xml.i --> <leafNode name="disable-forwarding"> <properties> <help>Disable IPv4 forwarding on this interface</help> <valueless/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-disable-link-detect.xml.i b/interface-definitions/include/interface/interface-disable-link-detect.xml.i index 4298b4b5d..c528885b2 100644 --- a/interface-definitions/include/interface-disable-link-detect.xml.i +++ b/interface-definitions/include/interface/interface-disable-link-detect.xml.i @@ -1,8 +1,8 @@ -<!-- included start from interface-disable-link-detect.xml.i --> +<!-- include start from interface/interface-disable-link-detect.xml.i --> <leafNode name="disable-link-detect"> <properties> <help>Ignore link state changes</help> <valueless/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-disable.xml.i b/interface-definitions/include/interface/interface-disable.xml.i index 5d73d54ba..d90e6395b 100644 --- a/interface-definitions/include/interface-disable.xml.i +++ b/interface-definitions/include/interface/interface-disable.xml.i @@ -1,8 +1,8 @@ -<!-- included start from interface-disable.xml.i --> +<!-- include start from interface/interface-disable.xml.i --> <leafNode name="disable"> <properties> <help>Administratively disable interface</help> <valueless/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-eapol.xml.i b/interface-definitions/include/interface/interface-eapol.xml.i index 94476f0f1..92b7a3f35 100644 --- a/interface-definitions/include/interface-eapol.xml.i +++ b/interface-definitions/include/interface/interface-eapol.xml.i @@ -1,12 +1,12 @@ -<!-- included start from interface-eapol.xml.i -->
-<node name="eapol">
- <properties>
- <help>Extensible Authentication Protocol over Local Area Network</help>
- </properties>
- <children>
- #include <include/certificate.xml.i>
- #include <include/certificate-ca.xml.i>
- #include <include/certificate-key.xml.i>
- </children>
-</node>
-<!-- included end -->
+<!-- include start from interface/interface-eapol.xml.i --> +<node name="eapol"> + <properties> + <help>Extensible Authentication Protocol over Local Area Network</help> + </properties> + <children> + #include <include/certificate.xml.i> + #include <include/certificate-ca.xml.i> + #include <include/certificate-key.xml.i> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/interface-enable-arp-accept.xml.i b/interface-definitions/include/interface/interface-enable-arp-accept.xml.i index 688b3572e..7c5d51857 100644 --- a/interface-definitions/include/interface-enable-arp-accept.xml.i +++ b/interface-definitions/include/interface/interface-enable-arp-accept.xml.i @@ -1,8 +1,8 @@ -<!-- included start from interface-enable-arp-accept.xml.i --> +<!-- include start from interface/interface-enable-arp-accept.xml.i --> <leafNode name="enable-arp-accept"> <properties> <help>Enable ARP accept on this interface</help> <valueless/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-enable-arp-announce.xml.i b/interface-definitions/include/interface/interface-enable-arp-announce.xml.i index c84bb7ea9..f44599c54 100644 --- a/interface-definitions/include/interface-enable-arp-announce.xml.i +++ b/interface-definitions/include/interface/interface-enable-arp-announce.xml.i @@ -1,8 +1,8 @@ -<!-- included start from interface-enable-arp-announce.xml.i --> +<!-- include start from interface/interface-enable-arp-announce.xml.i --> <leafNode name="enable-arp-announce"> <properties> <help>Enable ARP announce on this interface</help> <valueless/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-enable-arp-ignore.xml.i b/interface-definitions/include/interface/interface-enable-arp-ignore.xml.i index 741771a89..3ea39613c 100644 --- a/interface-definitions/include/interface-enable-arp-ignore.xml.i +++ b/interface-definitions/include/interface/interface-enable-arp-ignore.xml.i @@ -1,8 +1,8 @@ -<!-- included start from interface-enable-arp-ignore.xml.i --> +<!-- include start from interface/interface-enable-arp-ignore.xml.i --> <leafNode name="enable-arp-ignore"> <properties> <help>Enable ARP ignore on this interface</help> <valueless/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-enable-proxy-arp.xml.i b/interface-definitions/include/interface/interface-enable-proxy-arp.xml.i index 08351e673..dbdeeb7a7 100644 --- a/interface-definitions/include/interface-enable-proxy-arp.xml.i +++ b/interface-definitions/include/interface/interface-enable-proxy-arp.xml.i @@ -1,8 +1,8 @@ -<!-- included start from interface-enable-proxy-arp.xml.i --> +<!-- include start from interface/interface-enable-proxy-arp.xml.i --> <leafNode name="enable-proxy-arp"> <properties> <help>Enable proxy-arp on this interface</help> <valueless/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-hw-id.xml.i b/interface-definitions/include/interface/interface-hw-id.xml.i index af58fae3c..989cd9cb7 100644 --- a/interface-definitions/include/interface-hw-id.xml.i +++ b/interface-definitions/include/interface/interface-hw-id.xml.i @@ -1,14 +1,14 @@ -<!-- included start from interface-hw-id.xml.i --> +<!-- include start from interface/interface-hw-id.xml.i --> <leafNode name="hw-id"> <properties> <help>Associate Ethernet Interface with given Media Access Control (MAC) address</help> <valueHelp> - <format>h:h:h:h:h:h</format> - <description>Hardware Media Access Control (MAC) address</description> + <format>macaddr</format> + <description>Hardware (MAC) address</description> </valueHelp> <constraint> <validator name="mac-address"/> </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface/interface-ipv4-options.xml.i b/interface-definitions/include/interface/interface-ipv4-options.xml.i new file mode 100644 index 000000000..c2d0677b7 --- /dev/null +++ b/interface-definitions/include/interface/interface-ipv4-options.xml.i @@ -0,0 +1,18 @@ +<!-- include start from interface/interface-ipv4-options.xml.i --> +<node name="ip"> + <properties> + <help>IPv4 routing parameters</help> + </properties> + <children> + #include <include/interface/interface-arp-cache-timeout.xml.i> + #include <include/interface/interface-disable-arp-filter.xml.i> + #include <include/interface/interface-disable-forwarding.xml.i> + #include <include/interface/interface-enable-arp-accept.xml.i> + #include <include/interface/interface-enable-arp-announce.xml.i> + #include <include/interface/interface-enable-arp-ignore.xml.i> + #include <include/interface/interface-enable-proxy-arp.xml.i> + #include <include/interface/interface-proxy-arp-pvlan.xml.i> + #include <include/interface/interface-source-validation.xml.i> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/interface/interface-ipv6-options.xml.i b/interface-definitions/include/interface/interface-ipv6-options.xml.i new file mode 100644 index 000000000..dcd5a8710 --- /dev/null +++ b/interface-definitions/include/interface/interface-ipv6-options.xml.i @@ -0,0 +1,12 @@ +<!-- include start from interface/interface-ipv6-options.xml.i --> +<node name="ipv6"> + <properties> + <help>IPv6 routing parameters</help> + </properties> + <children> + #include <include/interface/ipv6-address.xml.i> + #include <include/interface/ipv6-disable-forwarding.xml.i> + #include <include/interface/ipv6-dup-addr-detect-transmits.xml.i> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/interface-mac.xml.i b/interface-definitions/include/interface/interface-mac.xml.i index e277de85c..d7107ad23 100644 --- a/interface-definitions/include/interface-mac.xml.i +++ b/interface-definitions/include/interface/interface-mac.xml.i @@ -1,9 +1,9 @@ -<!-- included start from mac.xml.i --> +<!-- include start from interface/interface-mac.xml.i --> <leafNode name="mac"> <properties> <help>Media Access Control (MAC) address</help> <valueHelp> - <format>h:h:h:h:h:h</format> + <format>macaddr</format> <description>Hardware (MAC) address</description> </valueHelp> <constraint> @@ -11,4 +11,4 @@ </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-mirror.xml.i b/interface-definitions/include/interface/interface-mirror.xml.i index d34132a9c..b3b45fb43 100644 --- a/interface-definitions/include/interface-mirror.xml.i +++ b/interface-definitions/include/interface/interface-mirror.xml.i @@ -1,4 +1,4 @@ -<!-- included start from interface-mirror.xml.i --> +<!-- include start from interface/interface-mirror.xml.i --> <node name="mirror"> <properties> <help>Incoming/outgoing packet mirroring destination</help> @@ -22,4 +22,4 @@ </leafNode> </children> </node> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-mtu-1200-16000.xml.i b/interface-definitions/include/interface/interface-mtu-1200-16000.xml.i index 04b5ec8ac..3241ba912 100644 --- a/interface-definitions/include/interface-mtu-1200-16000.xml.i +++ b/interface-definitions/include/interface/interface-mtu-1200-16000.xml.i @@ -1,4 +1,4 @@ -<!-- included start from interface-mtu-1200-16000.xml.i --> +<!-- include start from interface/interface-mtu-1200-16000.xml.i --> <leafNode name="mtu"> <properties> <help>Maximum Transmission Unit (MTU)</help> @@ -13,4 +13,4 @@ </properties> <defaultValue>1500</defaultValue> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-mtu-1450-16000.xml.i b/interface-definitions/include/interface/interface-mtu-1450-16000.xml.i index 41dd5fb00..0a35bbbaa 100644 --- a/interface-definitions/include/interface-mtu-1450-16000.xml.i +++ b/interface-definitions/include/interface/interface-mtu-1450-16000.xml.i @@ -1,4 +1,4 @@ -<!-- included start from interface-mtu-1450-16000.xml.i --> +<!-- include start from interface/interface-mtu-1450-16000.xml.i --> <leafNode name="mtu"> <properties> <help>Maximum Transmission Unit (MTU)</help> @@ -13,4 +13,4 @@ </properties> <defaultValue>1500</defaultValue> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-mtu-64-8024.xml.i b/interface-definitions/include/interface/interface-mtu-64-8024.xml.i index 0a455bc64..f75de02ba 100644 --- a/interface-definitions/include/interface-mtu-64-8024.xml.i +++ b/interface-definitions/include/interface/interface-mtu-64-8024.xml.i @@ -1,4 +1,4 @@ -<!-- included start from interface-mtu-68-8024.xml.i --> +<!-- include start from interface/interface-mtu-68-8024.xml.i --> <leafNode name="mtu"> <properties> <help>Maximum Transmission Unit (MTU)</help> @@ -13,4 +13,4 @@ </properties> <defaultValue>1500</defaultValue> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-mtu-68-1500.xml.i b/interface-definitions/include/interface/interface-mtu-68-1500.xml.i index 78c2c6920..9e6fe8760 100644 --- a/interface-definitions/include/interface-mtu-68-1500.xml.i +++ b/interface-definitions/include/interface/interface-mtu-68-1500.xml.i @@ -1,4 +1,4 @@ -<!-- included start from interface-mtu-68-1500.xml.i --> +<!-- include start from interface/interface-mtu-68-1500.xml.i --> <leafNode name="mtu"> <properties> <help>Maximum Transmission Unit (MTU)</help> @@ -13,4 +13,4 @@ </properties> <defaultValue>1500</defaultValue> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-mtu-68-16000.xml.i b/interface-definitions/include/interface/interface-mtu-68-16000.xml.i index 9f18464bf..83af7bbd4 100644 --- a/interface-definitions/include/interface-mtu-68-16000.xml.i +++ b/interface-definitions/include/interface/interface-mtu-68-16000.xml.i @@ -1,4 +1,4 @@ -<!-- included start from interface-mtu-68-16000.xml.i --> +<!-- include start from interface/interface-mtu-68-16000.xml.i --> <leafNode name="mtu"> <properties> <help>Maximum Transmission Unit (MTU)</help> @@ -13,4 +13,4 @@ </properties> <defaultValue>1500</defaultValue> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface/interface-parameters-dont-fragment.xml.i b/interface-definitions/include/interface/interface-parameters-dont-fragment.xml.i new file mode 100644 index 000000000..166c31115 --- /dev/null +++ b/interface-definitions/include/interface/interface-parameters-dont-fragment.xml.i @@ -0,0 +1,8 @@ +<!-- include start from interface/interface-parameters-df.xml.i --> +<leafNode name="dont-fragment"> + <properties> + <help>Specifies the usage of the dont fragment (DF) bit</help> + <valueless/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/interface/interface-parameters-flowlabel.xml.i b/interface-definitions/include/interface/interface-parameters-flowlabel.xml.i new file mode 100644 index 000000000..ed075e40d --- /dev/null +++ b/interface-definitions/include/interface/interface-parameters-flowlabel.xml.i @@ -0,0 +1,15 @@ +<!-- include start from interface/interface-parameters-flowlabel.xml.i --> +<leafNode name="flowlabel"> + <properties> + <help>Specifies the flow label to use in outgoing packets</help> + <valueHelp> + <format>0x0-0x0FFFFF</format> + <description>Tunnel key, 'inherit' or hex value</description> + </valueHelp> + <constraint> + <regex>^((0x){0,1}(0?[0-9A-Fa-f]{1,5})|inherit)$</regex> + </constraint> + <constraintErrorMessage>Must be 'inherit' or a number</constraintErrorMessage> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/interface/interface-parameters-key.xml.i b/interface-definitions/include/interface/interface-parameters-key.xml.i new file mode 100644 index 000000000..1b1d67174 --- /dev/null +++ b/interface-definitions/include/interface/interface-parameters-key.xml.i @@ -0,0 +1,15 @@ +<!-- include start from interface/interface-parameters-key.xml.i --> +<leafNode name="key"> + <properties> + <help>Tunnel key</help> + <valueHelp> + <format>u32</format> + <description>Tunnel key</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + </constraint> + <constraintErrorMessage>key must be between 0-4294967295</constraintErrorMessage> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/interface/interface-parameters-tos.xml.i b/interface-definitions/include/interface/interface-parameters-tos.xml.i new file mode 100644 index 000000000..83b4e0671 --- /dev/null +++ b/interface-definitions/include/interface/interface-parameters-tos.xml.i @@ -0,0 +1,16 @@ +<!-- include start from interface/tunnel-parameters-tos.xml.i --> +<leafNode name="tos"> + <properties> + <help>Specifies TOS value to use in outgoing packets</help> + <valueHelp> + <format>0-99</format> + <description>Type of Service (TOS)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-99"/> + </constraint> + <constraintErrorMessage>TOS must be between 0 and 99</constraintErrorMessage> + </properties> + <defaultValue>inherit</defaultValue> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/interface/interface-parameters-ttl.xml.i b/interface-definitions/include/interface/interface-parameters-ttl.xml.i new file mode 100644 index 000000000..df193cf24 --- /dev/null +++ b/interface-definitions/include/interface/interface-parameters-ttl.xml.i @@ -0,0 +1,20 @@ +<!-- include start from interface/interface-parameters-ttl.xml.i --> +<leafNode name="ttl"> + <properties> + <help>Specifies TTL value to use in outgoing packets</help> + <valueHelp> + <format>0</format> + <description>Inherit - copy value from original IP header</description> + </valueHelp> + <valueHelp> + <format>1-255</format> + <description>Time to Live</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-255"/> + </constraint> + <constraintErrorMessage>TTL must be between 0 and 255</constraintErrorMessage> + </properties> + <defaultValue>0</defaultValue> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/interface-proxy-arp-pvlan.xml.i b/interface-definitions/include/interface/interface-proxy-arp-pvlan.xml.i index 02b96e353..153dfc072 100644 --- a/interface-definitions/include/interface-proxy-arp-pvlan.xml.i +++ b/interface-definitions/include/interface/interface-proxy-arp-pvlan.xml.i @@ -1,8 +1,8 @@ -<!-- included start from interface-proxy-arp-pvlan.xml.i --> +<!-- include start from interface/interface-proxy-arp-pvlan.xml.i --> <leafNode name="proxy-arp-pvlan"> <properties> <help>Enable private VLAN proxy ARP on this interface</help> <valueless/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-source-validation.xml.i b/interface-definitions/include/interface/interface-source-validation.xml.i index 32cec464e..70914f2e9 100644 --- a/interface-definitions/include/interface-source-validation.xml.i +++ b/interface-definitions/include/interface/interface-source-validation.xml.i @@ -1,4 +1,4 @@ -<!-- included start from interface-source-validation.xml.i --> +<!-- include start from interface/interface-source-validation.xml.i --> <leafNode name="source-validation"> <properties> <help>Source validation by reversed path (RFC3704)</help> @@ -22,4 +22,4 @@ </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-vrf.xml.i b/interface-definitions/include/interface/interface-vrf.xml.i index e3d6b53e0..ef6ca1241 100644 --- a/interface-definitions/include/interface-vrf.xml.i +++ b/interface-definitions/include/interface/interface-vrf.xml.i @@ -1,4 +1,4 @@ -<!-- included start from interface-vrf.xml.i --> +<!-- include start from interface/interface-vrf.xml.i --> <leafNode name="vrf"> <properties> <help>VRF instance name</help> @@ -11,4 +11,4 @@ </completionHelp> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface-xdp.xml.i b/interface-definitions/include/interface/interface-xdp.xml.i index d224c177f..0253f6dad 100644 --- a/interface-definitions/include/interface-xdp.xml.i +++ b/interface-definitions/include/interface/interface-xdp.xml.i @@ -1,8 +1,8 @@ -<!-- included start from interface-vrf.xml.i --> +<!-- include start from interface/interface-xdp.xml.i --> <leafNode name="xdp"> <properties> <help>Enable eXpress Data Path</help> <valueless/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/ipv6-address-autoconf.xml.i b/interface-definitions/include/interface/ipv6-address-autoconf.xml.i index 580f060d7..cd1483bc1 100644 --- a/interface-definitions/include/ipv6-address-autoconf.xml.i +++ b/interface-definitions/include/interface/ipv6-address-autoconf.xml.i @@ -1,8 +1,8 @@ -<!-- included start from ipv6-address-autoconf.xml.i --> +<!-- include start from interface/ipv6-address-autoconf.xml.i --> <leafNode name="autoconf"> <properties> <help>Enable acquisition of IPv6 address using stateless autoconfig (SLAAC)</help> <valueless/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface/ipv6-address-eui64.xml.i b/interface-definitions/include/interface/ipv6-address-eui64.xml.i new file mode 100644 index 000000000..fe1f43df4 --- /dev/null +++ b/interface-definitions/include/interface/ipv6-address-eui64.xml.i @@ -0,0 +1,16 @@ +<!-- include start from interface/ipv6-address-eui64.xml.i --> +<leafNode name="eui64"> + <properties> + <help>Prefix for IPv6 address with MAC-based EUI-64</help> + <valueHelp> + <format><h:h:h:h:h:h:h:h/64></format> + <description>IPv6 /64 network</description> + </valueHelp> + <constraint> + <validator name="ipv6-eui64-prefix"/> + </constraint> + <constraintErrorMessage>EUI64 prefix length must be 64</constraintErrorMessage> + <multi/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/ipv6-address-no-default-link-local.xml.i b/interface-definitions/include/interface/ipv6-address-no-default-link-local.xml.i index 1c9e832dc..012490edc 100644 --- a/interface-definitions/include/ipv6-address-no-default-link-local.xml.i +++ b/interface-definitions/include/interface/ipv6-address-no-default-link-local.xml.i @@ -1,8 +1,8 @@ -<!-- included start from ipv6-address-no-default-link-local.xml.i --> +<!-- include start from interface/ipv6-address-no-default-link-local.xml.i --> <leafNode name="no-default-link-local"> <properties> <help>Remove the default link-local address from the interface</help> <valueless/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface/ipv6-address.xml.i b/interface-definitions/include/interface/ipv6-address.xml.i new file mode 100644 index 000000000..e1bdf02fd --- /dev/null +++ b/interface-definitions/include/interface/ipv6-address.xml.i @@ -0,0 +1,12 @@ +<!-- include start from interface/ipv6-address.xml.i --> +<node name="address"> + <properties> + <help>IPv6 address configuration modes</help> + </properties> + <children> + #include <include/interface/ipv6-address-autoconf.xml.i> + #include <include/interface/ipv6-address-eui64.xml.i> + #include <include/interface/ipv6-address-no-default-link-local.xml.i> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/ipv6-disable-forwarding.xml.i b/interface-definitions/include/interface/ipv6-disable-forwarding.xml.i index 14d9eada9..4adb77d1b 100644 --- a/interface-definitions/include/ipv6-disable-forwarding.xml.i +++ b/interface-definitions/include/interface/ipv6-disable-forwarding.xml.i @@ -1,8 +1,8 @@ -<!-- included start from ipv6-disable-forwarding.xml.i --> +<!-- include start from interface/ipv6-disable-forwarding.xml.i --> <leafNode name="disable-forwarding"> <properties> <help>Disable IPv6 forwarding on this interface</help> <valueless/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/ipv6-dup-addr-detect-transmits.xml.i b/interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i index 61e6669c4..2b5ec0281 100644 --- a/interface-definitions/include/ipv6-dup-addr-detect-transmits.xml.i +++ b/interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i @@ -1,4 +1,4 @@ -<!-- included start from ipv6-dup-addr-detect-transmits.xml.i --> +<!-- include start from interface/ipv6-dup-addr-detect-transmits.xml.i --> <leafNode name="dup-addr-detect-transmits"> <properties> <help>Number of NS messages to send while performing DAD (default: 1)</help> @@ -15,4 +15,4 @@ </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/interface/tunnel-remote.xml.i b/interface-definitions/include/interface/tunnel-remote.xml.i new file mode 100644 index 000000000..1ba9b0382 --- /dev/null +++ b/interface-definitions/include/interface/tunnel-remote.xml.i @@ -0,0 +1,18 @@ +<!-- include start from rip/tunnel-remote.xml.i --> +<leafNode name="remote"> + <properties> + <help>Tunnel remote address</help> + <valueHelp> + <format>ipv4</format> + <description>Tunnel remote IPv4 address</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>Tunnel remote IPv6 address</description> + </valueHelp> + <constraint> + <validator name="ip-address"/> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/vif-s.xml.i b/interface-definitions/include/interface/vif-s.xml.i index 6ba7e0b99..045fd3e24 100644 --- a/interface-definitions/include/vif-s.xml.i +++ b/interface-definitions/include/interface/vif-s.xml.i @@ -1,4 +1,4 @@ -<!-- included start from vif-s.xml.i --> +<!-- include start from interface/vif-s.xml.i --> <tagNode name="vif-s"> <properties> <help>QinQ TAG-S Virtual Local Area Network (VLAN) ID</help> @@ -8,12 +8,12 @@ <constraintErrorMessage>VLAN ID must be between 0 and 4094</constraintErrorMessage> </properties> <children> - #include <include/address-ipv4-ipv6-dhcp.xml.i> - #include <include/interface-description.xml.i> - #include <include/dhcp-options.xml.i> - #include <include/dhcpv6-options.xml.i> - #include <include/interface-disable-link-detect.xml.i> - #include <include/interface-disable.xml.i> + #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> + #include <include/interface/interface-description.xml.i> + #include <include/interface/dhcp-options.xml.i> + #include <include/interface/dhcpv6-options.xml.i> + #include <include/interface/interface-disable-link-detect.xml.i> + #include <include/interface/interface-disable.xml.i> <leafNode name="protocol"> <properties> <help>Protocol used for service VLAN (default: 802.1ad)</help> @@ -35,10 +35,10 @@ </properties> <defaultValue>802.1ad</defaultValue> </leafNode> - #include <include/interface-ipv4-options.xml.i> - #include <include/interface-ipv6-options.xml.i> - #include <include/interface-mac.xml.i> - #include <include/interface-mtu-68-16000.xml.i> + #include <include/interface/interface-ipv4-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> + #include <include/interface/interface-mac.xml.i> + #include <include/interface/interface-mtu-68-16000.xml.i> <tagNode name="vif-c"> <properties> <help>QinQ TAG-C Virtual Local Area Network (VLAN) ID</help> @@ -48,19 +48,20 @@ <constraintErrorMessage>VLAN ID must be between 0 and 4094</constraintErrorMessage> </properties> <children> - #include <include/address-ipv4-ipv6-dhcp.xml.i> - #include <include/interface-description.xml.i> - #include <include/dhcp-options.xml.i> - #include <include/dhcpv6-options.xml.i> - #include <include/interface-disable-link-detect.xml.i> - #include <include/interface-disable.xml.i> - #include <include/interface-ipv4-options.xml.i> - #include <include/interface-ipv6-options.xml.i> - #include <include/interface-mac.xml.i> - #include <include/interface-mtu-68-16000.xml.i> - #include <include/interface-vrf.xml.i> + #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> + #include <include/interface/interface-description.xml.i> + #include <include/interface/dhcp-options.xml.i> + #include <include/interface/dhcpv6-options.xml.i> + #include <include/interface/interface-disable-link-detect.xml.i> + #include <include/interface/interface-disable.xml.i> + #include <include/interface/interface-ipv4-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> + #include <include/interface/interface-mac.xml.i> + #include <include/interface/interface-mtu-68-16000.xml.i> + #include <include/interface/interface-vrf.xml.i> </children> </tagNode> + #include <include/interface/interface-vrf.xml.i> </children> </tagNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/vif.xml.i b/interface-definitions/include/interface/vif.xml.i index bc60dd04d..0355054a4 100644 --- a/interface-definitions/include/vif.xml.i +++ b/interface-definitions/include/interface/vif.xml.i @@ -1,4 +1,4 @@ -<!-- included start from vif.xml.i --> +<!-- include start from interface/vif.xml.i --> <tagNode name="vif"> <properties> <help>Virtual Local Area Network (VLAN) ID</help> @@ -12,13 +12,13 @@ <constraintErrorMessage>VLAN ID must be between 0 and 4094</constraintErrorMessage> </properties> <children> - #include <include/address-ipv4-ipv6-dhcp.xml.i> - #include <include/interface-description.xml.i> - #include <include/dhcp-options.xml.i> - #include <include/dhcpv6-options.xml.i> - #include <include/interface-disable-link-detect.xml.i> - #include <include/interface-disable.xml.i> - #include <include/interface-vrf.xml.i> + #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> + #include <include/interface/interface-description.xml.i> + #include <include/interface/dhcp-options.xml.i> + #include <include/interface/dhcpv6-options.xml.i> + #include <include/interface/interface-disable-link-detect.xml.i> + #include <include/interface/interface-disable.xml.i> + #include <include/interface/interface-vrf.xml.i> <leafNode name="egress-qos"> <properties> <help>VLAN egress QoS</help> @@ -43,10 +43,10 @@ <constraintErrorMessage>QoS mapping should be in the format of '0:7 2:3' with numbers 0-9</constraintErrorMessage> </properties> </leafNode> - #include <include/interface-ipv4-options.xml.i> - #include <include/interface-ipv6-options.xml.i> - #include <include/interface-mac.xml.i> - #include <include/interface-mtu-68-16000.xml.i> + #include <include/interface/interface-ipv4-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> + #include <include/interface/interface-mac.xml.i> + #include <include/interface/interface-mtu-68-16000.xml.i> </children> </tagNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/ipv6-address-eui64.xml.i b/interface-definitions/include/ipv6-address-eui64.xml.i deleted file mode 100644 index 093a1dc79..000000000 --- a/interface-definitions/include/ipv6-address-eui64.xml.i +++ /dev/null @@ -1,15 +0,0 @@ -<!-- included start from ipv6-address-eui64.xml.i --> -<leafNode name="eui64"> - <properties> - <help>Prefix for IPv6 address with MAC-based EUI-64</help> - <valueHelp> - <format>ipv6net</format> - <description>IPv6 network and prefix length</description> - </valueHelp> - <constraint> - <validator name="ipv6-prefix"/> - </constraint> - <multi/> - </properties> -</leafNode> -<!-- included end --> diff --git a/interface-definitions/include/ipv6-address.xml.i b/interface-definitions/include/ipv6-address.xml.i deleted file mode 100644 index 276456248..000000000 --- a/interface-definitions/include/ipv6-address.xml.i +++ /dev/null @@ -1,12 +0,0 @@ -<!-- included start from ipv6-address.xml.i --> -<node name="address"> - <properties> - <help>IPv6 address configuration modes</help> - </properties> - <children> - #include <include/ipv6-address-autoconf.xml.i> - #include <include/ipv6-address-eui64.xml.i> - #include <include/ipv6-address-no-default-link-local.xml.i> - </children> -</node> -<!-- included end --> diff --git a/interface-definitions/include/isis/isis-common-config.xml.i b/interface-definitions/include/isis/isis-common-config.xml.i new file mode 100644 index 000000000..8b753b082 --- /dev/null +++ b/interface-definitions/include/isis/isis-common-config.xml.i @@ -0,0 +1,755 @@ +<!-- include start from isis/isis-common-config.xml.i --> +<node name="area-password"> + <properties> + <help>Configure the authentication password for an area</help> + </properties> + <children> + <leafNode name="plaintext-password"> + <properties> + <help>Plain-text authentication type</help> + <valueHelp> + <format>txt</format> + <description>Level-wide password</description> + </valueHelp> + </properties> + </leafNode> + <leafNode name="md5"> + <properties> + <help>MD5 authentication type</help> + <valueHelp> + <format>txt</format> + <description>Level-wide password</description> + </valueHelp> + </properties> + </leafNode> + </children> +</node> +<node name="default-information"> + <properties> + <help>Control distribution of default information</help> + </properties> + <children> + <node name="originate"> + <properties> + <help>Distribute a default route</help> + </properties> + <children> + <node name="ipv4"> + <properties> + <help>Distribute default route for IPv4</help> + </properties> + <children> + <leafNode name="level-1"> + <properties> + <help>Distribute default route into level-1</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="level-2"> + <properties> + <help>Distribute default route into level-2</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + <node name="ipv6"> + <properties> + <help>Distribute default route for IPv6</help> + </properties> + <children> + <leafNode name="level-1"> + <properties> + <help>Distribute default route into level-1</help> + <completionHelp> + <list>always</list> + </completionHelp> + <valueHelp> + <format>always</format> + <description>Always advertise default route</description> + </valueHelp> + </properties> + </leafNode> + <leafNode name="level-2"> + <properties> + <help>Distribute default route into level-2</help> + <completionHelp> + <list>always</list> + </completionHelp> + <valueHelp> + <format>always</format> + <description>Always advertise default route</description> + </valueHelp> + </properties> + </leafNode> + </children> + </node> + </children> + </node> + </children> +</node> +<node name="domain-password"> + <properties> + <help>Set the authentication password for a routing domain</help> + </properties> + <children> + <leafNode name="plaintext-password"> + <properties> + <help>Plain-text authentication type</help> + <valueHelp> + <format>txt</format> + <description>Level-wide password</description> + </valueHelp> + </properties> + </leafNode> +<!-- + <leafNode name="md5"> + <properties> + <help>MD5 authentication type</help> + <valueHelp> + <format>txt</format> + <description>Level-wide password</description> + </valueHelp> + </properties> + </leafNode> +--> + </children> +</node> +<leafNode name="dynamic-hostname"> + <properties> + <help>Dynamic hostname for IS-IS</help> + <valueless/> + </properties> +</leafNode> +<leafNode name="level"> + <properties> + <help>IS-IS level number</help> + <completionHelp> + <list>level-1 level-1-2 level-2</list> + </completionHelp> + <valueHelp> + <format>level-1</format> + <description>Act as a station router</description> + </valueHelp> + <valueHelp> + <format>level-1-2</format> + <description>Act as both a station and an area router</description> + </valueHelp> + <valueHelp> + <format>level-2</format> + <description>Act as an area router</description> + </valueHelp> + <constraint> + <regex>(level-1|level-1-2|level-2)</regex> + </constraint> + </properties> +</leafNode> +<leafNode name="lsp-gen-interval"> + <properties> + <help>Minimum interval between regenerating same LSP</help> + <valueHelp> + <format>u32:1-120</format> + <description>Minimum interval in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-120"/> + </constraint> + </properties> +</leafNode> +<leafNode name="lsp-mtu"> + <properties> + <help>Configure the maximum size of generated LSPs</help> + <valueHelp> + <format>u32:128-4352</format> + <description>Maximum size of generated LSPs</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 128-4352"/> + </constraint> + </properties> +</leafNode> +<leafNode name="lsp-refresh-interval"> + <properties> + <help>LSP refresh interval</help> + <valueHelp> + <format>u32:1-65235</format> + <description>LSP refresh interval in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65235"/> + </constraint> + </properties> +</leafNode> +<leafNode name="max-lsp-lifetime"> + <properties> + <help>Maximum LSP lifetime</help> + <valueHelp> + <format>u32:350-65535</format> + <description>LSP lifetime in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> +</leafNode> +<leafNode name="metric-style"> + <properties> + <help>Use old-style (ISO 10589) or new-style packet formats</help> + <completionHelp> + <list>narrow transition wide</list> + </completionHelp> + <valueHelp> + <format>narrow</format> + <description>Use old style of TLVs with narrow metric</description> + </valueHelp> + <valueHelp> + <format>transition</format> + <description>Send and accept both styles of TLVs during transition</description> + </valueHelp> + <valueHelp> + <format>wide</format> + <description>Use new style of TLVs to carry wider metric</description> + </valueHelp> + <constraint> + <regex>(narrow|transition|wide)</regex> + </constraint> + </properties> +</leafNode> +<leafNode name="net"> + <properties> + <help>A Network Entity Title for this process (ISO only)</help> + <valueHelp> + <format>XX.XXXX. ... .XXX.XX</format> + <description>Network entity title (NET)</description> + </valueHelp> + <constraint> + <regex>[a-fA-F0-9]{2}(\.[a-fA-F0-9]{4}){3,9}\.[a-fA-F0-9]{2}</regex> + </constraint> + </properties> +</leafNode> +<leafNode name="purge-originator"> + <properties> + <help>Use the RFC 6232 purge-originator</help> + <valueless/> + </properties> +</leafNode> +<node name="traffic-engineering"> + <properties> + <help>Show IS-IS neighbor adjacencies</help> + </properties> + <children> + <leafNode name="enable"> + <properties> + <help>Enable MPLS traffic engineering extensions</help> + <valueless/> + </properties> + </leafNode> +<!-- + <node name="inter-as"> + <properties> + <help>MPLS traffic engineering inter-AS support</help> + </properties> + <children> + <leafNode name="level-1"> + <properties> + <help>Area native mode self originate inter-AS LSP with L1 only flooding scope</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="level-1-2"> + <properties> + <help>Area native mode self originate inter-AS LSP with L1 and L2 flooding scope</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="level-2"> + <properties> + <help>Area native mode self originate inter-AS LSP with L2 only flooding scope</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + <leafNode name="inter-as"> + <properties> + <help>MPLS traffic engineering inter-AS support</help> + <valueless/> + </properties> + </leafNode> +--> + <leafNode name="address"> + <properties> + <help>MPLS traffic engineering router ID</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + </leafNode> + </children> +</node> +<node name="segment-routing"> + <properties> + <help>Segment-Routing (SPRING) settings</help> + </properties> + <children> + <leafNode name="enable"> + <properties> + <help>Enable segment-routing functionality</help> + <valueless/> + </properties> + </leafNode> + <node name="global-block"> + <properties> + <help>Global block label range</help> + </properties> + <children> + <leafNode name="low-label-value"> + <properties> + <help>The lower bound of the global block</help> + <valueHelp> + <format>u32:16-1048575</format> + <description>MPLS label value</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 16-1048575"/> + </constraint> + </properties> + </leafNode> + <leafNode name="high-label-value"> + <properties> + <help>The upper bound of the global block</help> + <valueHelp> + <format>u32:16-1048575</format> + <description>MPLS label value</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 16-1048575"/> + </constraint> + </properties> + </leafNode> + </children> + </node> +<!-- + <node name="local-block"> + <properties> + <help>Local Block label range</help> + </properties> + <children> + <leafNode name="low-label-value"> + <properties> + <help>The lower bound of the local block</help> + <valueHelp> + <format>u32:16-1048575</format> + <description>MPLS label value</description> + </valueHelp> + <constraint> + <validator name="numeric" argument=" range 16-1048575"/> + </constraint> + </properties> + </leafNode> + <leafNode name="high-label-value"> + <properties> + <help>The upper bound of the local block</help> + <valueHelp> + <format>u32:16-1048575</format> + <description>MPLS label value</description> + </valueHelp> + <constraint> + <validator name="numeric" argument=" range 16-1048575"/> + </constraint> + </properties> + </leafNode> + </children> + </node> +--> + <leafNode name="maximum-label-depth"> + <properties> + <help>Maximum MPLS labels allowed for this router</help> + <valueHelp> + <format>u32:1-16</format> + <description>MPLS label depth</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-16"/> + </constraint> + </properties> + </leafNode> + <tagNode name="prefix"> + <properties> + <help>Static IPv4/IPv6 prefix segment/label mapping</help> + <valueHelp> + <format>ipv4net</format> + <description>IPv4 prefix segment</description> + </valueHelp> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 prefix segment</description> + </valueHelp> + <constraint> + <validator name="ipv4-prefix"/> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + <children> + <node name="absolute"> + <properties> + <help>Specify the absolute value of prefix segment/label ID</help> + </properties> + <children> + <leafNode name="value"> + <properties> + <help>Specify the absolute value of prefix segment/label ID</help> + <valueHelp> + <format>u32:16-1048575</format> + <description>The absolute segment/label ID value</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 16-1048575"/> + </constraint> + </properties> + </leafNode> + <leafNode name="explicit-null"> + <properties> + <help>Request upstream neighbor to replace segment/label with explicit null label</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="no-php-flag"> + <properties> + <help>Do not request penultimate hop popping for segment/label</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + <node name="index"> + <properties> + <help>Specify the index value of prefix segment/label ID</help> + </properties> + <children> + <leafNode name="value"> + <properties> + <help>Specify the index value of prefix segment/label ID</help> + <valueHelp> + <format>u32:0-65535</format> + <description>The index segment/label ID value</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-65535"/> + </constraint> + </properties> + </leafNode> + <leafNode name="explicit-null"> + <properties> + <help>Request upstream neighbor to replace segment/label with explicit null label</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="no-php-flag"> + <properties> + <help>Do not request penultimate hop popping for segment/label</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + </children> + </tagNode> + </children> +</node> +<node name="redistribute"> + <properties> + <help>Redistribute information from another routing protocol</help> + </properties> + <children> + <node name="ipv4"> + <properties> + <help>Redistribute IPv4 routes</help> + </properties> + <children> + <node name="bgp"> + <properties> + <help>Border Gateway Protocol (BGP)</help> + </properties> + <children> + #include <include/isis/isis-redistribute-ipv4.xml.i> + </children> + </node> + <node name="connected"> + <properties> + <help>Redistribute connected routes into IS-IS</help> + </properties> + <children> + #include <include/isis/isis-redistribute-ipv4.xml.i> + </children> + </node> + <node name="kernel"> + <properties> + <help>Redistribute kernel routes into IS-IS</help> + </properties> + <children> + #include <include/isis/isis-redistribute-ipv4.xml.i> + </children> + </node> + <node name="ospf"> + <properties> + <help>Redistribute OSPF routes into IS-IS</help> + </properties> + <children> + #include <include/isis/isis-redistribute-ipv4.xml.i> + </children> + </node> + <node name="rip"> + <properties> + <help>Redistribute RIP routes into IS-IS</help> + </properties> + <children> + #include <include/isis/isis-redistribute-ipv4.xml.i> + </children> + </node> + <node name="static"> + <properties> + <help>Redistribute static routes into IS-IS</help> + </properties> + <children> + #include <include/isis/isis-redistribute-ipv4.xml.i> + </children> + </node> + </children> + </node> + </children> +</node> +<leafNode name="set-attached-bit"> + <properties> + <help>Set attached bit to identify as L1/L2 router for inter-area traffic</help> + <valueless/> + </properties> +</leafNode> +<leafNode name="set-overload-bit"> + <properties> + <help>Set overload bit to avoid any transit traffic</help> + <valueless/> + </properties> +</leafNode> +<node name="spf-delay-ietf"> + <properties> + <help>IETF SPF delay algorithm</help> + </properties> + <children> + <leafNode name="init-delay"> + <properties> + <help>Delay used while in QUIET state</help> + <valueHelp> + <format>u32:0-60000</format> + <description>Delay used while in QUIET state (in ms)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-60000"/> + </constraint> + </properties> + </leafNode> + <leafNode name="short-delay"> + <properties> + <help>Delay used while in SHORT_WAIT state</help> + <valueHelp> + <format>u32:0-60000</format> + <description>Delay used while in SHORT_WAIT state (in ms)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-60000"/> + </constraint> + </properties> + </leafNode> + <leafNode name="long-delay"> + <properties> + <help>Delay used while in LONG_WAIT</help> + <valueHelp> + <format>u32:0-60000</format> + <description>Delay used while in LONG_WAIT state (in ms)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-60000"/> + </constraint> + </properties> + </leafNode> + <leafNode name="holddown"> + <properties> + <help>Time with no received IGP events before considering IGP stable</help> + <valueHelp> + <format>u32:0-60000</format> + <description>Time with no received IGP events before considering IGP stable (in ms)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-60000"/> + </constraint> + </properties> + </leafNode> + <leafNode name="time-to-learn"> + <properties> + <help>Maximum duration needed to learn all the events related to a single failure</help> + <valueHelp> + <format>u32:0-60000</format> + <description>Maximum duration needed to learn all the events related to a single failure (in ms)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-60000"/> + </constraint> + </properties> + </leafNode> + </children> +</node> +<leafNode name="spf-interval"> + <properties> + <help>Minimum interval between SPF calculations</help> + <valueHelp> + <format>u32:1-120</format> + <description>Minimum interval between consecutive SPFs in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-120"/> + </constraint> + </properties> +</leafNode> +<tagNode name="interface"> + <!-- (config-if)# ip router isis WORD (same as name of IS-IS process) + if any section of "interface" pesent --> + <properties> + <help>Interface params</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + </properties> + <children> + #include <include/bfd.xml.i> + <leafNode name="circuit-type"> + <properties> + <help>Configure circuit type for interface</help> + <completionHelp> + <list>level-1 level-1-2 level-2-only</list> + </completionHelp> + <valueHelp> + <format>level-1</format> + <description>Level-1 only adjacencies are formed</description> + </valueHelp> + <valueHelp> + <format>level-1-2</format> + <description>Level-1-2 adjacencies are formed</description> + </valueHelp> + <valueHelp> + <format>level-2-only</format> + <description>Level-2 only adjacencies are formed</description> + </valueHelp> + <constraint> + <regex>(level-1|level-1-2|level-2-only)</regex> + </constraint> + </properties> + </leafNode> + <leafNode name="hello-padding"> + <properties> + <help>Add padding to IS-IS hello packets</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="hello-interval"> + <properties> + <help>Set Hello interval</help> + <valueHelp> + <format>u32:1-600</format> + <description>Set Hello interval</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-600"/> + </constraint> + </properties> + </leafNode> + <leafNode name="hello-multiplier"> + <properties> + <help>Set Hello interval</help> + <valueHelp> + <format>u32:2-100</format> + <description>Set multiplier for Hello holding time</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 2-100"/> + </constraint> + </properties> + </leafNode> + <leafNode name="metric"> + <properties> + <help>Set default metric for circuit</help> + <valueHelp> + <format>u32:0-16777215</format> + <description>Default metric value</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-16777215"/> + </constraint> + </properties> + </leafNode> + <node name="network"> + <properties> + <help>Set network type</help> + </properties> + <children> + <leafNode name="point-to-point"> + <properties> + <help>point-to-point network type</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + #include <include/isis/passive.xml.i> + <node name="password"> + <properties> + <help>Configure the authentication password for a circuit</help> + </properties> + <children> + <leafNode name="plaintext-password"> + <properties> + <help>Plain-text authentication type</help> + <valueHelp> + <format>txt</format> + <description>Circuit password</description> + </valueHelp> + </properties> + </leafNode> + </children> + </node> + <leafNode name="priority"> + <properties> + <help>Set priority for Designated Router election</help> + <valueHelp> + <format>u32:0-127</format> + <description>Priority value</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-127"/> + </constraint> + </properties> + </leafNode> + <leafNode name="psnp-interval"> + <properties> + <help>Set PSNP interval in seconds</help> + <valueHelp> + <format>u32:0-127</format> + <description>Priority value</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-127"/> + </constraint> + </properties> + </leafNode> + <leafNode name="no-three-way-handshake"> + <properties> + <help>Disable three-way handshake</help> + <valueless/> + </properties> + </leafNode> + </children> +</tagNode> +<!-- include end --> diff --git a/interface-definitions/include/isis-redistribute-ipv4.xml.i b/interface-definitions/include/isis/isis-redistribute-ipv4.xml.i index 97ab64250..df48b4d28 100644 --- a/interface-definitions/include/isis-redistribute-ipv4.xml.i +++ b/interface-definitions/include/isis/isis-redistribute-ipv4.xml.i @@ -1,4 +1,4 @@ -<!-- included start from isis-redistribute-ipv4.xml.i --> +<!-- include start from isis/isis-redistribute-ipv4.xml.i --> <node name="level-1"> <properties> <help>Redistribute into level-1</help> @@ -53,4 +53,4 @@ </leafNode> </children> </node> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/isis/passive.xml.i b/interface-definitions/include/isis/passive.xml.i new file mode 100644 index 000000000..6d05f8cc7 --- /dev/null +++ b/interface-definitions/include/isis/passive.xml.i @@ -0,0 +1,8 @@ +<!-- include start from isis/passive.xml.i --> +<leafNode name="passive"> + <properties> + <help>Configure passive mode for interface</help> + <valueless/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/listen-address-ipv4.xml.i b/interface-definitions/include/listen-address-ipv4.xml.i index 530dbf619..ee52cebe8 100644 --- a/interface-definitions/include/listen-address-ipv4.xml.i +++ b/interface-definitions/include/listen-address-ipv4.xml.i @@ -1,4 +1,4 @@ -<!-- included start from listen-address-ipv4.xml.i --> +<!-- include start from listen-address-ipv4.xml.i --> <leafNode name="listen-address"> <properties> <help>Local IPv4 addresses for service to listen on</help> @@ -15,4 +15,4 @@ </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/listen-address.xml.i b/interface-definitions/include/listen-address.xml.i index 5bfb7eb38..9b86851c7 100644 --- a/interface-definitions/include/listen-address.xml.i +++ b/interface-definitions/include/listen-address.xml.i @@ -1,4 +1,4 @@ -<!-- included start from listen-address.xml.i --> +<!-- include start from listen-address.xml.i --> <leafNode name="listen-address"> <properties> <help>Local IP addresses for service to listen on</help> @@ -20,4 +20,4 @@ </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/nat-address.xml.i b/interface-definitions/include/nat-address.xml.i index 846ef3dec..a6460ac0f 100644 --- a/interface-definitions/include/nat-address.xml.i +++ b/interface-definitions/include/nat-address.xml.i @@ -1,4 +1,4 @@ -<!-- included start from nat-address.xml.i --> +<!-- include start from nat-address.xml.i --> <leafNode name="address"> <properties> <help>IP address, subnet, or range</help> @@ -36,4 +36,4 @@ </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/nat-interface.xml.i b/interface-definitions/include/nat-interface.xml.i index e42003530..68969472f 100644 --- a/interface-definitions/include/nat-interface.xml.i +++ b/interface-definitions/include/nat-interface.xml.i @@ -1,4 +1,4 @@ -<!-- included start from nat-interface.xml.i --> +<!-- include start from nat-interface.xml.i --> <leafNode name="outbound-interface"> <properties> <help>Outbound interface of NAT traffic</help> @@ -8,4 +8,4 @@ </completionHelp> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/nat-port.xml.i b/interface-definitions/include/nat-port.xml.i index 6465c00e9..ebba43712 100644 --- a/interface-definitions/include/nat-port.xml.i +++ b/interface-definitions/include/nat-port.xml.i @@ -1,4 +1,4 @@ -<!-- included start from nat-port.xml.i --> +<!-- include start from nat-port.xml.i --> <leafNode name="port"> <properties> <help>Port number</help> @@ -16,4 +16,4 @@ </valueHelp> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/nat-rule.xml.i b/interface-definitions/include/nat-rule.xml.i index e034ef4dd..579d19bdd 100644 --- a/interface-definitions/include/nat-rule.xml.i +++ b/interface-definitions/include/nat-rule.xml.i @@ -1,4 +1,4 @@ -<!-- included start from nat-rule.xml.i --> +<!-- include start from nat-rule.xml.i --> <tagNode name="rule"> <properties> <help>Rule number for NAT</help> @@ -26,12 +26,7 @@ #include <include/nat-port.xml.i> </children> </node> - <leafNode name="disable"> - <properties> - <help>Disable NAT rule</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="exclude"> <properties> <help>Exclude packets matching this rule from NAT</help> @@ -303,4 +298,4 @@ </node> </children> </tagNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/nat-translation-port.xml.i b/interface-definitions/include/nat-translation-port.xml.i index a3e05316f..6e507353c 100644 --- a/interface-definitions/include/nat-translation-port.xml.i +++ b/interface-definitions/include/nat-translation-port.xml.i @@ -1,4 +1,4 @@ -<!-- included start from nat-translation-port.xml.i --> +<!-- include start from nat-translation-port.xml.i --> <leafNode name="port"> <properties> <help>Port number</help> @@ -12,4 +12,4 @@ </valueHelp> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/ospf/ospf-authentication.xml.i b/interface-definitions/include/ospf/ospf-authentication.xml.i new file mode 100644 index 000000000..322c002e4 --- /dev/null +++ b/interface-definitions/include/ospf/ospf-authentication.xml.i @@ -0,0 +1,56 @@ +<!-- include start from ospf/ospf-authentication.xml.i --> +<node name="authentication"> + <properties> + <help>Authentication</help> + </properties> + <children> + <node name="md5"> + <properties> + <help>MD5 key id</help> + </properties> + <children> + <tagNode name="key-id"> + <properties> + <help>MD5 key id</help> + <valueHelp> + <format>u32:1-255</format> + <description>MD5 key id</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> + <children> + <leafNode name="md5-key"> + <properties> + <help>MD5 authentication type</help> + <valueHelp> + <format>txt</format> + <description>MD5 Key (16 characters or less)</description> + </valueHelp> + <constraint> + <regex>^[^[:space:]]{1,16}$</regex> + </constraint> + <constraintErrorMessage>Password must be 16 characters or less</constraintErrorMessage> + </properties> + </leafNode> + </children> + </tagNode> + </children> + </node> + <leafNode name="plaintext-password"> + <properties> + <help>Plain text password</help> + <valueHelp> + <format>txt</format> + <description>Plain text password (8 characters or less)</description> + </valueHelp> + <constraint> + <regex>^[^[:space:]]{1,8}$</regex> + </constraint> + <constraintErrorMessage>Password must be 8 characters or less</constraintErrorMessage> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/ospf/ospf-common-config.xml.i b/interface-definitions/include/ospf/ospf-common-config.xml.i new file mode 100644 index 000000000..7316af670 --- /dev/null +++ b/interface-definitions/include/ospf/ospf-common-config.xml.i @@ -0,0 +1,761 @@ +<!-- include start from ospf/ospf-common-config.xml.i --> +<tagNode name="access-list"> + <properties> + <help>Access list to filter networks in routing updates</help> + <completionHelp> + <path>policy access-list</path> + </completionHelp> + <valueHelp> + <format>u32</format> + <description>Access-list number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + </constraint> + </properties> + <children> + <leafNode name="export"> + <properties> + <help>Filter for outgoing routing update [REQUIRED]</help> + <completionHelp> + <list>bgp connected kernel rip static</list> + </completionHelp> + <valueHelp> + <format>bgp</format> + <description>Filter BGP routes</description> + </valueHelp> + <valueHelp> + <format>connected</format> + <description>Filter connected routes</description> + </valueHelp> + <valueHelp> + <format>isis</format> + <description>Filter IS-IS routes</description> + </valueHelp> + <valueHelp> + <format>kernel</format> + <description>Filter Kernel routes</description> + </valueHelp> + <valueHelp> + <format>rip</format> + <description>Filter RIP routes</description> + </valueHelp> + <valueHelp> + <format>static</format> + <description>Filter static routes</description> + </valueHelp> + <constraint> + <regex>^(bgp|connected|isis|kernel|rip|static)$</regex> + </constraint> + <constraintErrorMessage>Must be bgp, connected, kernel, rip, or static</constraintErrorMessage> + <multi/> + </properties> + </leafNode> + </children> +</tagNode> +<tagNode name="area"> + <properties> + <help>OSPF Area</help> + <valueHelp> + <format>u32</format> + <description>OSPF area in decimal notation</description> + </valueHelp> + <valueHelp> + <format>ipv4</format> + <description>OSPF area in dotted decimal notation</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + <validator name="ip-address"/> + </constraint> + </properties> + <children> + <node name="area-type"> + <properties> + <help>Area type</help> + </properties> + <children> + <leafNode name="normal"> + <properties> + <help>Normal OSPF area</help> + <valueless/> + </properties> + </leafNode> + <node name="nssa"> + <properties> + <help>Nssa OSPF area</help> + </properties> + <children> + <leafNode name="default-cost"> + <properties> + <help>Summary-default cost of nssa area</help> + <valueHelp> + <format>u32:0-16777215</format> + <description>Summary default cost</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-16777215"/> + </constraint> + </properties> + </leafNode> + <leafNode name="no-summary"> + <properties> + <help>Do not inject inter-area routes into stub</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="translate"> + <properties> + <help>Configure NSSA-ABR (default: candidate)</help> + <completionHelp> + <list>always candidate never</list> + </completionHelp> + <valueHelp> + <format>always</format> + <description>NSSA-ABR to always translate</description> + </valueHelp> + <valueHelp> + <format>candidate</format> + <description>NSSA-ABR for translate election (default)</description> + </valueHelp> + <valueHelp> + <format>never</format> + <description>NSSA-ABR to never translate</description> + </valueHelp> + <constraint> + <regex>^(always|candidate|never)$</regex> + </constraint> + </properties> + <defaultValue>candidate</defaultValue> + </leafNode> + </children> + </node> + <node name="stub"> + <properties> + <help>Stub OSPF area</help> + </properties> + <children> + <leafNode name="default-cost"> + <properties> + <help>Summary-default cost of nssa area</help> + <valueHelp> + <format>u32:0-16777215</format> + <description>Summary default cost</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-16777215"/> + </constraint> + </properties> + </leafNode> + <leafNode name="no-summary"> + <properties> + <help>Do not inject inter-area routes into stub</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + </children> + </node> + <leafNode name="authentication"> + <properties> + <help>OSPF area authentication type</help> + <completionHelp> + <list>plaintext-password md5</list> + </completionHelp> + <valueHelp> + <format>plaintext-password</format> + <description>Use plain-text authentication</description> + </valueHelp> + <valueHelp> + <format>md5</format> + <description>Use md5 authentication</description> + </valueHelp> + <constraint> + <regex>^(plaintext-password|md5)$</regex> + </constraint> + </properties> + </leafNode> + <leafNode name="network"> + <properties> + <help>OSPF network [REQUIRED]</help> + <valueHelp> + <format>ipv4net</format> + <description>OSPF network [REQUIRED]</description> + </valueHelp> + <constraint> + <validator name="ipv4-prefix"/> + </constraint> + <multi/> + </properties> + </leafNode> + <tagNode name="range"> + <properties> + <help>Summarize routes matching prefix (border routers only)</help> + <valueHelp> + <format>ipv4net</format> + <description>Area range prefix</description> + </valueHelp> + <constraint> + <validator name="ipv4-prefix"/> + </constraint> + </properties> + <children> + <leafNode name="cost"> + <properties> + <help>Metric for this range</help> + <valueHelp> + <format>u32:0-16777215</format> + <description>Metric for this range</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-16777215"/> + </constraint> + </properties> + </leafNode> + <leafNode name="not-advertise"> + <properties> + <help>Do not advertise this range</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="substitute"> + <properties> + <help>Announce area range as another prefix</help> + <valueHelp> + <format>ipv4net</format> + <description>Announce area range as another prefix</description> + </valueHelp> + <constraint> + <validator name="ipv4-prefix"/> + </constraint> + </properties> + </leafNode> + </children> + </tagNode> + <leafNode name="shortcut"> + <properties> + <help>Area shortcut mode</help> + <completionHelp> + <list>default disable enable</list> + </completionHelp> + <valueHelp> + <format>default</format> + <description>Set default</description> + </valueHelp> + <valueHelp> + <format>disable</format> + <description>Disable shortcutting mode</description> + </valueHelp> + <valueHelp> + <format>enable</format> + <description>Enable shortcutting mode</description> + </valueHelp> + <constraint> + <regex>^(default|disable|enable)$</regex> + </constraint> + </properties> + </leafNode> + <tagNode name="virtual-link"> + <properties> + <help>Virtual link</help> + <valueHelp> + <format>ipv4</format> + <description>OSPF area in dotted decimal notation</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + <validator name="ip-address"/> + </constraint> + </properties> + <children> + #include <include/ospf/ospf-authentication.xml.i> + #include <include/ospf/ospf-intervals.xml.i> + </children> + </tagNode> + </children> +</tagNode> +<node name="auto-cost"> + <properties> + <help>Calculate OSPF interface cost according to bandwidth (default: 100)</help> + </properties> + <children> + <leafNode name="reference-bandwidth"> + <properties> + <help>Reference bandwidth method to assign OSPF cost</help> + <valueHelp> + <format>u32:1-4294967</format> + <description>Reference bandwidth cost in Mbits/sec</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967"/> + </constraint> + </properties> + <defaultValue>100</defaultValue> + </leafNode> + </children> +</node> +<node name="default-information"> + <properties> + <help>Control distribution of default information</help> + </properties> + <children> + <node name="originate"> + <properties> + <help>Distribute a default route</help> + </properties> + <children> + <leafNode name="always"> + <properties> + <help>Always advertise default route</help> + <valueless/> + </properties> + </leafNode> + #include <include/ospf/ospf-metric.xml.i> + #include <include/ospf/ospf-metric-type.xml.i> + #include <include/route-map.xml.i> + </children> + </node> + </children> +</node> +<leafNode name="default-metric"> + <properties> + <help>Metric of redistributed routes</help> + <valueHelp> + <format>u32:0-16777214</format> + <description>Metric of redistributed routes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-16777214"/> + </constraint> + </properties> +</leafNode> +<node name="distance"> + <properties> + <help>Administrative distance</help> + </properties> + <children> + #include <include/ospf/ospf-distance-global.xml.i> + <node name="ospf"> + <properties> + <help>OSPF administrative distance</help> + </properties> + <children> + #include <include/ospf/ospf-distance-per-protocol.xml.i> + </children> + </node> + </children> +</node> +<tagNode name="interface"> + <properties> + <help>Interface related configuration</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface name</description> + </valueHelp> + <constraint> + <validator name="interface-name"/> + </constraint> + </properties> + <children> + #include <include/ospf/ospf-authentication.xml.i> + #include <include/ospf/ospf-intervals.xml.i> + #include <include/ospf/ospf-interface-common.xml.i> + <leafNode name="bandwidth"> + <properties> + <help>Bandwidth of interface (Megabit/sec)</help> + <valueHelp> + <format>u32:1-100000</format> + <description>Bandwidth in Megabit/sec (for calculating OSPF cost)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-100000"/> + </constraint> + </properties> + </leafNode> + <leafNode name="hello-multiplier"> + <properties> + <help>Hello multiplier factor</help> + <valueHelp> + <format>u32:1-10</format> + <description>Number of Hellos to send each second</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-10"/> + </constraint> + </properties> + </leafNode> + <leafNode name="network"> + <properties> + <help>Network type</help> + <completionHelp> + <list>broadcast non-broadcast point-to-multipoint point-to-point</list> + </completionHelp> + <valueHelp> + <format>broadcast</format> + <description>Broadcast network type</description> + </valueHelp> + <valueHelp> + <format>non-broadcast</format> + <description>Non-broadcast network type</description> + </valueHelp> + <valueHelp> + <format>point-to-multipoint</format> + <description>Point-to-multipoint network type</description> + </valueHelp> + <valueHelp> + <format>point-to-point</format> + <description>Point-to-point network type</description> + </valueHelp> + <constraint> + <regex>^(broadcast|non-broadcast|point-to-multipoint|point-to-point)$</regex> + </constraint> + <constraintErrorMessage>Must be broadcast, non-broadcast, point-to-multipoint or point-to-point</constraintErrorMessage> + </properties> + </leafNode> + </children> +</tagNode> +<node name="log-adjacency-changes"> + <properties> + <help>Log changes in adjacency state</help> + </properties> + <children> + <leafNode name="detail"> + <properties> + <help>Log all state changes</help> + <valueless/> + </properties> + </leafNode> + </children> +</node> +<node name="max-metric"> + <properties> + <help>OSPF maximum and infinite-distance metric</help> + </properties> + <children> + <node name="router-lsa"> + <properties> + <help>Advertise own Router-LSA with infinite distance (stub router)</help> + </properties> + <children> + <leafNode name="administrative"> + <properties> + <help>Administratively apply, for an indefinite period</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="on-shutdown"> + <properties> + <help>Advertise stub-router prior to full shutdown of OSPF</help> + <valueHelp> + <format>u32:5-100</format> + <description>Time (seconds) to advertise self as stub-router</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 5-100"/> + </constraint> + </properties> + </leafNode> + <leafNode name="on-startup"> + <properties> + <help>Automatically advertise stub Router-LSA on startup of OSPF</help> + <valueHelp> + <format>u32:5-86400</format> + <description>Time (seconds) to advertise self as stub-router</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 5-86400"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + </children> +</node> +<node name="mpls-te"> + <properties> + <help>MultiProtocol Label Switching-Traffic Engineering (MPLS-TE) parameters</help> + </properties> + <children> + <leafNode name="enable"> + <properties> + <help>Enable MPLS-TE functionality</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="router-address"> + <properties> + <help>Stable IP address of the advertising router</help> + <valueHelp> + <format>ipv4</format> + <description>Stable IP address of the advertising router</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + <defaultValue>0.0.0.0</defaultValue> + </leafNode> + </children> +</node> +<tagNode name="neighbor"> + <properties> + <help>Specify neighbor router</help> + <valueHelp> + <format>ipv4</format> + <description>Neighbor IP address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + <children> + <leafNode name="poll-interval"> + <properties> + <help>Dead neighbor polling interval (default: 60)</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Seconds between dead neighbor polling interval</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <defaultValue>60</defaultValue> + </leafNode> + <leafNode name="priority"> + <properties> + <help>Neighbor priority in seconds (default: 0)</help> + <valueHelp> + <format>u32:0-255</format> + <description>Neighbor priority</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-255"/> + </constraint> + </properties> + <defaultValue>0</defaultValue> + </leafNode> + </children> +</tagNode> +<node name="parameters"> + <properties> + <help>OSPF specific parameters</help> + </properties> + <children> + <leafNode name="abr-type"> + <properties> + <help>OSPF ABR type (default: cisco)</help> + <completionHelp> + <list>cisco ibm shortcut standard</list> + </completionHelp> + <valueHelp> + <format>cisco</format> + <description>Cisco ABR type (default)</description> + </valueHelp> + <valueHelp> + <format>ibm</format> + <description>Ibm ABR type</description> + </valueHelp> + <valueHelp> + <format>shortcut</format> + <description>Shortcut ABR type</description> + </valueHelp> + <valueHelp> + <format>standard</format> + <description>Standard ABR type</description> + </valueHelp> + <constraint> + <regex>^(cisco|ibm|shortcut|standard)$</regex> + </constraint> + </properties> + <defaultValue>cisco</defaultValue> + </leafNode> + <leafNode name="opaque-lsa"> + <properties> + <help>Enable the Opaque-LSA capability (rfc2370)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="rfc1583-compatibility"> + <properties> + <help>Enable rfc1583 criteria for handling AS external routes</help> + <valueless/> + </properties> + </leafNode> + #include <include/ospf/ospf-router-id.xml.i> + </children> +</node> +#include <include/routing-passive-interface-xml.i> +<leafNode name="passive-interface-exclude"> + <properties> + <help>Interface to exclude when using 'passive-interface default'</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface to exclude when suppressing routing updates</description> + </valueHelp> + <valueHelp> + <format>vlinkN</format> + <description>Virtual-link interface to exclude when suppressing routing updates</description> + </valueHelp> + <constraint> + <validator name="interface-name"/> + <regex>^(vlink[0-9]+)$</regex> + </constraint> + <multi/> + </properties> +</leafNode> +<node name="redistribute"> + <properties> + <help>Redistribute information from another routing protocol</help> + </properties> + <children> + <node name="bgp"> + <properties> + <help>Redistribute BGP routes</help> + </properties> + <children> + #include <include/ospf/ospf-metric.xml.i> + #include <include/ospf/ospf-metric-type.xml.i> + #include <include/route-map.xml.i> + </children> + </node> + <node name="connected"> + <properties> + <help>Redistribute connected routes</help> + </properties> + <children> + #include <include/ospf/ospf-metric.xml.i> + #include <include/ospf/ospf-metric-type.xml.i> + #include <include/route-map.xml.i> + </children> + </node> + <node name="isis"> + <properties> + <help>Redistribute IS-IS routes</help> + </properties> + <children> + #include <include/ospf/ospf-metric.xml.i> + #include <include/ospf/ospf-metric-type.xml.i> + #include <include/route-map.xml.i> + </children> + </node> + <node name="kernel"> + <properties> + <help>Redistribute kernel routes</help> + </properties> + <children> + #include <include/ospf/ospf-metric.xml.i> + #include <include/ospf/ospf-metric-type.xml.i> + #include <include/route-map.xml.i> + </children> + </node> + <node name="rip"> + <properties> + <help>Redistribute rip routes</help> + </properties> + <children> + #include <include/ospf/ospf-metric.xml.i> + #include <include/ospf/ospf-metric-type.xml.i> + #include <include/route-map.xml.i> + </children> + </node> + <node name="static"> + <properties> + <help>Redistribute static routes</help> + </properties> + <children> + #include <include/ospf/ospf-metric.xml.i> + #include <include/ospf/ospf-metric-type.xml.i> + #include <include/route-map.xml.i> + </children> + </node> + </children> +</node> +<node name="refresh"> + <properties> + <help>Adjust refresh parameters</help> + </properties> + <children> + <leafNode name="timers"> + <properties> + <help>Refresh timer</help> + <valueHelp> + <format>u32:10-1800</format> + <description>Timer value in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 10-1800"/> + </constraint> + </properties> + </leafNode> + </children> +</node> +#include <include/route-map.xml.i> +<node name="timers"> + <properties> + <help>Adjust routing timers</help> + </properties> + <children> + <node name="throttle"> + <properties> + <help>Throttling adaptive timers</help> + </properties> + <children> + <node name="spf"> + <properties> + <help>OSPF SPF timers</help> + </properties> + <children> + <leafNode name="delay"> + <properties> + <help>Delay from first change received till SPF calculation (default: 200)</help> + <valueHelp> + <format>u32:0-600000</format> + <description>Delay in milliseconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-600000"/> + </constraint> + </properties> + <defaultValue>200</defaultValue> + </leafNode> + <leafNode name="initial-holdtime"> + <properties> + <help>Initial hold time between consecutive SPF calculations (default: 1000)</help> + <valueHelp> + <format>u32:0-600000</format> + <description>Initial hold time in milliseconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-600000"/> + </constraint> + </properties> + <defaultValue>1000</defaultValue> + </leafNode> + <leafNode name="max-holdtime"> + <properties> + <help>Maximum hold time (default: 10000)</help> + <valueHelp> + <format>u32:0-600000</format> + <description>Max hold time in milliseconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-600000"/> + </constraint> + </properties> + <defaultValue>10000</defaultValue> + </leafNode> + </children> + </node> + </children> + </node> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/ospf/ospf-distance-global.xml.i b/interface-definitions/include/ospf/ospf-distance-global.xml.i new file mode 100644 index 000000000..08cd76cba --- /dev/null +++ b/interface-definitions/include/ospf/ospf-distance-global.xml.i @@ -0,0 +1,14 @@ +<!-- include start from ospf/ospf-distance-global.xml.i --> +<leafNode name="global"> + <properties> + <help>Administrative distance</help> + <valueHelp> + <format>u32:1-255</format> + <description>Administrative distance</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/ospf/ospf-distance-per-protocol.xml.i b/interface-definitions/include/ospf/ospf-distance-per-protocol.xml.i new file mode 100644 index 000000000..d2c4b8b52 --- /dev/null +++ b/interface-definitions/include/ospf/ospf-distance-per-protocol.xml.i @@ -0,0 +1,38 @@ +<!-- include start from ospf/ospf-distance-per-protocol.xml.i --> +<leafNode name="external"> + <properties> + <help>Distance for external routes</help> + <valueHelp> + <format>u32:1-255</format> + <description>Distance for external routes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> +</leafNode> +<leafNode name="inter-area"> + <properties> + <help>Distance for inter-area routes</help> + <valueHelp> + <format>u32:1-255</format> + <description>Distance for inter-area routes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> +</leafNode> +<leafNode name="intra-area"> + <properties> + <help>Distance for intra-area routes</help> + <valueHelp> + <format>u32:1-255</format> + <description>Distance for intra-area routes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/ospf/ospf-interface-common.xml.i b/interface-definitions/include/ospf/ospf-interface-common.xml.i new file mode 100644 index 000000000..39e90482c --- /dev/null +++ b/interface-definitions/include/ospf/ospf-interface-common.xml.i @@ -0,0 +1,34 @@ +<!-- include start from ospf/ospf-interface-common.xml.i --> +#include <include/bfd.xml.i> +<leafNode name="cost"> + <properties> + <help>Interface cost</help> + <valueHelp> + <format>u32:1-65535</format> + <description>OSPF interface cost</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> +</leafNode> +<leafNode name="mtu-ignore"> + <properties> + <help>Disable Maximum Transmission Unit (MTU) mismatch detection</help> + <valueless/> + </properties> +</leafNode> +<leafNode name="priority"> + <properties> + <help>Router priority (default: 1)</help> + <valueHelp> + <format>u32:0-255</format> + <description>OSPF router priority cost</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-255"/> + </constraint> + </properties> + <defaultValue>1</defaultValue> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/ospf/ospf-intervals.xml.i b/interface-definitions/include/ospf/ospf-intervals.xml.i new file mode 100644 index 000000000..fe220eceb --- /dev/null +++ b/interface-definitions/include/ospf/ospf-intervals.xml.i @@ -0,0 +1,54 @@ +<!-- include start from ospf/ospf-intervals.xml.i --> +<leafNode name="dead-interval"> + <properties> + <help>Interval after which a neighbor is declared dead (default: 40)</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Neighbor dead interval (seconds)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <defaultValue>40</defaultValue> +</leafNode> +<leafNode name="hello-interval"> + <properties> + <help>Interval between hello packets (default: 10)</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Hello interval (seconds)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <defaultValue>10</defaultValue> +</leafNode> +<leafNode name="retransmit-interval"> + <properties> + <help>Interval between retransmitting lost link state advertisements (default: 5)</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Retransmit interval (seconds)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <defaultValue>5</defaultValue> +</leafNode> +<leafNode name="transmit-delay"> + <properties> + <help>Link state transmit delay (default: 1)</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Link state transmit delay (seconds)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <defaultValue>1</defaultValue> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/ospf/ospf-metric-type.xml.i b/interface-definitions/include/ospf/ospf-metric-type.xml.i new file mode 100644 index 000000000..1e982c4bc --- /dev/null +++ b/interface-definitions/include/ospf/ospf-metric-type.xml.i @@ -0,0 +1,15 @@ +<!-- include start from ospf/ospf-metric-type.xml.i --> +<leafNode name="metric-type"> + <properties> + <help>OSPF metric type for default routes (default: 2)</help> + <valueHelp> + <format>u32:1-2</format> + <description>Metric type for default routes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-2"/> + </constraint> + </properties> + <defaultValue>2</defaultValue> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/ospf/ospf-metric.xml.i b/interface-definitions/include/ospf/ospf-metric.xml.i new file mode 100644 index 000000000..125aedea7 --- /dev/null +++ b/interface-definitions/include/ospf/ospf-metric.xml.i @@ -0,0 +1,14 @@ +<!-- include start from ospf/ospf-metric.xml.i --> +<leafNode name="metric"> + <properties> + <help>OSPF default metric</help> + <valueHelp> + <format>u32:0-16777214</format> + <description>Default metric</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-16777214"/> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/ospf/ospf-router-id.xml.i b/interface-definitions/include/ospf/ospf-router-id.xml.i new file mode 100644 index 000000000..5dbb52a36 --- /dev/null +++ b/interface-definitions/include/ospf/ospf-router-id.xml.i @@ -0,0 +1,14 @@ +<!-- include start from ospf/ospf-router-id.xml.i --> +<leafNode name="router-id"> + <properties> + <help>Override the default router identifier</help> + <valueHelp> + <format>ipv4</format> + <description>Override the default router identifier</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/policy-list-action.xml.i b/interface-definitions/include/policy-list-action.xml.i new file mode 100644 index 000000000..fddbd5a98 --- /dev/null +++ b/interface-definitions/include/policy-list-action.xml.i @@ -0,0 +1,21 @@ +<!-- included start from policy-list-action.xml.i --> +<leafNode name="action"> + <properties> + <help>Action to take on entries matching this rule [REQUIRED]</help> + <completionHelp> + <list>permit deny</list> + </completionHelp> + <valueHelp> + <format>permit</format> + <description>Permit matching entries</description> + </valueHelp> + <valueHelp> + <format>deny</format> + <description>Deny matching entries</description> + </valueHelp> + <constraint> + <regex>^(permit|deny)$</regex> + </constraint> + </properties> +</leafNode> +<!-- included end --> diff --git a/interface-definitions/include/policy-list-description.xml.i b/interface-definitions/include/policy-list-description.xml.i new file mode 100644 index 000000000..a50278729 --- /dev/null +++ b/interface-definitions/include/policy-list-description.xml.i @@ -0,0 +1,11 @@ +<!-- included start from policy-list-description.xml.i --> +<leafNode name="description"> + <properties> + <help>Description for this policy</help> + <valueHelp> + <format>txt</format> + <description>Description for this policy</description> + </valueHelp> + </properties> +</leafNode> +<!-- included end --> diff --git a/interface-definitions/include/policy-list-rule-description.xml.i b/interface-definitions/include/policy-list-rule-description.xml.i new file mode 100644 index 000000000..e22fb7c28 --- /dev/null +++ b/interface-definitions/include/policy-list-rule-description.xml.i @@ -0,0 +1,11 @@ +<!-- included start from policy-list-rule-description.xml.i --> +<leafNode name="description"> + <properties> + <help>Description for this rule</help> + <valueHelp> + <format>txt</format> + <description>Description for this rule</description> + </valueHelp> + </properties> +</leafNode> +<!-- included end --> diff --git a/interface-definitions/include/port-number.xml.i b/interface-definitions/include/port-number.xml.i index 81c192628..b62aef32b 100644 --- a/interface-definitions/include/port-number.xml.i +++ b/interface-definitions/include/port-number.xml.i @@ -1,4 +1,4 @@ -<!-- included start from port-number.xml.i --> +<!-- include start from port-number.xml.i --> <leafNode name="port"> <properties> <help>Port number used by connection</help> @@ -11,4 +11,4 @@ </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/radius-server-ipv4-ipv6.xml.i b/interface-definitions/include/radius-server-ipv4-ipv6.xml.i new file mode 100644 index 000000000..5b12bec62 --- /dev/null +++ b/interface-definitions/include/radius-server-ipv4-ipv6.xml.i @@ -0,0 +1,52 @@ +<!-- include start from radius-server-ipv4-ipv6.xml.i --> +<node name="radius"> + <properties> + <help>RADIUS based user authentication</help> + </properties> + <children> + <tagNode name="server"> + <properties> + <help>RADIUS server configuration</help> + <valueHelp> + <format>ipv4</format> + <description>RADIUS server IPv4 address</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>RADIUS server IPv6 address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + <validator name="ipv6-address"/> + </constraint> + </properties> + <children> + #include <include/generic-disable-node.xml.i> + #include <include/radius-server-key.xml.i> + #include <include/radius-server-port.xml.i> + </children> + </tagNode> + <leafNode name="source-address"> + <properties> + <help>Source IP address used to initiate connection</help> + <completionHelp> + <script>${vyos_completion_dir}/list_local_ips.sh --both</script> + </completionHelp> + <valueHelp> + <format>ipv4</format> + <description>IPv4 source address</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>IPv6 source address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + <validator name="ipv6-address"/> + </constraint> + <multi/> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/radius-server-ipv4.xml.i b/interface-definitions/include/radius-server-ipv4.xml.i new file mode 100644 index 000000000..ab4c8e10e --- /dev/null +++ b/interface-definitions/include/radius-server-ipv4.xml.i @@ -0,0 +1,27 @@ +<!-- include start from radius-server-ipv4.xml.i --> +<node name="radius"> + <properties> + <help>RADIUS based user authentication</help> + </properties> + <children> + #include <include/source-address-ipv4.xml.i> + <tagNode name="server"> + <properties> + <help>RADIUS server configuration</help> + <valueHelp> + <format>ipv4</format> + <description>RADIUS server IPv4 address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + <children> + #include <include/generic-disable-node.xml.i> + #include <include/radius-server-key.xml.i> + #include <include/radius-server-port.xml.i> + </children> + </tagNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/radius-server-key.xml.i b/interface-definitions/include/radius-server-key.xml.i new file mode 100644 index 000000000..c6301646b --- /dev/null +++ b/interface-definitions/include/radius-server-key.xml.i @@ -0,0 +1,7 @@ +<!-- include start from radius-server-key.xml.i --> +<leafNode name="key"> + <properties> + <help>Shared secret key</help> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/radius-server-port.xml.i b/interface-definitions/include/radius-server-port.xml.i new file mode 100644 index 000000000..4e5d906bc --- /dev/null +++ b/interface-definitions/include/radius-server-port.xml.i @@ -0,0 +1,15 @@ +<!-- include start from radius-server-port.xml.i --> +<leafNode name="port"> + <properties> + <help>Authentication port</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Numeric IP port (default: 1812)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <defaultValue>1812</defaultValue> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/radius-server.xml.i b/interface-definitions/include/radius-server.xml.i deleted file mode 100644 index c1dadd2a2..000000000 --- a/interface-definitions/include/radius-server.xml.i +++ /dev/null @@ -1,48 +0,0 @@ -<!-- included start from radius-server.xml.i --> -<node name="radius"> - <properties> - <help>RADIUS based user authentication</help> - </properties> - <children> - #include <include/source-address-ipv4.xml.i> - <tagNode name="server"> - <properties> - <help>RADIUS server configuration</help> - <valueHelp> - <format>ipv4</format> - <description>RADIUS server IPv4 address</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - </constraint> - </properties> - <children> - <leafNode name="disable"> - <properties> - <help>Temporary disable this server</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="key"> - <properties> - <help>Shared secret key</help> - </properties> - </leafNode> - <leafNode name="port"> - <properties> - <help>Authentication port</help> - <valueHelp> - <format>u32:1-65535</format> - <description>Numeric IP port (default: 1812)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - <defaultValue>1812</defaultValue> - </leafNode> - </children> - </tagNode> - </children> -</node> -<!-- included end --> diff --git a/interface-definitions/include/rip-redistribute.xml.i b/interface-definitions/include/rip-redistribute.xml.i deleted file mode 100644 index f9dba3ffe..000000000 --- a/interface-definitions/include/rip-redistribute.xml.i +++ /dev/null @@ -1,26 +0,0 @@ -<!-- included start from rip-redistribute.xml.i --> -<leafNode name="metric"> - <properties> - <help>Metric for redistributed routes</help> - <valueHelp> - <format>u32:1-16</format> - <description>Redistribute route metric</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-16"/> - </constraint> - </properties> -</leafNode> -<leafNode name="route-map"> - <properties> - <help>Route map reference</help> - <valueHelp> - <format>txt</format> - <description>Route map reference</description> - </valueHelp> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> -</leafNode> -<!-- included end --> diff --git a/interface-definitions/include/rip/rip-access-list.xml.i b/interface-definitions/include/rip/rip-access-list.xml.i new file mode 100644 index 000000000..00ee9b736 --- /dev/null +++ b/interface-definitions/include/rip/rip-access-list.xml.i @@ -0,0 +1,39 @@ +<!-- include start from rip/rip-access-list.xml.i --> +<node name="access-list"> + <properties> + <help>Access-list</help> + </properties> + <children> + <leafNode name="in"> + <properties> + <help>Access list to apply to input packets</help> + <valueHelp> + <format>u32</format> + <description>Access list to apply to input packets</description> + </valueHelp> + <completionHelp> + <path>policy access-list</path> + </completionHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + </constraint> + </properties> + </leafNode> + <leafNode name="out"> + <properties> + <help>Access list to apply to output packets</help> + <valueHelp> + <format>u32</format> + <description>Access list to apply to output packets</description> + </valueHelp> + <completionHelp> + <path>policy access-list</path> + </completionHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + </constraint> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/rip/rip-access-list6.xml.i b/interface-definitions/include/rip/rip-access-list6.xml.i new file mode 100644 index 000000000..9e4298bc0 --- /dev/null +++ b/interface-definitions/include/rip/rip-access-list6.xml.i @@ -0,0 +1,39 @@ +<!-- include start from rip/rip-access-list.xml.i --> +<node name="access-list"> + <properties> + <help>Access-list</help> + </properties> + <children> + <leafNode name="in"> + <properties> + <help>Access list to apply to input packets</help> + <valueHelp> + <format>u32</format> + <description>Access list to apply to input packets</description> + </valueHelp> + <completionHelp> + <path>policy access-list6</path> + </completionHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + </constraint> + </properties> + </leafNode> + <leafNode name="out"> + <properties> + <help>Access list to apply to output packets</help> + <valueHelp> + <format>u32</format> + <description>Access list to apply to output packets</description> + </valueHelp> + <completionHelp> + <path>policy access-list6</path> + </completionHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + </constraint> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/rip/rip-default-information.xml.i b/interface-definitions/include/rip/rip-default-information.xml.i new file mode 100644 index 000000000..28c540c26 --- /dev/null +++ b/interface-definitions/include/rip/rip-default-information.xml.i @@ -0,0 +1,15 @@ +<!-- include start from rip/rip-default-information.xml.i --> +<node name="default-information"> + <properties> + <help>Control distribution of default route</help> + </properties> + <children> + <leafNode name="originate"> + <properties> + <help>Distribute a default route</help> + <valueless/> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/rip/rip-default-metric.xml.i b/interface-definitions/include/rip/rip-default-metric.xml.i new file mode 100644 index 000000000..297af5af8 --- /dev/null +++ b/interface-definitions/include/rip/rip-default-metric.xml.i @@ -0,0 +1,14 @@ +<!-- include start from rip/rip-default-metric.xml.i --> +<leafNode name="default-metric"> + <properties> + <help>Metric of redistributed routes</help> + <valueHelp> + <format>u32:1-16</format> + <description>Default metric</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-16"/> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/rip/rip-interface.xml.i b/interface-definitions/include/rip/rip-interface.xml.i new file mode 100644 index 000000000..dd3bddd4f --- /dev/null +++ b/interface-definitions/include/rip/rip-interface.xml.i @@ -0,0 +1,38 @@ +<!-- include start from rip/rip-interface.xml.i --> +<tagNode name="interface"> + <properties> + <help>Interface name</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface name</description> + </valueHelp> + <constraint> + <validator name="interface-name"/> + </constraint> + </properties> + <children> + <node name="split-horizon"> + <properties> + <help>Split horizon parameters</help> + </properties> + <children> + <leafNode name="disable"> + <properties> + <help>Disable split horizon on specified interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="poison-reverse"> + <properties> + <help>Disable split horizon on specified interface</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + </children> +</tagNode> +<!-- include end --> diff --git a/interface-definitions/include/rip/rip-prefix-list.xml.i b/interface-definitions/include/rip/rip-prefix-list.xml.i new file mode 100644 index 000000000..2569a2a09 --- /dev/null +++ b/interface-definitions/include/rip/rip-prefix-list.xml.i @@ -0,0 +1,33 @@ +<!-- include start from rip/rip-prefix-list.xml.i --> +<node name="prefix-list"> + <properties> + <help>Prefix-list</help> + </properties> + <children> + <leafNode name="in"> + <properties> + <help>Prefix-list to apply to input packets</help> + <valueHelp> + <format>txt</format> + <description>Prefix-list to apply to input packets</description> + </valueHelp> + <completionHelp> + <path>policy prefix-list</path> + </completionHelp> + </properties> + </leafNode> + <leafNode name="out"> + <properties> + <help>Prefix-list to apply to output packets</help> + <valueHelp> + <format>txt</format> + <description>Prefix-list to apply to output packets</description> + </valueHelp> + <completionHelp> + <path>policy prefix-list</path> + </completionHelp> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/rip/rip-prefix-list6.xml.i b/interface-definitions/include/rip/rip-prefix-list6.xml.i new file mode 100644 index 000000000..fcf1499e0 --- /dev/null +++ b/interface-definitions/include/rip/rip-prefix-list6.xml.i @@ -0,0 +1,33 @@ +<!-- include start from rip/rip-prefix-list.xml.i --> +<node name="prefix-list"> + <properties> + <help>Prefix-list</help> + </properties> + <children> + <leafNode name="in"> + <properties> + <help>Prefix-list to apply to input packets</help> + <valueHelp> + <format>txt</format> + <description>Prefix-list to apply to input packets</description> + </valueHelp> + <completionHelp> + <path>policy prefix-list6</path> + </completionHelp> + </properties> + </leafNode> + <leafNode name="out"> + <properties> + <help>Prefix-list to apply to output packets</help> + <valueHelp> + <format>txt</format> + <description>Prefix-list to apply to output packets</description> + </valueHelp> + <completionHelp> + <path>policy prefix-list6</path> + </completionHelp> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/rip/rip-redistribute.xml.i b/interface-definitions/include/rip/rip-redistribute.xml.i new file mode 100644 index 000000000..d7a79b007 --- /dev/null +++ b/interface-definitions/include/rip/rip-redistribute.xml.i @@ -0,0 +1,15 @@ +<!-- include start from rip/rip-redistribute.xml.i --> +<leafNode name="metric"> + <properties> + <help>Metric for redistributed routes</help> + <valueHelp> + <format>u32:1-16</format> + <description>Redistribute route metric</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-16"/> + </constraint> + </properties> +</leafNode> +#include <include/route-map.xml.i> +<!-- include end --> diff --git a/interface-definitions/include/rip/rip-timers.xml.i b/interface-definitions/include/rip/rip-timers.xml.i new file mode 100644 index 000000000..3aaaf8e65 --- /dev/null +++ b/interface-definitions/include/rip/rip-timers.xml.i @@ -0,0 +1,48 @@ +<!-- include start from rip/rip-timers.xml.i --> +<node name="timers"> + <properties> + <help>RIPng timer values</help> + </properties> + <children> + <leafNode name="garbage-collection"> + <properties> + <help>Garbage collection timer</help> + <valueHelp> + <format>u32:5-2147483647</format> + <description>Garbage colletion time (default 120)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 5-2147483647"/> + </constraint> + </properties> + <defaultValue>120</defaultValue> + </leafNode> + <leafNode name="timeout"> + <properties> + <help>Routing information timeout timer</help> + <valueHelp> + <format>u32:5-2147483647</format> + <description>Routing information timeout timer (default 180)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 5-2147483647"/> + </constraint> + </properties> + <defaultValue>180</defaultValue> + </leafNode> + <leafNode name="update"> + <properties> + <help>Routing table update timer</help> + <valueHelp> + <format>u32:5-2147483647</format> + <description>Routing table update timer in seconds (default 30)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 5-2147483647"/> + </constraint> + </properties> + <defaultValue>30</defaultValue> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/route-map.xml.i b/interface-definitions/include/route-map.xml.i new file mode 100644 index 000000000..5a1c137b9 --- /dev/null +++ b/interface-definitions/include/route-map.xml.i @@ -0,0 +1,14 @@ +<!-- include start from route-map.xml.i --> +<leafNode name="route-map"> + <properties> + <help>Route map reference</help> + <valueHelp> + <format>txt</format> + <description>Route map reference</description> + </valueHelp> + <completionHelp> + <path>policy route-map</path> + </completionHelp> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/routing-passive-interface-xml.i b/interface-definitions/include/routing-passive-interface-xml.i new file mode 100644 index 000000000..8478620cf --- /dev/null +++ b/interface-definitions/include/routing-passive-interface-xml.i @@ -0,0 +1,24 @@ +<!-- included start from routing-passive-interface-xml.i --> +<leafNode name="passive-interface"> + <properties> + <help>Suppress routing updates on an interface</help> + <completionHelp> + <list>default</list> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface to be passive (i.e. suppress routing updates)</description> + </valueHelp> + <valueHelp> + <format>default</format> + <description>Default to suppress routing updates on all interfaces</description> + </valueHelp> + <constraint> + <regex>^(default)$</regex> + <validator name="interface-name"/> + </constraint> + <multi/> + </properties> +</leafNode> +<!-- included end --> diff --git a/interface-definitions/include/source-address-ipv4-ipv6.xml.i b/interface-definitions/include/source-address-ipv4-ipv6.xml.i index 004e04f7b..af3f9bb68 100644 --- a/interface-definitions/include/source-address-ipv4-ipv6.xml.i +++ b/interface-definitions/include/source-address-ipv4-ipv6.xml.i @@ -1,4 +1,4 @@ -<!-- included start from source-address-ipv4-ipv6.xml.i --> +<!-- include start from source-address-ipv4-ipv6.xml.i --> <leafNode name="source-address"> <properties> <help>Source IP address used to initiate connection</help> @@ -14,9 +14,8 @@ <description>IPv6 source address</description> </valueHelp> <constraint> - <validator name="ipv4-address"/> - <validator name="ipv6-address"/> + <validator name="ip-address"/> </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/source-address-ipv4.xml.i b/interface-definitions/include/source-address-ipv4.xml.i index 2dff2c65e..86235df61 100644 --- a/interface-definitions/include/source-address-ipv4.xml.i +++ b/interface-definitions/include/source-address-ipv4.xml.i @@ -1,4 +1,4 @@ -<!-- included start from source-address-ipv4.xml.i --> +<!-- include start from source-address-ipv4.xml.i --> <leafNode name="source-address"> <properties> <help>IPv4 source address used to initiiate connection</help> @@ -14,4 +14,4 @@ </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/source-interface-ethernet.xml.i b/interface-definitions/include/source-interface-ethernet.xml.i index d641f3cb1..ee04f2cd5 100644 --- a/interface-definitions/include/source-interface-ethernet.xml.i +++ b/interface-definitions/include/source-interface-ethernet.xml.i @@ -1,4 +1,4 @@ -<!-- included start from source-interface-ethernet.xml.i --> +<!-- include start from source-interface-ethernet.xml.i --> <leafNode name="source-interface"> <properties> <help>Physical interface the traffic will go through</help> @@ -11,4 +11,4 @@ </completionHelp> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/source-interface.xml.i b/interface-definitions/include/source-interface.xml.i index e6f0b69a1..a9c2a0f9d 100644 --- a/interface-definitions/include/source-interface.xml.i +++ b/interface-definitions/include/source-interface.xml.i @@ -1,14 +1,17 @@ -<!-- included start from source-interface.xml.i --> +<!-- include start from source-interface.xml.i --> <leafNode name="source-interface"> <properties> - <help>Physical interface used for connection</help> + <help>Interface used to establish connection</help> <valueHelp> <format>interface</format> - <description>Physical interface used for connection</description> + <description>Interface name</description> </valueHelp> <completionHelp> <script>${vyos_completion_dir}/list_interfaces.py</script> </completionHelp> + <constraint> + <validator name="interface-name"/> + </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/static/static-route-blackhole.xml.i b/interface-definitions/include/static/static-route-blackhole.xml.i new file mode 100644 index 000000000..f2ad23e69 --- /dev/null +++ b/interface-definitions/include/static/static-route-blackhole.xml.i @@ -0,0 +1,10 @@ +<!-- include start from static/static-route-blackhole.xml.i --> +<node name="blackhole"> + <properties> + <help>Silently discard packets when matched</help> + </properties> + <children> + #include <include/static/static-route-distance.xml.i> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/static/static-route-distance.xml.i b/interface-definitions/include/static/static-route-distance.xml.i new file mode 100644 index 000000000..a651b98d7 --- /dev/null +++ b/interface-definitions/include/static/static-route-distance.xml.i @@ -0,0 +1,14 @@ +<!-- include start from static/static-route-distance.xml.i --> +<leafNode name="distance"> + <properties> + <help>Distance for this route</help> + <valueHelp> + <format>u32:1-255</format> + <description>Distance for this route</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/static/static-route-interface.xml.i b/interface-definitions/include/static/static-route-interface.xml.i new file mode 100644 index 000000000..ed4f455e5 --- /dev/null +++ b/interface-definitions/include/static/static-route-interface.xml.i @@ -0,0 +1,17 @@ +<!-- include start from static/static-route-interface.xml.i --> +<leafNode name="interface"> + <properties> + <help>Gateway interface name</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Gateway interface name</description> + </valueHelp> + <constraint> + <validator name="interface-name"/> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/static/static-route-map.xml.i b/interface-definitions/include/static/static-route-map.xml.i new file mode 100644 index 000000000..af825e043 --- /dev/null +++ b/interface-definitions/include/static/static-route-map.xml.i @@ -0,0 +1,10 @@ +<!-- include start from static/static-route-map.xml.i --> +<leafNode name="route-map"> + <properties> + <help>Filter routes installed in local route map</help> + <completionHelp> + <path>policy route-map</path> + </completionHelp> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/static/static-route-vrf.xml.i b/interface-definitions/include/static/static-route-vrf.xml.i new file mode 100644 index 000000000..69aba253c --- /dev/null +++ b/interface-definitions/include/static/static-route-vrf.xml.i @@ -0,0 +1,19 @@ +<!-- include start from static/static-route-vrf.xml.i --> +<leafNode name="vrf"> + <properties> + <help>VRF to leak route</help> + <completionHelp> + <list>default</list> + <path>vrf name</path> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Name of VRF to leak to</description> + </valueHelp> + <constraint> + <regex>^(default)$</regex> + <validator name="vrf-name"/> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/static/static-route.xml.i b/interface-definitions/include/static/static-route.xml.i new file mode 100644 index 000000000..254ea3163 --- /dev/null +++ b/interface-definitions/include/static/static-route.xml.i @@ -0,0 +1,90 @@ +<!-- include start from static/static-route.xml.i --> +<tagNode name="route"> + <properties> + <help>VRF static IPv4 route</help> + <valueHelp> + <format>ipv4net</format> + <description>IPv4 static route</description> + </valueHelp> + <constraint> + <validator name="ipv4-prefix"/> + </constraint> + </properties> + <children> + <node name="blackhole"> + <properties> + <help>Silently discard pkts when matched</help> + </properties> + <children> + #include <include/static/static-route-distance.xml.i> + <leafNode name="tag"> + <properties> + <help>Tag value for this route</help> + <valueHelp> + <format>u32:1-4294967295</format> + <description>Tag value for this route</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967295"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + <leafNode name="dhcp-interface"> + <properties> + <help>DHCP interface supplying next-hop IP address</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>DHCP interface name</description> + </valueHelp> + <constraint> + <validator name="interface-name"/> + </constraint> + </properties> + </leafNode> + <tagNode name="interface"> + <properties> + <help>Next-hop IPv4 router interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Gateway interface name</description> + </valueHelp> + <constraint> + <validator name="interface-name"/> + </constraint> + </properties> + <children> + #include <include/generic-disable-node.xml.i> + #include <include/static/static-route-distance.xml.i> + #include <include/static/static-route-vrf.xml.i> + </children> + </tagNode> + <tagNode name="next-hop"> + <properties> + <help>Next-hop IPv4 router address</help> + <valueHelp> + <format>ipv4</format> + <description>Next-hop router address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + <children> + #include <include/generic-disable-node.xml.i> + #include <include/static/static-route-distance.xml.i> + #include <include/static/static-route-interface.xml.i> + #include <include/static/static-route-vrf.xml.i> + </children> + </tagNode> + </children> +</tagNode> +<!-- include end --> + diff --git a/interface-definitions/include/static/static-route6.xml.i b/interface-definitions/include/static/static-route6.xml.i new file mode 100644 index 000000000..0ea995588 --- /dev/null +++ b/interface-definitions/include/static/static-route6.xml.i @@ -0,0 +1,75 @@ +<!-- include start from static/static-route6.xml.i --> +<tagNode name="route6"> + <properties> + <help>VRF static IPv6 route</help> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 static route</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + <children> + <node name="blackhole"> + <properties> + <help>Silently discard pkts when matched</help> + </properties> + <children> + #include <include/static/static-route-distance.xml.i> + <leafNode name="tag"> + <properties> + <help>Tag value for this route</help> + <valueHelp> + <format>u32:1-4294967295</format> + <description>Tag value for this route</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967295"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + <tagNode name="interface"> + <properties> + <help>IPv6 gateway interface name</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Gateway interface name</description> + </valueHelp> + <constraint> + <validator name="interface-name"/> + </constraint> + </properties> + <children> + #include <include/generic-disable-node.xml.i> + #include <include/static/static-route-distance.xml.i> + #include <include/static/static-route-vrf.xml.i> + </children> + </tagNode> + <tagNode name="next-hop"> + <properties> + <help>IPv6 gateway address</help> + <valueHelp> + <format>ipv6</format> + <description>Next-hop IPv6 router</description> + </valueHelp> + <constraint> + <validator name="ipv6-address"/> + </constraint> + </properties> + <children> + #include <include/generic-disable-node.xml.i> + #include <include/static/static-route-distance.xml.i> + #include <include/static/static-route-interface.xml.i> + #include <include/static/static-route-vrf.xml.i> + </children> + </tagNode> + </children> +</tagNode> +<!-- include end --> + diff --git a/interface-definitions/include/vni.xml.i b/interface-definitions/include/vni.xml.i new file mode 100644 index 000000000..faff4c3c3 --- /dev/null +++ b/interface-definitions/include/vni.xml.i @@ -0,0 +1,12 @@ + <leafNode name="vni"> + <properties> + <help>Virtual Network Identifier</help> + <valueHelp> + <format>0-16777214</format> + <description>VXLAN virtual network identifier</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-16777214"/> + </constraint> + </properties> + </leafNode> diff --git a/interface-definitions/include/vpn-ipsec-encryption.xml.i b/interface-definitions/include/vpn-ipsec-encryption.xml.i index 1c1d728fc..041ba9902 100644 --- a/interface-definitions/include/vpn-ipsec-encryption.xml.i +++ b/interface-definitions/include/vpn-ipsec-encryption.xml.i @@ -1,4 +1,4 @@ -<!-- included start from vpn-ipsec-encryption.xml.i --> +<!-- include start from vpn-ipsec-encryption.xml.i --> <leafNode name="encryption"> <properties> <help>Encryption algorithm</help> @@ -230,4 +230,4 @@ </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/vpn-ipsec-hash.xml.i b/interface-definitions/include/vpn-ipsec-hash.xml.i index ca5976d27..93d57b622 100644 --- a/interface-definitions/include/vpn-ipsec-hash.xml.i +++ b/interface-definitions/include/vpn-ipsec-hash.xml.i @@ -1,4 +1,4 @@ -<!-- included start from pn-ipsec-hash.xml.i --> +<!-- include start from pn-ipsec-hash.xml.i --> <leafNode name="hash"> <properties> <help>Hash algorithm</help> @@ -62,4 +62,4 @@ </constraint> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/include/webproxy-url-filtering.xml.i b/interface-definitions/include/webproxy-url-filtering.xml.i index 07db0948f..265bbff94 100644 --- a/interface-definitions/include/webproxy-url-filtering.xml.i +++ b/interface-definitions/include/webproxy-url-filtering.xml.i @@ -1,4 +1,4 @@ -<!-- included start from webproxy-url-filtering.xml.i --> +<!-- include start from webproxy-url-filtering.xml.i --> <leafNode name="allow-category"> <properties> <help>Category to allow</help> @@ -116,4 +116,4 @@ <multi/> </properties> </leafNode> -<!-- included end --> +<!-- include end --> diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in index f6ceefcaa..4382433b2 100644 --- a/interface-definitions/interfaces-bonding.xml.in +++ b/interface-definitions/interfaces-bonding.xml.in @@ -16,7 +16,7 @@ </valueHelp> </properties> <children> - #include <include/address-ipv4-ipv6-dhcp.xml.i> + #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> <node name="arp-monitor"> <properties> <help>ARP link monitoring parameters</help> @@ -49,13 +49,13 @@ </leafNode> </children> </node> - #include <include/interface-description.xml.i> - #include <include/dhcp-options.xml.i> - #include <include/dhcpv6-options.xml.i> - #include <include/interface-disable-link-detect.xml.i> - #include <include/interface-disable.xml.i> - #include <include/interface-vrf.xml.i> - #include <include/interface-mirror.xml.i> + #include <include/interface/interface-description.xml.i> + #include <include/interface/dhcp-options.xml.i> + #include <include/interface/dhcpv6-options.xml.i> + #include <include/interface/interface-disable-link-detect.xml.i> + #include <include/interface/interface-disable.xml.i> + #include <include/interface/interface-vrf.xml.i> + #include <include/interface/interface-mirror.xml.i> <leafNode name="hash-policy"> <properties> <help>Bonding transmit hash policy</help> @@ -81,9 +81,9 @@ </properties> <defaultValue>layer2</defaultValue> </leafNode> - #include <include/interface-ipv4-options.xml.i> - #include <include/interface-ipv6-options.xml.i> - #include <include/interface-mac.xml.i> + #include <include/interface/interface-ipv4-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> + #include <include/interface/interface-mac.xml.i> <leafNode name="min-links"> <properties> <help>Minimum number of member interfaces required up before enabling bond</help> @@ -154,7 +154,7 @@ </leafNode> </children> </node> - #include <include/interface-mtu-68-16000.xml.i> + #include <include/interface/interface-mtu-68-16000.xml.i> <leafNode name="primary"> <properties> <help>Primary device interface</help> @@ -163,9 +163,9 @@ </completionHelp> </properties> </leafNode> - #include <include/vif-s.xml.i> - #include <include/vif.xml.i> - #include <include/interface-xdp.xml.i> + #include <include/interface/vif-s.xml.i> + #include <include/interface/vif.xml.i> + #include <include/interface/interface-xdp.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in index c32c0ca32..1af002142 100644 --- a/interface-definitions/interfaces-bridge.xml.in +++ b/interface-definitions/interfaces-bridge.xml.in @@ -16,7 +16,7 @@ </valueHelp> </properties> <children> - #include <include/address-ipv4-ipv6-dhcp.xml.i> + #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> <leafNode name="aging"> <properties> <help>MAC address aging interval</help> @@ -34,13 +34,13 @@ </properties> <defaultValue>300</defaultValue> </leafNode> - #include <include/interface-description.xml.i> - #include <include/dhcp-options.xml.i> - #include <include/dhcpv6-options.xml.i> - #include <include/interface-disable-link-detect.xml.i> - #include <include/interface-disable.xml.i> - #include <include/interface-vrf.xml.i> - #include <include/interface-mtu-68-16000.xml.i> + #include <include/interface/interface-description.xml.i> + #include <include/interface/dhcp-options.xml.i> + #include <include/interface/dhcpv6-options.xml.i> + #include <include/interface/interface-disable-link-detect.xml.i> + #include <include/interface/interface-disable.xml.i> + #include <include/interface/interface-vrf.xml.i> + #include <include/interface/interface-mtu-68-16000.xml.i> <leafNode name="forwarding-delay"> <properties> <help>Forwarding delay</help> @@ -82,10 +82,16 @@ </leafNode> </children> </node> - #include <include/interface-ipv4-options.xml.i> - #include <include/interface-ipv6-options.xml.i> - #include <include/interface-mac.xml.i> - #include <include/interface-mirror.xml.i> + #include <include/interface/interface-ipv4-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> + #include <include/interface/interface-mac.xml.i> + #include <include/interface/interface-mirror.xml.i> + <leafNode name="enable-vlan"> + <properties> + <help>Enable VLAN aware bridge</help> + <valueless/> + </properties> + </leafNode> <leafNode name="max-age"> <properties> <help>Interval at which neighbor bridges are removed</help> @@ -138,7 +144,7 @@ <description>VLAN id range allowed on this interface (use '-' as delimiter)</description> </valueHelp> <constraint> - <regex>^([0-9]{1,4}-[0-9]{1,4})|([0-9]{1,4})$</regex> + <validator name="allowed-vlan"/> </constraint> <constraintErrorMessage>not a valid VLAN ID value or range</constraintErrorMessage> <multi/> @@ -172,6 +178,12 @@ </properties> <defaultValue>32</defaultValue> </leafNode> + <leafNode name="isolated"> + <properties> + <help>Port is isolated (also known as Private-VLAN)</help> + <valueless/> + </properties> + </leafNode> </children> </tagNode> </children> @@ -196,8 +208,7 @@ <valueless/> </properties> </leafNode> - #include <include/vif-s.xml.i> - #include <include/vif.xml.i> + #include <include/interface/vif.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/interfaces-dummy.xml.in b/interface-definitions/interfaces-dummy.xml.in index 54de43c7a..84c6903c7 100644 --- a/interface-definitions/interfaces-dummy.xml.in +++ b/interface-definitions/interfaces-dummy.xml.in @@ -16,18 +16,18 @@ </valueHelp> </properties> <children> - #include <include/address-ipv4-ipv6.xml.i> - #include <include/interface-description.xml.i> - #include <include/interface-disable.xml.i> + #include <include/interface/address-ipv4-ipv6.xml.i> + #include <include/interface/interface-description.xml.i> + #include <include/interface/interface-disable.xml.i> <node name="ip"> <properties> <help>IPv4 routing parameters</help> </properties> <children> - #include <include/interface-source-validation.xml.i> + #include <include/interface/interface-source-validation.xml.i> </children> </node> - #include <include/interface-vrf.xml.i> + #include <include/interface/interface-vrf.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/interfaces-erspan.xml.in b/interface-definitions/interfaces-erspan.xml.in new file mode 100644 index 000000000..769899415 --- /dev/null +++ b/interface-definitions/interfaces-erspan.xml.in @@ -0,0 +1,114 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="interfaces"> + <children> + <tagNode name="erspan" owner="${vyos_conf_scripts_dir}/interfaces-erspan.py"> + <properties> + <help>Encapsulated Remote SPAN over GRE and IPv4/IPv6 Tunnel Interface</help> + <priority>310</priority> + <constraint> + <regex>^ersp[0-9]+$</regex> + </constraint> + <constraintErrorMessage>ERSPAN tunnel interface must be named erspN</constraintErrorMessage> + <valueHelp> + <format>erspN</format> + <description>ERSPAN Tunnel interface name</description> + </valueHelp> + </properties> + <children> + #include <include/interface/interface-description.xml.i> + #include <include/interface/interface-disable.xml.i> + #include <include/interface/interface-disable-link-detect.xml.i> + #include <include/interface/interface-mtu-64-8024.xml.i> + #include <include/source-address-ipv4-ipv6.xml.i> + #include <include/interface/tunnel-remote.xml.i> + <leafNode name="encapsulation"> + <properties> + <help>Encapsulation of this tunnel interface</help> + <completionHelp> + <list>erspan ip6erspan</list> + </completionHelp> + <valueHelp> + <format>erspan</format> + <description>Generic Routing Encapsulation</description> + </valueHelp> + <valueHelp> + <format>ip6erspan</format> + <description>Generic Routing Encapsulation bridge interface</description> + </valueHelp> + <constraint> + <regex>^(erspan|ip6erspan)$</regex> + </constraint> + <constraintErrorMessage>Invalid encapsulation, must be one of: erspan, ip6erspan</constraintErrorMessage> + </properties> + </leafNode> + <node name="parameters"> + <properties> + <help>ERSPAN Tunnel parameters</help> + </properties> + <children> + <node name="ip"> + <properties> + <help>IPv4 specific tunnel parameters</help> + </properties> + <children> + #include <include/interface/interface-parameters-key.xml.i> + #include <include/interface/interface-parameters-tos.xml.i> + #include <include/interface/interface-parameters-ttl.xml.i> + </children> + </node> + <leafNode name="version"> + <properties> + <help>ERSPAN version number setting(default:1)</help> + <constraint> + <validator name="numeric" argument="--range 1-2"/> + </constraint> + <constraintErrorMessage>The version number of ERSPAN must be 1 or 2</constraintErrorMessage> + </properties> + <defaultValue>1</defaultValue> + </leafNode> + <leafNode name="direction"> + <properties> + <help>Specifies mirrored traffic direction</help> + <completionHelp> + <list>ingress egress</list> + </completionHelp> + <valueHelp> + <format>ingress</format> + <description>Mirror ingress direction</description> + </valueHelp> + <valueHelp> + <format>egress</format> + <description>Mirror egress direction</description> + </valueHelp> + <constraint> + <regex>^(ingress|egress)$</regex> + </constraint> + <constraintErrorMessage>The mirror direction of ERSPAN must be ingress or egress</constraintErrorMessage> + </properties> + </leafNode> + <leafNode name="hwid"> + <properties> + <help>an unique identifier of an ERSPAN v2 engine within a system</help> + <constraint> + <validator name="numeric" argument="--range 1-1048575"/> + </constraint> + <constraintErrorMessage>ERSPAN hwid must be a number(range:0-1048575)</constraintErrorMessage> + </properties> + </leafNode> + <leafNode name="idx"> + <properties> + <help>specifies the ERSPAN v1 index field</help> + <constraint> + <validator name="numeric" argument="--range 0-63"/> + </constraint> + <constraintErrorMessage>ERSPAN idx must be a number(range:0-63)</constraintErrorMessage> + </properties> + </leafNode> + </children> + </node> + </children> + </tagNode> + </children> + </node> +</interfaceDefinition> diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in index be44072a6..f00f65364 100644 --- a/interface-definitions/interfaces-ethernet.xml.in +++ b/interface-definitions/interfaces-ethernet.xml.in @@ -16,18 +16,18 @@ </valueHelp> </properties> <children> - #include <include/address-ipv4-ipv6-dhcp.xml.i> - #include <include/interface-description.xml.i> - #include <include/dhcp-options.xml.i> - #include <include/dhcpv6-options.xml.i> + #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> + #include <include/interface/interface-description.xml.i> + #include <include/interface/dhcp-options.xml.i> + #include <include/interface/dhcpv6-options.xml.i> <leafNode name="disable-flow-control"> <properties> <help>Disable Ethernet flow control (pause frames)</help> <valueless/> </properties> </leafNode> - #include <include/interface-disable-link-detect.xml.i> - #include <include/interface-disable.xml.i> + #include <include/interface/interface-disable-link-detect.xml.i> + #include <include/interface/interface-disable.xml.i> <leafNode name="duplex"> <properties> <help>Duplex mode</help> @@ -53,13 +53,13 @@ </properties> <defaultValue>auto</defaultValue> </leafNode> - #include <include/interface-eapol.xml.i> - #include <include/interface-hw-id.xml.i> - #include <include/interface-ipv4-options.xml.i> - #include <include/interface-ipv6-options.xml.i> - #include <include/interface-mac.xml.i> - #include <include/interface-mtu-68-16000.xml.i> - #include <include/interface-mirror.xml.i> + #include <include/interface/interface-eapol.xml.i> + #include <include/interface/interface-hw-id.xml.i> + #include <include/interface/interface-ipv4-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> + #include <include/interface/interface-mac.xml.i> + #include <include/interface/interface-mtu-68-16000.xml.i> + #include <include/interface/interface-mirror.xml.i> <node name="offload"> <properties> <help>Configurable offload options</help> @@ -191,10 +191,10 @@ </leafNode> </children> </node> - #include <include/vif-s.xml.i> - #include <include/vif.xml.i> - #include <include/interface-vrf.xml.i> - #include <include/interface-xdp.xml.i> + #include <include/interface/vif-s.xml.i> + #include <include/interface/vif.xml.i> + #include <include/interface/interface-vrf.xml.i> + #include <include/interface/interface-xdp.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/interfaces-geneve.xml.in b/interface-definitions/interfaces-geneve.xml.in index 0c776e3c3..bdcbc3f5e 100644 --- a/interface-definitions/interfaces-geneve.xml.in +++ b/interface-definitions/interfaces-geneve.xml.in @@ -16,37 +16,40 @@ </valueHelp> </properties> <children> - #include <include/address-ipv4-ipv6.xml.i> - #include <include/interface-description.xml.i> - #include <include/interface-disable.xml.i> - #include <include/interface-ipv4-options.xml.i> - #include <include/interface-ipv6-options.xml.i> - #include <include/interface-mac.xml.i> - #include <include/interface-mtu-1450-16000.xml.i> - <leafNode name="remote"> + #include <include/interface/address-ipv4-ipv6.xml.i> + #include <include/interface/interface-description.xml.i> + #include <include/interface/interface-disable.xml.i> + #include <include/interface/interface-ipv4-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> + #include <include/interface/interface-mac.xml.i> + #include <include/interface/interface-mtu-1450-16000.xml.i> + <node name="parameters"> <properties> - <help>Remote address of GENEVE tunnel</help> - <valueHelp> - <format>ipv4</format> - <description>Remote address of GENEVE tunnel</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - </constraint> + <help>GENEVE tunnel parameters</help> </properties> - </leafNode> - <leafNode name="vni"> - <properties> - <help>Virtual Network Identifier</help> - <valueHelp> - <format>0-16777214</format> - <description>GENEVE virtual network identifier</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-16777214"/> - </constraint> - </properties> - </leafNode> + <children> + <node name="ip"> + <properties> + <help>IPv4 specific tunnel parameters</help> + </properties> + <children> + #include <include/interface/interface-parameters-dont-fragment.xml.i> + #include <include/interface/interface-parameters-tos.xml.i> + #include <include/interface/interface-parameters-ttl.xml.i> + </children> + </node> + <node name="ipv6"> + <properties> + <help>IPv6 specific tunnel parameters</help> + </properties> + <children> + #include <include/interface/interface-parameters-flowlabel.xml.i> + </children> + </node> + </children> + </node> + #include <include/interface/tunnel-remote.xml.i> + #include <include/vni.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/interfaces-l2tpv3.xml.in b/interface-definitions/interfaces-l2tpv3.xml.in index a8ddb74fb..831b9b47a 100644 --- a/interface-definitions/interfaces-l2tpv3.xml.in +++ b/interface-definitions/interfaces-l2tpv3.xml.in @@ -16,8 +16,8 @@ </valueHelp> </properties> <children> - #include <include/address-ipv4-ipv6.xml.i> - #include <include/interface-description.xml.i> + #include <include/interface/address-ipv4-ipv6.xml.i> + #include <include/interface/interface-description.xml.i> <leafNode name="destination-port"> <properties> <help>UDP destination port for L2TPv3 tunnel (default: 5000)</help> @@ -31,7 +31,7 @@ </properties> <defaultValue>5000</defaultValue> </leafNode> - #include <include/interface-disable.xml.i> + #include <include/interface/interface-disable.xml.i> <leafNode name="encapsulation"> <properties> <help>Encapsulation type (default: UDP)</help> @@ -53,25 +53,13 @@ </properties> <defaultValue>udp</defaultValue> </leafNode> - #include <include/interface-ipv4-options.xml.i> - #include <include/interface-ipv6-options.xml.i> - <leafNode name="local-ip"> - <properties> - <help>Local IP address for L2TPv3 tunnel</help> - <valueHelp> - <format>ipv4</format> - <description>Local IPv4 address of tunnel</description> - </valueHelp> - <valueHelp> - <format>ipv6</format> - <description>Local IPv6 address of tunnel</description> - </valueHelp> - <constraint> - <validator name="ip-address"/> - </constraint> - </properties> + #include <include/interface/interface-ipv4-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> + #include <include/source-address-ipv4-ipv6.xml.i> + #include <include/interface/interface-mtu-68-16000.xml.i> + <leafNode name="mtu"> + <defaultValue>1488</defaultValue> </leafNode> - #include <include/interface-mtu-68-16000.xml.i> <leafNode name="peer-session-id"> <properties> <help>Peer session identifier</help> @@ -96,22 +84,8 @@ </constraint> </properties> </leafNode> - <leafNode name="remote-ip"> - <properties> - <help>Remote IP address for L2TPv3 tunnel</help> - <valueHelp> - <format>ipv4</format> - <description>Remote IPv4 address of tunnel</description> - </valueHelp> - <valueHelp> - <format>ipv6</format> - <description>Remote IPv6 address of tunnel</description> - </valueHelp> - <constraint> - <validator name="ip-address"/> - </constraint> - </properties> - </leafNode> + #include <include/interface/interface-mtu-68-16000.xml.i> + #include <include/interface/tunnel-remote.xml.i> <leafNode name="session-id"> <properties> <help>Session identifier</help> diff --git a/interface-definitions/interfaces-loopback.xml.in b/interface-definitions/interfaces-loopback.xml.in index 0fd74f302..5d0ca5b0a 100644 --- a/interface-definitions/interfaces-loopback.xml.in +++ b/interface-definitions/interfaces-loopback.xml.in @@ -16,14 +16,14 @@ </valueHelp> </properties> <children> - #include <include/address-ipv4-ipv6.xml.i> - #include <include/interface-description.xml.i> + #include <include/interface/address-ipv4-ipv6.xml.i> + #include <include/interface/interface-description.xml.i> <node name="ip"> <properties> <help>IPv4 routing parameters</help> </properties> <children> - #include <include/interface-source-validation.xml.i> + #include <include/interface/interface-source-validation.xml.i> </children> </node> </children> diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in index 4d2581906..fce88b21c 100644 --- a/interface-definitions/interfaces-macsec.xml.in +++ b/interface-definitions/interfaces-macsec.xml.in @@ -16,9 +16,9 @@ </valueHelp> </properties> <children> - #include <include/address-ipv4-ipv6.xml.i> - #include <include/interface-ipv4-options.xml.i> - #include <include/interface-ipv6-options.xml.i> + #include <include/interface/address-ipv4-ipv6.xml.i> + #include <include/interface/interface-ipv4-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> <node name="security"> <properties> <help>Security/Encryption Settings</help> @@ -28,14 +28,18 @@ <properties> <help>Cipher suite used</help> <completionHelp> - <list>gcm-aes-128</list> + <list>gcm-aes-128 gcm-aes-256</list> </completionHelp> <valueHelp> <format>gcm-aes-128</format> <description>Galois/Counter Mode of AES cipher with 128-bit key (default)</description> </valueHelp> + <valueHelp> + <format>gcm-aes-256</format> + <description>Galois/Counter Mode of AES cipher with 256-bit key</description> + </valueHelp> <constraint> - <regex>(gcm-aes-128)</regex> + <regex>^(gcm-aes-128|gcm-aes-256)$</regex> </constraint> </properties> </leafNode> @@ -107,11 +111,14 @@ </leafNode> </children> </node> - #include <include/interface-description.xml.i> - #include <include/interface-disable.xml.i> - #include <include/interface-mtu-68-16000.xml.i> + #include <include/interface/interface-description.xml.i> + #include <include/interface/interface-disable.xml.i> + #include <include/interface/interface-mtu-68-16000.xml.i> + <leafNode name="mtu"> + <defaultValue>1460</defaultValue> + </leafNode> #include <include/source-interface-ethernet.xml.i> - #include <include/interface-vrf.xml.i> + #include <include/interface/interface-vrf.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index 34040bf72..effbdd674 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -33,7 +33,7 @@ </leafNode> </children> </node> - #include <include/interface-description.xml.i> + #include <include/interface/interface-description.xml.i> <leafNode name="device-type"> <properties> <help>OpenVPN interface device-type (default: tun)</help> @@ -54,7 +54,7 @@ </properties> <defaultValue>tun</defaultValue> </leafNode> - #include <include/interface-disable.xml.i> + #include <include/interface/interface-disable.xml.i> <node name="encryption"> <properties> <help>Data Encryption settings</help> @@ -171,7 +171,7 @@ </leafNode> </children> </node> - #include <include/interface-ipv6-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> <leafNode name="hash"> <properties> <help>Hashing Algorithm</help> @@ -418,12 +418,7 @@ </valueHelp> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Option to disable client connection</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="ip"> <properties> <help>IP address of the client</help> @@ -482,12 +477,7 @@ <help>Pool of client IPv4 addresses</help> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Disable client IP pool</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="start"> <properties> <help>First IP address in the pool</help> @@ -546,12 +536,7 @@ </constraint> </properties> </leafNode> - <leafNode name="disable"> - <properties> - <help>Disable client IPv6 pool</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> </children> </node> <leafNode name="domain-name"> @@ -776,7 +761,7 @@ <valueless/> </properties> </leafNode> - #include <include/interface-vrf.xml.i> + #include <include/interface/interface-vrf.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in index 7dccfbc9c..a8e371f85 100644 --- a/interface-definitions/interfaces-pppoe.xml.in +++ b/interface-definitions/interfaces-pppoe.xml.in @@ -42,7 +42,7 @@ </leafNode> </children> </node> - #include <include/interface-dial-on-demand.xml.i> + #include <include/interface/interface-dial-on-demand.xml.i> <leafNode name="default-route"> <properties> <help>Default route insertion behaviour (default: auto)</help> @@ -68,10 +68,10 @@ </properties> <defaultValue>auto</defaultValue> </leafNode> - #include <include/dhcpv6-options.xml.i> - #include <include/interface-description.xml.i> - #include <include/interface-disable.xml.i> - #include <include/interface-vrf.xml.i> + #include <include/interface/dhcpv6-options.xml.i> + #include <include/interface/interface-description.xml.i> + #include <include/interface/interface-disable.xml.i> + #include <include/interface/interface-vrf.xml.i> <leafNode name="idle-timeout"> <properties> <help>Delay before disconnecting idle session (in seconds)</help> @@ -86,7 +86,7 @@ <help>IPv4 routing parameters</help> </properties> <children> - #include <include/interface-source-validation.xml.i> + #include <include/interface/interface-source-validation.xml.i> </children> </node> <node name="ipv6"> @@ -99,7 +99,7 @@ <help>IPv6 address configuration modes</help> </properties> <children> - #include <include/ipv6-address-autoconf.xml.i> + #include <include/interface/ipv6-address-autoconf.xml.i> </children> </node> </children> @@ -124,7 +124,10 @@ </constraint> </properties> </leafNode> - #include <include/interface-mtu-68-1500.xml.i> + #include <include/interface/interface-mtu-68-1500.xml.i> + <leafNode name="mtu"> + <defaultValue>1492</defaultValue> + </leafNode> <leafNode name="no-peer-dns"> <properties> <help>Do not use DNS servers provided by the peer</help> diff --git a/interface-definitions/interfaces-pseudo-ethernet.xml.in b/interface-definitions/interfaces-pseudo-ethernet.xml.in index 32ba5ea01..9e3e0cb26 100644 --- a/interface-definitions/interfaces-pseudo-ethernet.xml.in +++ b/interface-definitions/interfaces-pseudo-ethernet.xml.in @@ -16,17 +16,17 @@ </valueHelp> </properties> <children> - #include <include/address-ipv4-ipv6-dhcp.xml.i> - #include <include/interface-description.xml.i> - #include <include/dhcp-options.xml.i> - #include <include/dhcpv6-options.xml.i> - #include <include/interface-disable-link-detect.xml.i> - #include <include/interface-disable.xml.i> - #include <include/interface-vrf.xml.i> - #include <include/interface-ipv4-options.xml.i> - #include <include/interface-ipv6-options.xml.i> + #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> + #include <include/interface/interface-description.xml.i> + #include <include/interface/dhcp-options.xml.i> + #include <include/interface/dhcpv6-options.xml.i> + #include <include/interface/interface-disable-link-detect.xml.i> + #include <include/interface/interface-disable.xml.i> + #include <include/interface/interface-vrf.xml.i> + #include <include/interface/interface-ipv4-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> #include <include/source-interface-ethernet.xml.i> - #include <include/interface-mac.xml.i> + #include <include/interface/interface-mac.xml.i> <leafNode name="mode"> <properties> <help>Receive mode (default: private)</help> @@ -56,9 +56,9 @@ </properties> <defaultValue>private</defaultValue> </leafNode> - #include <include/interface-mtu-68-16000.xml.i> - #include <include/vif-s.xml.i> - #include <include/vif.xml.i> + #include <include/interface/interface-mtu-68-16000.xml.i> + #include <include/interface/vif-s.xml.i> + #include <include/interface/vif.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in index 3a4db6f09..e3aad2719 100644 --- a/interface-definitions/interfaces-tunnel.xml.in +++ b/interface-definitions/interfaces-tunnel.xml.in @@ -16,50 +16,19 @@ </valueHelp> </properties> <children> - #include <include/interface-description.xml.i> - #include <include/address-ipv4-ipv6.xml.i> - #include <include/interface-disable.xml.i> - #include <include/interface-disable-link-detect.xml.i> - #include <include/interface-vrf.xml.i> - #include <include/interface-mtu-64-8024.xml.i> - #include <include/interface-ipv4-options.xml.i> - #include <include/interface-ipv6-options.xml.i> - <leafNode name="local-ip"> - <properties> - <help>Local IP address for this tunnel</help> - <valueHelp> - <format>ipv4</format> - <description>Local IPv4 address for this tunnel</description> - </valueHelp> - <valueHelp> - <format>ipv6</format> - <description>Local IPv6 address for this tunnel [NOTICE: unavailable for mGRE tunnels]</description> - </valueHelp> - <completionHelp> - <script>${vyos_completion_dir}/list_local_ips.sh --both</script> - </completionHelp> - <constraint> - <validator name="ip-address"/> - </constraint> - </properties> - </leafNode> - <leafNode name="remote-ip"> - <properties> - <help>Remote IP address for this tunnel</help> - <valueHelp> - <format>ipv4</format> - <description>Remote IPv4 address for this tunnel</description> - </valueHelp> - <valueHelp> - <format>ipv6</format> - <description>Remote IPv6 address for this tunnel</description> - </valueHelp> - <constraint> - <!-- does it need fixing/changing to be more restrictive ? --> - <validator name="ip-address"/> - </constraint> - </properties> + #include <include/interface/interface-description.xml.i> + #include <include/interface/address-ipv4-ipv6.xml.i> + #include <include/interface/interface-disable.xml.i> + #include <include/interface/interface-disable-link-detect.xml.i> + #include <include/interface/interface-vrf.xml.i> + #include <include/interface/interface-mtu-64-8024.xml.i> + <leafNode name="mtu"> + <defaultValue>1476</defaultValue> </leafNode> + #include <include/interface/interface-ipv4-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> + #include <include/source-address-ipv4-ipv6.xml.i> + #include <include/interface/tunnel-remote.xml.i> <leafNode name="source-interface"> <properties> <help>Physical Interface used for underlaying traffic</help> @@ -111,21 +80,25 @@ <properties> <help>Encapsulation of this tunnel interface</help> <completionHelp> - <list>gre gre-bridge ip6gre ip6ip6 ipip ipip6 sit</list> + <list>gre gretap ip6gre ip6gretap ip6ip6 ipip ipip6 sit</list> </completionHelp> <valueHelp> <format>gre</format> <description>Generic Routing Encapsulation</description> </valueHelp> <valueHelp> - <format>gre-bridge</format> - <description>Generic Routing Encapsulation bridge interface</description> + <format>gretap</format> + <description>Generic Routing Encapsulation (virtual L2 tunnel)</description> </valueHelp> <valueHelp> <format>ip6gre</format> <description>GRE over IPv6 network</description> </valueHelp> <valueHelp> + <format>ip6gretap</format> + <description>Generic Routing Encapsulation over IPv6 (virtual L2 tunnel)</description> + </valueHelp> + <valueHelp> <format>ip6ip6</format> <description>IP6 in IP6 encapsulation</description> </valueHelp> @@ -142,9 +115,9 @@ <description>Simple Internet Transition encapsulation</description> </valueHelp> <constraint> - <regex>^(gre|gre-bridge|ip6gre|ip6ip6|ipip|ipip6|sit)$</regex> + <regex>^(gre|gretap|ip6gre|ip6gretap|ip6ip6|ipip|ipip6|sit)$</regex> </constraint> - <constraintErrorMessage>Invalid encapsulation, must be one of: gre, gre-bridge, ipip, sit, ipip6, ip6ip6, ip6gre</constraintErrorMessage> + <constraintErrorMessage>Invalid encapsulation, must be one of: gre, gretap, ip6gre, ip6gretap, ipip, sit, ipip6 or ip6ip6</constraintErrorMessage> </properties> </leafNode> <leafNode name="multicast"> @@ -177,47 +150,15 @@ <help>IPv4 specific tunnel parameters</help> </properties> <children> - <leafNode name="ttl"> - <properties> - <help>Time to live field</help> - <valueHelp> - <format>0-255</format> - <description>Time to live (default 255)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-255"/> - </constraint> - <constraintErrorMessage>TTL must be between 0 and 255</constraintErrorMessage> - </properties> - <defaultValue>255</defaultValue> - </leafNode> - <leafNode name="tos"> - <properties> - <help>Type of Service (TOS)</help> - <valueHelp> - <format>0-99</format> - <description>Type of Service (TOS)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-99"/> - </constraint> - <constraintErrorMessage>TOS must be between 0 and 99</constraintErrorMessage> - </properties> - <defaultValue>inherit</defaultValue> - </leafNode> - <leafNode name="key"> + <leafNode name="no-pmtu-discovery"> <properties> - <help>Tunnel key</help> - <valueHelp> - <format>u32</format> - <description>Tunnel key</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-4294967295"/> - </constraint> - <constraintErrorMessage>key must be between 0-4294967295</constraintErrorMessage> + <help>Disable path MTU discovery</help> + <valueless/> </properties> </leafNode> + #include <include/interface/interface-parameters-key.xml.i> + #include <include/interface/interface-parameters-tos.xml.i> + #include <include/interface/interface-parameters-ttl.xml.i> </children> </node> <node name="ipv6"> @@ -227,32 +168,27 @@ <children> <leafNode name="encaplimit"> <properties> - <help>Encaplimit field</help> + <help>Set fixed encapsulation limit</help> + <completionHelp> + <list>none</list> + </completionHelp> <valueHelp> <format>0-255</format> - <description>Encaplimit (default 4)</description> + <description>Encaplimit (default: 4)</description> </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-255"/> - </constraint> - <constraintErrorMessage>key must be between 0-255</constraintErrorMessage> - </properties> - <defaultValue>4</defaultValue> - </leafNode> - <leafNode name="flowlabel"> - <properties> - <help>Flowlabel</help> <valueHelp> - <format>0x0-0x0FFFFF</format> - <description>Tunnel key, 'inherit' or hex value</description> + <format>none</format> + <description>Encaplimit disabled</description> </valueHelp> <constraint> - <regex>(0x){0,1}(0?[0-9A-Fa-f]{1,5})</regex> + <regex>^(none)$</regex> + <validator name="numeric" argument="--range 0-255"/> </constraint> - <constraintErrorMessage>Must be 'inherit' or a number</constraintErrorMessage> + <constraintErrorMessage>Tunnel encaplimit must be 0-255 or none</constraintErrorMessage> </properties> - <defaultValue>inherit</defaultValue> + <defaultValue>4</defaultValue> </leafNode> + #include <include/interface/interface-parameters-flowlabel.xml.i> <leafNode name="hoplimit"> <properties> <help>Hoplimit</help> diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in index 8c76ab60b..7a286eaf2 100644 --- a/interface-definitions/interfaces-vxlan.xml.in +++ b/interface-definitions/interfaces-vxlan.xml.in @@ -16,9 +16,9 @@ </valueHelp> </properties> <children> - #include <include/address-ipv4-ipv6.xml.i> - #include <include/interface-description.xml.i> - #include <include/interface-disable.xml.i> + #include <include/interface/address-ipv4-ipv6.xml.i> + #include <include/interface/interface-description.xml.i> + #include <include/interface/interface-disable.xml.i> <leafNode name="group"> <properties> <help>Multicast group address for VXLAN interface</help> @@ -35,28 +35,47 @@ </constraint> </properties> </leafNode> - #include <include/interface-ipv4-options.xml.i> - #include <include/interface-ipv6-options.xml.i> - #include <include/source-address-ipv4.xml.i> - #include <include/source-interface.xml.i> - #include <include/interface-mac.xml.i> - #include <include/interface-mtu-1200-16000.xml.i> - <leafNode name="remote"> + #include <include/interface/interface-ipv4-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> + #include <include/interface/interface-mac.xml.i> + #include <include/interface/interface-mtu-1200-16000.xml.i> + <leafNode name="mtu"> + <defaultValue>1450</defaultValue> + </leafNode> + <node name="parameters"> <properties> - <help>Remote address of VXLAN tunnel</help> - <valueHelp> - <format>ipv4</format> - <description>Remote IPv4 address of VXLAN tunnel</description> - </valueHelp> - <valueHelp> - <format>ipv6</format> - <description>Remote IPv6 address of VXLAN tunnel</description> - </valueHelp> - <constraint> - <validator name="ip-address"/> - </constraint> + <help>VXLAN tunnel parameters</help> </properties> - </leafNode> + <children> + <node name="ip"> + <properties> + <help>IPv4 specific tunnel parameters</help> + </properties> + <children> + #include <include/interface/interface-parameters-dont-fragment.xml.i> + #include <include/interface/interface-parameters-tos.xml.i> + #include <include/interface/interface-parameters-ttl.xml.i> + <leafNode name="ttl"> + <defaultValue>16</defaultValue> + </leafNode> + </children> + </node> + <node name="ipv6"> + <properties> + <help>IPv6 specific tunnel parameters</help> + </properties> + <children> + #include <include/interface/interface-parameters-flowlabel.xml.i> + </children> + </node> + <leafNode name="nolearning"> + <properties> + <help>Do not add unknown addresses into forwarding database</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> <leafNode name="port"> <properties> <help>Destination port of VXLAN tunnel (default: 8472)</help> @@ -70,18 +89,10 @@ </properties> <defaultValue>8472</defaultValue> </leafNode> - <leafNode name="vni"> - <properties> - <help>Virtual Network Identifier</help> - <valueHelp> - <format>0-16777214</format> - <description>VXLAN virtual network identifier</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-16777214"/> - </constraint> - </properties> - </leafNode> + #include <include/source-address-ipv4-ipv6.xml.i> + #include <include/source-interface.xml.i> + #include <include/interface/tunnel-remote.xml.i> + #include <include/vni.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in index 92c9f510c..378251fed 100644 --- a/interface-definitions/interfaces-wireguard.xml.in +++ b/interface-definitions/interfaces-wireguard.xml.in @@ -16,14 +16,17 @@ </valueHelp> </properties> <children> - #include <include/address-ipv4-ipv6.xml.i> - #include <include/interface-description.xml.i> - #include <include/interface-disable.xml.i> - #include <include/interface-vrf.xml.i> + #include <include/interface/address-ipv4-ipv6.xml.i> + #include <include/interface/interface-description.xml.i> + #include <include/interface/interface-disable.xml.i> + #include <include/interface/interface-vrf.xml.i> #include <include/port-number.xml.i> - #include <include/interface-mtu-68-16000.xml.i> - #include <include/interface-ipv4-options.xml.i> - #include <include/interface-ipv6-options.xml.i> + #include <include/interface/interface-mtu-68-16000.xml.i> + <leafNode name="mtu"> + <defaultValue>1420</defaultValue> + </leafNode> + #include <include/interface/interface-ipv4-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> <leafNode name="fwmark"> <properties> <help>A 32-bit fwmark value set on all outgoing packets</help> @@ -55,12 +58,7 @@ <constraintErrorMessage>peer alias too long (limit 100 characters)</constraintErrorMessage> </properties> <children> - <leafNode name="disable"> - <properties> - <help>disables peer</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="pubkey"> <properties> <help>base64 encoded public key</help> diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in index 86f529278..aaeb285f1 100644 --- a/interface-definitions/interfaces-wireless.xml.in +++ b/interface-definitions/interfaces-wireless.xml.in @@ -16,7 +16,7 @@ </valueHelp> </properties> <children> - #include <include/address-ipv4-ipv6-dhcp.xml.i> + #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> <node name="capabilities"> <properties> <help>HT and VHT capabilities for your card</help> @@ -464,34 +464,34 @@ <constraintErrorMessage>Invalid ISO/IEC 3166-1 Country Code</constraintErrorMessage> </properties> </leafNode> - #include <include/interface-description.xml.i> - #include <include/dhcp-options.xml.i> - #include <include/dhcpv6-options.xml.i> + #include <include/interface/interface-description.xml.i> + #include <include/interface/dhcp-options.xml.i> + #include <include/interface/dhcpv6-options.xml.i> <leafNode name="disable-broadcast-ssid"> <properties> <help>Disable broadcast of SSID from access-point</help> <valueless/> </properties> </leafNode> - #include <include/interface-disable-link-detect.xml.i> - #include <include/interface-disable.xml.i> - #include <include/interface-vrf.xml.i> + #include <include/interface/interface-disable-link-detect.xml.i> + #include <include/interface/interface-disable.xml.i> + #include <include/interface/interface-vrf.xml.i> <leafNode name="expunge-failing-stations"> <properties> <help>Disassociate stations based on excessive transmission failures</help> <valueless/> </properties> </leafNode> - #include <include/interface-ipv4-options.xml.i> - #include <include/interface-ipv6-options.xml.i> - #include <include/interface-hw-id.xml.i> + #include <include/interface/interface-ipv4-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> + #include <include/interface/interface-hw-id.xml.i> <leafNode name="isolate-stations"> <properties> <help>Isolate stations on the AP so they cannot see each other</help> <valueless/> </properties> </leafNode> - #include <include/interface-mac.xml.i> + #include <include/interface/interface-mac.xml.i> <leafNode name="max-stations"> <properties> <help>Maximum number of wireless radio stations. Excess stations will be rejected upon authentication request.</help> @@ -722,7 +722,7 @@ <constraintErrorMessage>Invalid WPA pass phrase, must be 8 to 63 printable characters!</constraintErrorMessage> </properties> </leafNode> - #include <include/radius-server.xml.i> + #include <include/radius-server-ipv4.xml.i> <node name="radius"> <children> <tagNode name="server"> @@ -775,8 +775,8 @@ </properties> <defaultValue>monitor</defaultValue> </leafNode> - #include <include/vif.xml.i> - #include <include/vif-s.xml.i> + #include <include/interface/vif.xml.i> + #include <include/interface/vif-s.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/interfaces-wirelessmodem.xml.in b/interface-definitions/interfaces-wirelessmodem.xml.in index 969ff0014..25ac2d6e0 100644 --- a/interface-definitions/interfaces-wirelessmodem.xml.in +++ b/interface-definitions/interfaces-wirelessmodem.xml.in @@ -42,9 +42,9 @@ </leafNode> </children> </node> - #include <include/interface-description.xml.i> - #include <include/interface-disable.xml.i> - #include <include/interface-vrf.xml.i> + #include <include/interface/interface-description.xml.i> + #include <include/interface/interface-disable.xml.i> + #include <include/interface/interface-vrf.xml.i> <leafNode name="device"> <properties> <help>Serial device </help> @@ -65,17 +65,17 @@ </constraint> </properties> </leafNode> - #include <include/interface-disable-link-detect.xml.i> - #include <include/interface-mtu-68-16000.xml.i> - #include <include/interface-ipv4-options.xml.i> - #include <include/interface-ipv6-options.xml.i> + #include <include/interface/interface-disable-link-detect.xml.i> + #include <include/interface/interface-mtu-68-16000.xml.i> + #include <include/interface/interface-ipv4-options.xml.i> + #include <include/interface/interface-ipv6-options.xml.i> <leafNode name="no-peer-dns"> <properties> <help>Do not use peer supplied DNS server information</help> <valueless/> </properties> </leafNode> - #include <include/interface-dial-on-demand.xml.i> + #include <include/interface/interface-dial-on-demand.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/lldp.xml.in b/interface-definitions/lldp.xml.in index 950b267ef..9fdffcea1 100644 --- a/interface-definitions/lldp.xml.in +++ b/interface-definitions/lldp.xml.in @@ -25,12 +25,7 @@ </completionHelp> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Disable lldp on this interface</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <node name="location"> <properties> <help>LLDP-MED location data [REQUIRED]</help> diff --git a/interface-definitions/nat.xml.in b/interface-definitions/nat.xml.in index 00aaddb17..3cff8abc9 100644 --- a/interface-definitions/nat.xml.in +++ b/interface-definitions/nat.xml.in @@ -56,78 +56,6 @@ </tagNode> </children> </node> - <node name="nptv6"> - <properties> - <help>IPv6-to-IPv6 Network Prefix Translation Settings</help> - </properties> - <children> - <tagNode name="rule"> - <properties> - <help>NPTv6 rule number</help> - <valueHelp> - <format>1-999999</format> - <description>Number for this rule</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-999999"/> - </constraint> - <constraintErrorMessage>NAT rule number must be between 1 and 999999</constraintErrorMessage> - </properties> - <children> - <leafNode name="description"> - <properties> - <help>Rule description</help> - </properties> - </leafNode> - <leafNode name="disable"> - <properties> - <help>Disable NAT rule</help> - <valueless/> - </properties> - </leafNode> - #include <include/nat-interface.xml.i> - <node name="source"> - <properties> - <help>IPv6 source prefix options</help> - </properties> - <children> - <leafNode name="prefix"> - <properties> - <help>IPv6 prefix to be translated</help> - <valueHelp> - <format>ipv6net</format> - <description>IPv6 prefix</description> - </valueHelp> - <constraint> - <validator name="ipv6-prefix"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <node name="translation"> - <properties> - <help>Translated IPv6 prefix options</help> - </properties> - <children> - <leafNode name="prefix"> - <properties> - <help>IPv6 prefix to translate to</help> - <valueHelp> - <format>ipv6net</format> - <description>IPv6 prefix</description> - </valueHelp> - <constraint> - <validator name="ipv6-prefix"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - </children> - </tagNode> - </children> - </node> <node name="source"> <properties> <help>Source NAT settings</help> diff --git a/interface-definitions/nat66.xml.in b/interface-definitions/nat66.xml.in new file mode 100644 index 000000000..d5e1226f9 --- /dev/null +++ b/interface-definitions/nat66.xml.in @@ -0,0 +1,205 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="nat66" owner="${vyos_conf_scripts_dir}/nat66.py"> + <properties> + <help>IPv6-to-IPv6 Network Prefix Translation (NAT66/NPT) Settings</help> + <priority>220</priority> + </properties> + <children> + <node name="source"> + <properties> + <help>Prefix mapping of IPv6 source address translation</help> + </properties> + <children> + <tagNode name="rule"> + <properties> + <help>Source NAT66 rule number</help> + <valueHelp> + <format>1-999999</format> + <description>Number for this rule</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-999999"/> + </constraint> + <constraintErrorMessage>NAT66 rule number must be between 1 and 999999</constraintErrorMessage> + </properties> + <children> + <leafNode name="description"> + <properties> + <help>Rule description</help> + </properties> + </leafNode> + <leafNode name="disable"> + <properties> + <help>Disable NAT66 rule</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="log"> + <properties> + <help>NAT66 rule logging</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="outbound-interface"> + <properties> + <help>Outbound interface of NAT66 traffic</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + </properties> + </leafNode> + <node name="source"> + <properties> + <help>IPv6 source prefix options</help> + </properties> + <children> + <leafNode name="prefix"> + <properties> + <help>IPv6 prefix to be translated</help> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 prefix</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + <node name="translation"> + <properties> + <help>Translated IPv6 address options</help> + </properties> + <children> + <leafNode name="address"> + <properties> + <help>IPv6 address to translate to</help> + <completionHelp> + <list>masquerade</list> + </completionHelp> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address</description> + </valueHelp> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 prefix</description> + </valueHelp> + <valueHelp> + <format>masquerade</format> + <description>NAT to the primary address of outbound-interface</description> + </valueHelp> + <constraint> + <validator name="ipv6-address"/> + <validator name="ipv6-prefix"/> + <regex>(masquerade)</regex> + </constraint> + </properties> + </leafNode> + </children> + </node> + </children> + </tagNode> + </children> + </node> + <node name="destination"> + <properties> + <help>Prefix mapping for IPv6 destination address translation</help> + </properties> + <children> + <tagNode name="rule"> + <properties> + <help>Destination NAT66 rule number</help> + <valueHelp> + <format>1-999999</format> + <description>Number for this rule</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-999999"/> + </constraint> + <constraintErrorMessage>NAT66 rule number must be between 1 and 999999</constraintErrorMessage> + </properties> + <children> + <leafNode name="description"> + <properties> + <help>Rule description</help> + </properties> + </leafNode> + <leafNode name="disable"> + <properties> + <help>Disable NAT66 rule</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="log"> + <properties> + <help>NAT66 rule logging</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="inbound-interface"> + <properties> + <help>Inbound interface of NAT66 traffic</help> + <completionHelp> + <list>any</list> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + </properties> + </leafNode> + <node name="destination"> + <properties> + <help>IPv6 destination prefix options</help> + </properties> + <children> + <leafNode name="address"> + <properties> + <help>IPv6 address or prefix to be translated</help> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address</description> + </valueHelp> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 prefix</description> + </valueHelp> + <constraint> + <validator name="ipv6-address"/> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + <node name="translation"> + <properties> + <help>Translated IPv6 address options</help> + </properties> + <children> + <leafNode name="address"> + <properties> + <help>IPv6 address or prefix to translate to</help> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address</description> + </valueHelp> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 prefix</description> + </valueHelp> + <constraint> + <validator name="ipv6-address"/> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + </children> + </tagNode> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/interface-definitions/ntp.xml.in b/interface-definitions/ntp.xml.in index b939d9dc6..2bfac900b 100644 --- a/interface-definitions/ntp.xml.in +++ b/interface-definitions/ntp.xml.in @@ -5,8 +5,8 @@ <children> <node name="ntp" owner="${vyos_conf_scripts_dir}/ntp.py"> <properties> - <priority>400</priority> <help>Network Time Protocol (NTP) configuration</help> + <priority>900</priority> </properties> <children> <tagNode name="server"> @@ -82,7 +82,7 @@ </children> </node> #include <include/listen-address.xml.i> - #include <include/interface-vrf.xml.i> + #include <include/interface/interface-vrf.xml.i> </children> </node> </children> diff --git a/interface-definitions/policy-lists.xml.in b/interface-definitions/policy-lists.xml.in new file mode 100644 index 000000000..a0bea2ce2 --- /dev/null +++ b/interface-definitions/policy-lists.xml.in @@ -0,0 +1,1266 @@ +<?xml version="1.0"?> +<!-- Policy access|prefix|route-map lists --> +<interfaceDefinition> + <node name="npolicy" owner="${vyos_conf_scripts_dir}/policy-lists.py"> + <properties> + <help>Routing policy</help> + </properties> + <children> + <tagNode name="access-list"> + <properties> + <help>IP access-list filter</help> + <priority>470</priority> + <valueHelp> + <format>u32:1-99</format> + <description>IP standard access list</description> + </valueHelp> + <valueHelp> + <format>u32:100-199</format> + <description>IP extended access list</description> + </valueHelp> + <valueHelp> + <format>u32:1300-1999</format> + <description>IP standard access list (expanded range)</description> + </valueHelp> + <valueHelp> + <format>u32:2000-2699</format> + <description>IP extended access list (expanded range)</description> + </valueHelp> + </properties> + <children> + #include <include/policy-list-description.xml.i> + <tagNode name="rule"> + <properties> + <help>Rule for this access-list</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Access-list rule number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <children> + #include <include/policy-list-action.xml.i> + #include <include/policy-list-rule-description.xml.i> + <node name="destination"> + <properties> + <help>Destination network or address</help> + </properties> + <children> + <leafNode name="any"> + <properties> + <help>Any IP address to match</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="host"> + <properties> + <help>Single host IP address to match</help> + <valueHelp> + <format>ipv4</format> + <description>Host address to match</description> + </valueHelp> + <constraint> + <validator name="ipv4-host"/> + </constraint> + </properties> + </leafNode> + <leafNode name="inverse-mask"> + <properties> + <help>Network/netmask to match (requires network be defined)</help> + <valueHelp> + <format>ipv4</format> + <description>Inverse-mask to match</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + </leafNode> + <leafNode name="network"> + <properties> + <help>Network/netmask to match (requires inverse-mask be defined)</help> + <valueHelp> + <format>ipv4net</format> + <description>Inverse-mask to match</description> + </valueHelp> + <constraint> + <validator name="ip-prefix"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + <node name="source"> + <properties> + <help>Source network or address to match</help> + </properties> + <children> + <leafNode name="any"> + <properties> + <help>Any IP address to match</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="host"> + <properties> + <help>Single host IP address to match</help> + <valueHelp> + <format>ipv4</format> + <description>Host address to match</description> + </valueHelp> + <constraint> + <validator name="ipv4-host"/> + </constraint> + </properties> + </leafNode> + <leafNode name="inverse-mask"> + <properties> + <help>Network/netmask to match (requires network be defined)</help> + <valueHelp> + <format>ipv4</format> + <description>Inverse-mask to match</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + </leafNode> + <leafNode name="network"> + <properties> + <help>Network/netmask to match (requires inverse-mask be defined)</help> + <valueHelp> + <format>ipv4net</format> + <description>Inverse-mask to match</description> + </valueHelp> + <constraint> + <validator name="ip-prefix"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + </children> + </tagNode> + </children> + </tagNode> + <tagNode name="access-list6"> + <properties> + <help>IPv6 access-list filter</help> + <priority>470</priority> + <valueHelp> + <format>txt</format> + <description>Name of IPv6 access-list</description> + </valueHelp> + </properties> + <children> + #include <include/policy-list-description.xml.i> + <tagNode name="rule"> + <properties> + <help>Rule for this access-list6</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Access-list6 rule number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <children> + #include <include/policy-list-action.xml.i> + #include <include/policy-list-rule-description.xml.i> + <node name="source"> + <properties> + <help>Source IPv6 network to match</help> + </properties> + <children> + <leafNode name="any"> + <properties> + <help>Any IP address to match</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="exact-match"> + <properties> + <help>Exact match of the network prefixes</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="network"> + <properties> + <help>Network/netmask to match</help> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 address and prefix length</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + </children> + </tagNode> + </children> + </tagNode> + <tagNode name="as-path-list"> + <properties> + <help>Border Gateway Protocol (BGP) autonomous system path filter</help> + <priority>470</priority> + <valueHelp> + <format>txt</format> + <description>AS path list name</description> + </valueHelp> + </properties> + <children> + #include <include/policy-list-description.xml.i> + <tagNode name="rule"> + <properties> + <help>Rule for this as-path-list</help> + <valueHelp> + <format>u32:1-65535</format> + <description>AS path list rule number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <children> + #include <include/policy-list-action.xml.i> + #include <include/policy-list-rule-description.xml.i> + <leafNode name="regex"> + <properties> + <help>Regular expression to match against an AS path</help> + <valueHelp> + <format><asn></format> + <description>AS path regular expression (ex: "64501 64502")</description> + </valueHelp> + </properties> + </leafNode> + </children> + </tagNode> + </children> + </tagNode> + <tagNode name="community-list"> + <properties> + <help>Border Gateway Protocol (BGP) autonomous system path filter</help> + <priority>470</priority> + <valueHelp> + <format>txt</format> + <description>Border Gateway Protocol (BGP) community-list filter</description> + </valueHelp> + </properties> + <children> + #include <include/policy-list-description.xml.i> + <tagNode name="rule"> + <properties> + <help>Rule for this BGP community list</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Community-list rule number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <children> + #include <include/policy-list-action.xml.i> + #include <include/policy-list-rule-description.xml.i> + <leafNode name="regex"> + <properties> + <help>Regular expression to match against a community list</help> + <valueHelp> + <format><aa:nn></format> + <description>Community list regular expression or one of: internet, local-AS, no-advertise, no-export</description> + </valueHelp> + </properties> + </leafNode> + </children> + </tagNode> + </children> + </tagNode> + <tagNode name="extcommunity-list"> + <properties> + <help>Border Gateway Protocol (BGP) extended community-list filter</help> + <priority>490</priority> + <valueHelp> + <format>txt</format> + <description>Border Gateway Protocol (BGP) extended community-list filter</description> + </valueHelp> + </properties> + <children> + #include <include/policy-list-description.xml.i> + <tagNode name="rule"> + <properties> + <help>Rule for this BGP extended community list</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Extended community-list rule number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <children> + #include <include/policy-list-action.xml.i> + #include <include/policy-list-rule-description.xml.i> + <leafNode name="regex"> + <properties> + <help>Regular expression to match against an extended community list</help> + <valueHelp> + <format><aa:nn:nn></format> + <description>Extended community list regular expression</description> + </valueHelp> + <valueHelp> + <format><rt aa:nn:nn></format> + <description>Extended community list regular expression</description> + </valueHelp> + <valueHelp> + <format><soo aa:nn></format> + <description>Extended community list regular expression</description> + </valueHelp> + </properties> + </leafNode> + </children> + </tagNode> + </children> + </tagNode> + <tagNode name="large-community-list"> + <properties> + <help>Border Gateway Protocol (BGP) large-community-list filter</help> + <priority>470</priority> + <valueHelp> + <format>txt</format> + <description>Border Gateway Protocol (BGP) large-community-list filter</description> + </valueHelp> + </properties> + <children> + #include <include/policy-list-description.xml.i> + <tagNode name="rule"> + <properties> + <help>Rule for this BGP extended community list</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Large community-list rule number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <children> + #include <include/policy-list-action.xml.i> + #include <include/policy-list-rule-description.xml.i> + <leafNode name="regex"> + <properties> + <help>Regular expression to match against a large community list</help> + <valueHelp> + <format><aa:nn:nn></format> + <description>Large community list regular expression</description> + </valueHelp> + </properties> + </leafNode> + </children> + </tagNode> + </children> + </tagNode> + <tagNode name="prefix-list"> + <properties> + <help>IP prefix-list filter</help> + <priority>470</priority> + <valueHelp> + <format>txt</format> + <description>Prefix list name</description> + </valueHelp> + </properties> + <children> + #include <include/policy-list-description.xml.i> + <tagNode name="rule"> + <properties> + <help>Rule for this prefix-list</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Prefix-list rule number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <children> + #include <include/policy-list-action.xml.i> + #include <include/policy-list-rule-description.xml.i> + <leafNode name="ge"> + <properties> + <help>Prefix length to match a netmask greater than or equal to it</help> + <valueHelp> + <format>u32:0-32</format> + <description>Netmask greater than length</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-32"/> + </constraint> + </properties> + </leafNode> + <leafNode name="le"> + <properties> + <help>Prefix length to match a netmask less than or equal to it</help> + <valueHelp> + <format>u32:0-32</format> + <description>Netmask less than length</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-32"/> + </constraint> + </properties> + </leafNode> + <leafNode name="prefix"> + <properties> + <help>Prefix to match</help> + <valueHelp> + <format>ipv4net</format> + <description>Prefix to match against</description> + </valueHelp> + <constraint> + <validator name="ip-prefix"/> + </constraint> + </properties> + </leafNode> + </children> + </tagNode> + </children> + </tagNode> + <tagNode name="prefix-list6"> + <properties> + <help>IPv6 prefix-list filter</help> + <priority>470</priority> + <valueHelp> + <format>txt</format> + <description>Prefix list name</description> + </valueHelp> + </properties> + <children> + #include <include/policy-list-description.xml.i> + <tagNode name="rule"> + <properties> + <help>Rule for this prefix-list6</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Prefix-list rule number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <children> + #include <include/policy-list-action.xml.i> + #include <include/policy-list-rule-description.xml.i> + <leafNode name="ge"> + <properties> + <help>Prefix length to match a netmask greater than or equal to it</help> + <valueHelp> + <format>u32:0-128</format> + <description>Netmask greater than length</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-128"/> + </constraint> + </properties> + </leafNode> + <leafNode name="le"> + <properties> + <help>Prefix length to match a netmask less than or equal to it</help> + <valueHelp> + <format>u32:0-128</format> + <description>Netmask less than length</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-128"/> + </constraint> + </properties> + </leafNode> + <leafNode name="prefix"> + <properties> + <help>Prefix to match</help> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 prefix</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + </leafNode> + </children> + </tagNode> + </children> + </tagNode> + <tagNode name="route-map"> + <properties> + <help>IP route-map</help> + <priority>470</priority> + <valueHelp> + <format>txt</format> + <description>Route map name</description> + </valueHelp> + </properties> + <children> + #include <include/policy-list-description.xml.i> + <tagNode name="rule"> + <properties> + <help>Rule for this route-map</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Route-map rule number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <children> + #include <include/policy-list-action.xml.i> + <leafNode name="call"> + <properties> + <help>Call another route-map on match</help> + <valueHelp> + <format>txt</format> + <description>Route map name</description> + </valueHelp> + <completionHelp> + <path>policy route-map</path> + </completionHelp> + </properties> + </leafNode> + <leafNode name="continue"> + <properties> + <help>Jump to a different rule in this route-map on a match</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Rule number</description> + </valueHelp> + </properties> + </leafNode> + #include <include/policy-list-rule-description.xml.i> + <node name="match"> + <properties> + <help>Route parameters to match</help> + </properties> + <children> + <leafNode name="as-path"> + <properties> + <help>BGP as-path-list to match</help> + <completionHelp> + <path>policy as-path-list</path> + </completionHelp> + </properties> + </leafNode> + <node name="community"> + <properties> + <help>BGP community-list to match</help> + </properties> + <children> + <leafNode name="community-list"> + <properties> + <help>BGP community-list to match</help> + <completionHelp> + <path>policy community-list</path> + </completionHelp> + </properties> + </leafNode> + <leafNode name="exact-match"> + <properties> + <help>Community-list to exactly match</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + <leafNode name="extcommunity"> + <properties> + <help>BGP extended community to match</help> + <completionHelp> + <path>policy extcommunity-list</path> + </completionHelp> + </properties> + </leafNode> + <leafNode name="interface"> + <properties> + <help>First hop interface of a route to match</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + </properties> + </leafNode> + <node name="ip"> + <properties> + <help>IP prefix parameters to match</help> + </properties> + <children> + <node name="address"> + <properties> + <help>IP address of route to match</help> + </properties> + <children> + <leafNode name="access-list"> + <properties> + <help>IP access-list to match</help> + <valueHelp> + <format>u32:1-99</format> + <description>IP standard access list</description> + </valueHelp> + <valueHelp> + <format>u32:100-199</format> + <description>IP extended access list</description> + </valueHelp> + <valueHelp> + <format>u32:1300-1999</format> + <description>IP standard access list (expanded range)</description> + </valueHelp> + <valueHelp> + <format>u32:2000-2699</format> + <description>IP extended access list (expanded range)</description> + </valueHelp> + </properties> + </leafNode> + <leafNode name="prefix-list"> + <properties> + <help>IP prefix-list to match</help> + <completionHelp> + <path>policy prefix-list</path> + </completionHelp> + </properties> + </leafNode> + </children> + </node> + <!-- T3304 but it overwrite node nexthop + <leafNode name="nexthop"> + <properties> + <help>IP next-hop of route to match</help> + <valueHelp> + <format>ipv4</format> + <description>Next-hop IPv4 router address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + </leafNode> --> + <node name="nexthop"> + <properties> + <help>IP next-hop of route to match</help> + <valueHelp> + <format>ipv4</format> + <description>Next-hop IPv4 router address</description> + </valueHelp> + </properties> + <children> + <leafNode name="access-list"> + <properties> + <help>IP access-list to match</help> + <valueHelp> + <format>u32:1-99</format> + <description>IP standard access list</description> + </valueHelp> + <valueHelp> + <format>u32:100-199</format> + <description>IP extended access list</description> + </valueHelp> + <valueHelp> + <format>u32:1300-1999</format> + <description>IP standard access list (expanded range)</description> + </valueHelp> + <valueHelp> + <format>u32:2000-2699</format> + <description>IP extended access list (expanded range)</description> + </valueHelp> + </properties> + </leafNode> + <leafNode name="prefix-list"> + <properties> + <help>IP prefix-list to match</help> + <completionHelp> + <path>policy prefix-list</path> + </completionHelp> + </properties> + </leafNode> + </children> + </node> + <node name="route-source"> + <properties> + <help>test</help> + </properties> + <children> + <leafNode name="access-list"> + <properties> + <help>IP access-list to match</help> + <valueHelp> + <format>u32:1-99</format> + <description>IP standard access list</description> + </valueHelp> + <valueHelp> + <format>u32:100-199</format> + <description>IP extended access list</description> + </valueHelp> + <valueHelp> + <format>u32:1300-1999</format> + <description>IP standard access list (expanded range)</description> + </valueHelp> + <valueHelp> + <format>u32:2000-2699</format> + <description>IP extended access list (expanded range)</description> + </valueHelp> + </properties> + </leafNode> + <leafNode name="prefix-list"> + <properties> + <help>IP prefix-list to match</help> + <completionHelp> + <path>policy prefix-list</path> + </completionHelp> + </properties> + </leafNode> + </children> + </node> + </children> + </node> + <node name="ipv6"> + <properties> + <help>IPv6 prefix parameters to match</help> + </properties> + <children> + <node name="address"> + <properties> + <help>IPv6 address of route to match</help> + </properties> + <children> + <leafNode name="access-list"> + <properties> + <help>IPv6 access-list to match</help> + <valueHelp> + <format>txt</format> + <description>IPV6 access list name</description> + </valueHelp> + <completionHelp> + <path>policy access-list6</path> + </completionHelp> + </properties> + </leafNode> + <leafNode name="prefix-list"> + <properties> + <help>IPv6 prefix-list to match</help> + <completionHelp> + <path>policy prefix-list6</path> + </completionHelp> + </properties> + </leafNode> + </children> + </node> + <leafNode name="nexthop"> + <properties> + <help>IPv6 next-hop of route to match</help> + <valueHelp> + <format>ipv4</format> + <description>Peer IP address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + <node name="large-community"> + <properties> + <help>Match BGP large communities</help> + </properties> + <children> + <leafNode name="large-community-list"> + <properties> + <help>BGP large-community-list to match</help> + <completionHelp> + <path>policy large-community-list</path> + </completionHelp> + </properties> + </leafNode> + </children> + </node> + <leafNode name="local-preference"> + <properties> + <help>local-preference_help</help> + <valueHelp> + <format>u32:0-4294967295</format> + <description>Local Preference</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + </constraint> + </properties> + </leafNode> + <leafNode name="metric"> + <properties> + <help>Metric of route to match</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Route metric</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + </leafNode> + <leafNode name="origin"> + <properties> + <help>Border Gateway Protocol (BGP) origin code to match</help> + <completionHelp> + <list>egp igp incomplete</list> + </completionHelp> + <valueHelp> + <format>egp</format> + <description>Exterior gateway protocol origin</description> + </valueHelp> + <valueHelp> + <format>igp</format> + <description>Interior gateway protocol origin</description> + </valueHelp> + <valueHelp> + <format>incomplete</format> + <description>Incomplete origin</description> + </valueHelp> + <constraint> + <regex>^(egp|igp|incomplete)$</regex> + </constraint> + </properties> + </leafNode> + <leafNode name="peer"> + <properties> + <help>Peer address to match</help> + <valueHelp> + <format>ipv4</format> + <description>Peer IP address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + </leafNode> + <leafNode name="rpki"> + <properties> + <help>Match RPKI validation result</help> + <completionHelp> + <list>invalid notfound valid</list> + </completionHelp> + <valueHelp> + <format>invalid</format> + <description>Match invalid entries</description> + </valueHelp> + <valueHelp> + <format>notfound</format> + <description>Match notfound entries</description> + </valueHelp> + <valueHelp> + <format>valid</format> + <description>Match valid entries</description> + </valueHelp> + <constraint> + <regex>^(invalid|notfound|valid)$</regex> + </constraint> + </properties> + </leafNode> + <leafNode name="tag"> + <properties> + <help>Route tag to match</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Route tag</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + <node name="on-match"> + <properties> + <help>Exit policy on matches</help> + </properties> + <children> + <leafNode name="goto"> + <properties> + <help>Rule number to goto on match</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Rule number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + </leafNode> + <leafNode name="next"> + <properties> + <help>Next sequence number to goto on match</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + <node name="set"> + <properties> + <help>Route parameters</help> + </properties> + <children> + <node name="aggregator"> + <properties> + <help>Border Gateway Protocol (BGP) aggregator attribute</help> + </properties> + <children> + <leafNode name="as"> + <properties> + <help>AS number of an aggregation</help> + <valueHelp> + <format>u32:1-4294967295</format> + <description>Rule number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967295"/> + </constraint> + </properties> + </leafNode> + <leafNode name="ip"> + <properties> + <help>IP address of an aggregation</help> + <valueHelp> + <format>ipv4</format> + <description>IP address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + <leafNode name="as-path-exclude"> + <properties> + <help>Remove ASN(s) from a Border Gateway Protocol (BGP) AS-path attribute</help> + <valueHelp> + <format>txt</format> + <description>BGP AS path exclude string (ex: "456 64500 45001")</description> + </valueHelp> + </properties> + </leafNode> + <leafNode name="as-path-prepend"> + <properties> + <help>as-path-prepend_help</help> + <valueHelp> + <format>txt</format> + <description>BGP AS path prepend string (ex: "64501 64501")</description> + </valueHelp> + </properties> + </leafNode> + <leafNode name="atomic-aggregate"> + <properties> + <help>Border Gateway Protocol (BGP) atomic aggregate attribute</help> + </properties> + </leafNode> + <leafNode name="bgp-extcommunity-rt"> + <properties> + <help>Set route target value</help> + <valueHelp> + <format><aa:nn></format> + <description>ExtCommunity in format: asn:value</description> + </valueHelp> + </properties> + </leafNode> + <node name="comm-list"> + <properties> + <help>Border Gateway Protocol (BGP) communities matching a community-list</help> + </properties> + <children> + <leafNode name="comm-list"> + <properties> + <help>BGP communities with a community-list</help> + <valueHelp> + <format>txt</format> + <description>BGP communities with a community-list</description> + </valueHelp> + </properties> + </leafNode> + <leafNode name="delete"> + <properties> + <help>Delete BGP communities matching the community-list</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + <leafNode name="community"> + <properties> + <help>community_help</help> + <completionHelp> + <list>local-AS no-advertise no-export internet additive none</list> + </completionHelp> + <valueHelp> + <format><aa:nn></format> + <description>Community in 4 octet AS:value format</description> + </valueHelp> + <valueHelp> + <format>local-AS</format> + <description>local-AS</description> + </valueHelp> + <valueHelp> + <format>no-advertise</format> + <description>no-advertise</description> + </valueHelp> + <valueHelp> + <format>no-export</format> + <description>no-export</description> + </valueHelp> + <valueHelp> + <format>internet</format> + <description>internet</description> + </valueHelp> + <valueHelp> + <format>additive</format> + <description>additive</description> + </valueHelp> + <valueHelp> + <format>none</format> + <description>none</description> + </valueHelp> + <!-- Need to add properly validator + <constraint> + <regex>^(local-AS|no-advertise|no-export|internet|additive|none)$</regex> + </constraint> --> + </properties> + </leafNode> + <leafNode name="distance"> + <properties> + <help>Locally significant administrative distance</help> + <valueHelp> + <format>u32:0-255</format> + <description>Distance value</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-255"/> + </constraint> + </properties> + </leafNode> + <leafNode name="extcommunity-rt"> + <properties> + <help>Set route target value</help> + <valueHelp> + <format>txt</format> + <description>ASN:nn_or_IP_address:nn VPN extended community</description> + </valueHelp> + </properties> + </leafNode> + <leafNode name="extcommunity-soo"> + <properties> + <help>Set Site of Origin value</help> + <valueHelp> + <format>txt</format> + <description>ASN:nn_or_IP_address:nn VPN extended community</description> + </valueHelp> + </properties> + </leafNode> + <leafNode name="ip-next-hop"> + <properties> + <help>Nexthop IP address</help> + <valueHelp> + <format>ipv4</format> + <description>IP address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + </leafNode> + <node name="ipv6-next-hop"> + <properties> + <help>Nexthop IPv6 address</help> + </properties> + <children> + <leafNode name="global"> + <properties> + <help>Nexthop IPv6 global address</help> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address and prefix length</description> + </valueHelp> + <constraint> + <validator name="ipv6-address"/> + </constraint> + </properties> + </leafNode> + <leafNode name="local"> + <properties> + <help>Nexthop IPv6 local address</help> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address and prefix length</description> + </valueHelp> + <constraint> + <validator name="ipv6-address"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + <leafNode name="large-community"> + <properties> + <help>Set BGP large community value</help> + <valueHelp> + <format>txt</format> + <description>ASN:nn:mm BGP large community</description> + </valueHelp> + <completionHelp> + <path>policy large-community-list</path> + </completionHelp> + </properties> + </leafNode> + <leafNode name="local-preference"> + <properties> + <help>Border Gateway Protocol (BGP) local preference attribute</help> + <valueHelp> + <format>u32:0-4294967295</format> + <description>Local preference value</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + </constraint> + </properties> + </leafNode> + <leafNode name="metric"> + <properties> + <help>Destination routing protocol metric</help> + <valueHelp> + <format><+/-metric></format> + <description>Add or subtract metric</description> + </valueHelp> + <valueHelp> + <format>u32:0-4294967295</format> + <description>Metric value</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + </constraint> + </properties> + </leafNode> + <leafNode name="metric-type"> + <properties> + <help>Open Shortest Path First (OSPF) external metric-type</help> + <completionHelp> + <list>type-1 type-2</list> + </completionHelp> + <valueHelp> + <format>type-1</format> + <description>OSPF external type 1 metric</description> + </valueHelp> + <valueHelp> + <format>type-2</format> + <description>OSPF external type 2 metric</description> + </valueHelp> + <constraint> + <regex>^(type-1|type-2)$</regex> + </constraint> + </properties> + </leafNode> + <leafNode name="origin"> + <properties> + <help>origin_help</help> + <completionHelp> + <list>igp egp incomplete</list> + </completionHelp> + <valueHelp> + <format>igp</format> + <description>Interior gateway protocol origin</description> + </valueHelp> + <valueHelp> + <format>egp</format> + <description>Exterior gateway protocol origin</description> + </valueHelp> + <valueHelp> + <format>incomplete</format> + <description>Incomplete origin</description> + </valueHelp> + <constraint> + <regex>^(igp|egp|incomplete)$</regex> + </constraint> + </properties> + </leafNode> + <leafNode name="originator-id"> + <properties> + <help>Border Gateway Protocol (BGP) originator ID attribute</help> + <valueHelp> + <format>ipv4</format> + <description>Orignator IP address</description> + </valueHelp> + <constraint> + <validator name="ipv4-host"/> + </constraint> + </properties> + </leafNode> + <leafNode name="src"> + <properties> + <help>Source address for route</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 address</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address</description> + </valueHelp> + <constraint> + <validator name="ipv4-host"/> + <validator name="ipv6-host"/> + </constraint> + </properties> + </leafNode> + <leafNode name="table"> + <properties> + <help>Set prefixes to table</help> + <valueHelp> + <format>u32:1-200</format> + <description>Table value</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-200"/> + </constraint> + </properties> + </leafNode> + <leafNode name="tag"> + <properties> + <help>Tag value for routing protocol</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Tag value</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + </leafNode> + <leafNode name="weight"> + <properties> + <help>Border Gateway Protocol (BGP) weight attribute</help> + <valueHelp> + <format>u32:0-4294967295</format> + <description>BGP weight</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + </children> + </tagNode> + </children> + </tagNode> + </children> + </node> +</interfaceDefinition> diff --git a/interface-definitions/protocols-bfd.xml.in b/interface-definitions/protocols-bfd.xml.in index 8900e7955..cc3c3bf12 100644 --- a/interface-definitions/protocols-bfd.xml.in +++ b/interface-definitions/protocols-bfd.xml.in @@ -11,7 +11,7 @@ <children> <tagNode name="peer"> <properties> - <help>Configures a new BFD peer to listen and talk to</help> + <help>Configures BFD peer to listen and talk to</help> <valueHelp> <format>ipv4</format> <description>BFD peer IPv4 address</description> @@ -26,6 +26,18 @@ </constraint> </properties> <children> + <leafNode name="profile"> + <properties> + <help>Use settings from BFD profile</help> + <completionHelp> + <path>protocols bfd profile</path> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>BFD profile name</description> + </valueHelp> + </properties> + </leafNode> <node name="source"> <properties> <help>Bind listener to specified interface/address, mandatory for IPv6</help> @@ -42,6 +54,9 @@ <leafNode name="address"> <properties> <help>Local address to bind our peer listener to</help> + <completionHelp> + <script>${vyos_completion_dir}/list_local_ips.sh --both</script> + </completionHelp> <valueHelp> <format>ipv4</format> <description>Local IPv4 address used to connect to the peer</description> @@ -58,79 +73,28 @@ </leafNode> </children> </node> - <node name="interval"> - <properties> - <help>Configure timer intervals</help> - </properties> - <children> - <leafNode name="receive"> - <properties> - <help>Minimum interval of receiving control packets</help> - <valueHelp> - <format>10-60000</format> - <description>Interval in milliseconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 10-60000"/> - </constraint> - </properties> - </leafNode> - <leafNode name="transmit"> - <properties> - <help>Minimum interval of transmitting control packets</help> - <valueHelp> - <format>10-60000</format> - <description>Interval in milliseconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 10-60000"/> - </constraint> - </properties> - </leafNode> - <leafNode name="multiplier"> - <properties> - <help>Multiplier to determine packet loss</help> - <valueHelp> - <format>2-255</format> - <description>Remote transmission interval will be multiplied by this value</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 2-255"/> - </constraint> - </properties> - </leafNode> - <leafNode name="echo-interval"> - <properties> - <help>Echo receive transmission interval</help> - <valueHelp> - <format>10-60000</format> - <description>The minimal echo receive transmission interval that this system is capable of handling</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 10-60000"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <leafNode name="shutdown"> - <properties> - <help>Disable this peer</help> - <valueless/> - </properties> - </leafNode> + #include <include/bfd-common.xml.i> <leafNode name="multihop"> <properties> <help>Allow this BFD peer to not be directly connected</help> <valueless/> </properties> </leafNode> - <leafNode name="echo-mode"> - <properties> - <help>Enables the echo transmission mode</help> - <valueless/> - </properties> - </leafNode> + </children> + </tagNode> + <tagNode name="profile"> + <properties> + <help>Configure BFD profile used by individual peer</help> + <valueHelp> + <format>txt</format> + <description>Name of BFD profile</description> + </valueHelp> + <constraint> + <regex>^[-_a-zA-Z0-9]{1,32}$</regex> + </constraint> + </properties> + <children> + #include <include/bfd-common.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/protocols-bgp.xml.in b/interface-definitions/protocols-bgp.xml.in index 27cbc919a..d610f8dff 100644 --- a/interface-definitions/protocols-bgp.xml.in +++ b/interface-definitions/protocols-bgp.xml.in @@ -1,1208 +1,16 @@ <?xml version="1.0"?> -<!-- Border Gateway Protocol (BGP) configuration --> <interfaceDefinition> <node name="protocols"> <children> - <tagNode name="nbgp" owner="${vyos_conf_scripts_dir}/protocols_bgp.py"> + <node name="bgp" owner="${vyos_conf_scripts_dir}/protocols_bgp.py"> <properties> - <help>Border Gateway Protocol (BGP) parameters</help> - <valueHelp> - <format>u32:1-4294967294</format> - <description>AS number</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967294"/> - </constraint> + <help>Border Gateway Protocol (BGP)</help> <priority>820</priority> </properties> <children> - <node name="address-family"> - <properties> - <help>BGP address-family parameters</help> - </properties> - <children> - <node name="ipv4-unicast"> - <properties> - <help>IPv4 BGP settings</help> - </properties> - <children> - <tagNode name="aggregate-address"> - <properties> - <help>BGP aggregate network</help> - <valueHelp> - <format>ipv4net</format> - <description>BGP aggregate network</description> - </valueHelp> - <constraint> - <validator name="ipv4-prefix"/> - </constraint> - </properties> - <children> - #include <include/bgp-afi-aggregate-address.xml.i> - </children> - </tagNode> - <tagNode name="network"> - <properties> - <help>BGP network</help> - <valueHelp> - <format>ipv4net</format> - <description>BGP network</description> - </valueHelp> - <constraint> - <validator name="ipv4-prefix"/> - </constraint> - </properties> - <children> - <leafNode name="backdoor"> - <properties> - <help>Network as a backdoor route</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="route-map"> - <properties> - <help>Route-map to modify route attributes</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> - </leafNode> - </children> - </tagNode> - <node name="redistribute"> - <properties> - <help>Redistribute routes from other protocols into BGP</help> - </properties> - <children> - <node name="connected"> - <properties> - <help>Redistribute connected routes into BGP</help> - </properties> - <children> - #include <include/bgp-afi-redistribute-metric-route-map.xml.i> - </children> - </node> - <node name="kernel"> - <properties> - <help>Redistribute kernel routes into BGP</help> - </properties> - <children> - #include <include/bgp-afi-redistribute-metric-route-map.xml.i> - </children> - </node> - <node name="ospf"> - <properties> - <help>Redistribute OSPF routes into BGP</help> - </properties> - <children> - #include <include/bgp-afi-redistribute-metric-route-map.xml.i> - </children> - </node> - <node name="rip"> - <properties> - <help>Redistribute RIP routes into BGP</help> - </properties> - <children> - #include <include/bgp-afi-redistribute-metric-route-map.xml.i> - </children> - </node> - <node name="static"> - <properties> - <help>Redistribute static routes into BGP</help> - </properties> - <children> - #include <include/bgp-afi-redistribute-metric-route-map.xml.i> - </children> - </node> - <leafNode name="table"> - <properties> - <help>Redistribute non-main Kernel Routing Table</help> - </properties> - </leafNode> - </children> - </node> - </children> - </node> - <node name="ipv6-unicast"> - <properties> - <help>IPv6 BGP settings</help> - </properties> - <children> - <tagNode name="aggregate-address"> - <properties> - <help>BGP aggregate network</help> - <valueHelp> - <format>ipv6net</format> - <description>Aggregate network</description> - </valueHelp> - <constraint> - <validator name="ipv6-prefix"/> - </constraint> - </properties> - <children> - #include <include/bgp-afi-aggregate-address.xml.i> - </children> - </tagNode> - <tagNode name="network"> - <properties> - <help>BGP network</help> - <valueHelp> - <format>ipv6net</format> - <description>Aggregate network</description> - </valueHelp> - <constraint> - <validator name="ipv6-prefix"/> - </constraint> - </properties> - <children> - <leafNode name="path-limit"> - <properties> - <help>AS-path hopcount limit</help> - <valueHelp> - <format>u32:0-255</format> - <description>AS path hop count limit</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-255"/> - </constraint> - </properties> - </leafNode> - <leafNode name="route-map"> - <properties> - <help>Route-map to modify route attributes</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> - </leafNode> - </children> - </tagNode> - <node name="redistribute"> - <properties> - <help>Redistribute routes from other protocols into BGP</help> - </properties> - <children> - <node name="connected"> - <properties> - <help>Redistribute connected routes into BGP</help> - </properties> - <children> - #include <include/bgp-afi-redistribute-metric-route-map.xml.i> - </children> - </node> - <node name="kernel"> - <properties> - <help>Redistribute kernel routes into BGP</help> - </properties> - <children> - #include <include/bgp-afi-redistribute-metric-route-map.xml.i> - </children> - </node> - <node name="ospf"> - <properties> - <help>Redistribute OSPF routes into BGP</help> - </properties> - <children> - #include <include/bgp-afi-redistribute-metric-route-map.xml.i> - </children> - </node> - <node name="rip"> - <properties> - <help>Redistribute RIP routes into BGP</help> - </properties> - <children> - #include <include/bgp-afi-redistribute-metric-route-map.xml.i> - </children> - </node> - <node name="static"> - <properties> - <help>Redistribute static routes into BGP</help> - </properties> - <children> - #include <include/bgp-afi-redistribute-metric-route-map.xml.i> - </children> - </node> - <leafNode name="table"> - <properties> - <help>Redistribute non-main Kernel Routing Table</help> - </properties> - </leafNode> - </children> - </node> - </children> - </node> - </children> - </node> - <node name="maximum-paths"> - <properties> - <help>BGP multipaths</help> - </properties> - <children> - <leafNode name="ebgp"> - <properties> - <help>Maximum ebgp multipaths</help> - <valueHelp> - <format>u32:1-255</format> - <description>EBGP multipaths</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-255"/> - </constraint> - </properties> - </leafNode> - <leafNode name="ibgp"> - <properties> - <help>Maximum ibgp multipaths</help> - <valueHelp> - <format>u32:1-255</format> - <description>EBGP multipaths</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-255"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <tagNode name="neighbor"> - <properties> - <help>BGP neighbor</help> - <valueHelp> - <format>ipv4</format> - <description>BGP neighbor IP address</description> - </valueHelp> - <valueHelp> - <format>ipv6</format> - <description>BGP neighbor IPv6 address</description> - </valueHelp> - <valueHelp> - <format>txt</format> - <description>Interface name</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - <validator name="ipv6-address"/> - <regex>(br|bond|dum|en|eth|gnv|lo|peth|tun|vti|vxlan|wg|wlan)[0-9]+</regex> - </constraint> - </properties> - <children> - <node name="address-family"> - <properties> - <help>Parameters relating to IPv4 or IPv6 routes</help> - </properties> - <children> - #include <include/bgp-neighbor-afi-ipv4-unicast.xml.i> - #include <include/bgp-neighbor-afi-ipv6-unicast.xml.i> - </children> - </node> - <leafNode name="advertisement-interval"> - <properties> - <help>Minimum interval for sending routing updates</help> - <valueHelp> - <format>u32:0-600</format> - <description>Advertisement interval in seconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-600"/> - </constraint> - </properties> - </leafNode> - <node name="bfd"> - <properties> - <help>Enable Bidirectional Forwarding Detection (BFD) support</help> - </properties> - <children> - <leafNode name="check-control-plane-failure"> - <properties> - <help>Allow to write CBIT independence in BFD outgoing packets and read both C-BIT value of BFD and lookup BGP peer status</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <node name="capability"> - <properties> - <help>Advertise capabilities to this neighbor</help> - </properties> - <children> - <leafNode name="dynamic"> - <properties> - <help>Advertise dynamic capability to this neighbor</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="extended-nexthop"> - <properties> - <help>Advertise extended-nexthop capability to this neighbor</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <leafNode name="description"> - <properties> - <help>Description for this neighbor</help> - </properties> - </leafNode> - <leafNode name="disable-capability-negotiation"> - <properties> - <help>Disable capability negotiation with this neighbor</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="disable-connected-check"> - <properties> - <help>Disable check to see if eBGP peer address is a connected route</help> - <valueless/> - </properties> - </leafNode> - <node name="disable-send-community"> - <properties> - <help>Disable sending community attributes to this neighbor (IPv4)</help> - </properties> - <children> - <leafNode name="extended"> - <properties> - <help>Disable sending extended community attributes to this neighbor (IPv4)</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="standard"> - <properties> - <help>Disable sending standard community attributes to this neighbor (IPv4)</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <leafNode name="ebgp-multihop"> - <properties> - <help>Allow this EBGP neighbor to not be on a directly connected network</help> - <valueHelp> - <format>u32:1-255</format> - <description>Number of hops</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-255"/> - </constraint> - </properties> - </leafNode> - <node name="interface"> - <properties> - <help>Interface parameters</help> - </properties> - <children> - <leafNode name="peer-group"> - <properties> - <help>Peer group for this peer</help> - </properties> - </leafNode> - <leafNode name="remote-as"> - <properties> - <help>Neighbor BGP AS number [REQUIRED]</help> - <completionHelp> - <list>external internal</list> - </completionHelp> - <valueHelp> - <format>u32:1-4294967294</format> - <description>Neighbor AS number</description> - </valueHelp> - <valueHelp> - <format>external</format> - <description>Any AS different from the local AS</description> - </valueHelp> - <valueHelp> - <format>internal</format> - <description>Neighbor AS number</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967294"/> - <regex>(external|internal)</regex> - </constraint> - <constraintErrorMessage>Invalid ASN value</constraintErrorMessage> - </properties> - </leafNode> - <node name="v6only"> - <properties> - <help>Enable BGP with v6 link-local only</help> - </properties> - <children> - <leafNode name="peer-group"> - <properties> - <help>Peer group for this peer</help> - </properties> - </leafNode> - <leafNode name="remote-as"> - <properties> - <help>Neighbor BGP AS number [REQUIRED]</help> - <completionHelp> - <list>external internal</list> - </completionHelp> - <valueHelp> - <format>u32:1-4294967294</format> - <description>Neighbor AS number</description> - </valueHelp> - <valueHelp> - <format>external</format> - <description>Any AS different from the local AS</description> - </valueHelp> - <valueHelp> - <format>internal</format> - <description>Neighbor AS number</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967294"/> - <regex>(external|internal)</regex> - </constraint> - <constraintErrorMessage>Invalid ASN value</constraintErrorMessage> - </properties> - </leafNode> - </children> - </node> - </children> - </node> - <tagNode name="local-as"> - <properties> - <help>Local AS number</help> - <valueHelp> - <format>u32:1-4294967294</format> - <description>Local AS number</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967294"/> - </constraint> - </properties> - <children> - <leafNode name="no-prepend"> - <properties> - <help>Disable prepending local-as to updates from EBGP peers</help> - <valueless/> - </properties> - </leafNode> - </children> - </tagNode> - <leafNode name="override-capability"> - <properties> - <help>Ignore capability negotiation with specified neighbor</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="passive"> - <properties> - <help>Do not initiate a session with this neighbor</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="password"> - <properties> - <help>BGP MD5 password</help> - </properties> - </leafNode> - <leafNode name="peer-group"> - <properties> - <help>IPv4 peer group for this peer</help> - </properties> - </leafNode> - <leafNode name="port"> - <properties> - <help>Neighbor BGP port</help> - <valueHelp> - <format>u32:1-65535</format> - <description>Neighbor BGP port number</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - <leafNode name="remote-as"> - <properties> - <help>Neighbor BGP AS number [REQUIRED]</help> - <completionHelp> - <list>external internal</list> - </completionHelp> - <valueHelp> - <format>u32:1-4294967294</format> - <description>Neighbor AS number</description> - </valueHelp> - <valueHelp> - <format>external</format> - <description>Any AS different from the local AS</description> - </valueHelp> - <valueHelp> - <format>internal</format> - <description>Neighbor AS number</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967294"/> - <regex>(external|internal)</regex> - </constraint> - <constraintErrorMessage>Invalid ASN value</constraintErrorMessage> - </properties> - </leafNode> - <leafNode name="shutdown"> - <properties> - <help>Administratively shut down neighbor</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="strict-capability-match"> - <properties> - <help>Enable strict capability negotiation</help> - <valueless/> - </properties> - </leafNode> - <node name="timers"> - <properties> - <help>Neighbor timers</help> - </properties> - <children> - <leafNode name="connect"> - <properties> - <help>BGP connect timer for this neighbor</help> - <valueHelp> - <format>u32:1-65535</format> - <description>Connect timer in seconds</description> - </valueHelp> - <valueHelp> - <format>0</format> - <description>Disable connect timer</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-65535"/> - </constraint> - </properties> - </leafNode> - <leafNode name="holdtime"> - <properties> - <help>BGP hold timer for this neighbor</help> - <valueHelp> - <format>u32:1-65535</format> - <description>Hold timer in seconds</description> - </valueHelp> - <valueHelp> - <format>0</format> - <description>Hold timer disabled</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-65535"/> - </constraint> - </properties> - </leafNode> - <leafNode name="keepalive"> - <properties> - <help>BGP keepalive interval for this neighbor</help> - <valueHelp> - <format>u32:1-65535</format> - <description>Keepalive interval in seconds (default 60)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <node name="ttl-security"> - <properties> - <help>Ttl security mechanism for this BGP peer</help> - </properties> - <children> - <leafNode name="hops"> - <properties> - <help>Number of the maximum number of hops to the BGP peer</help> - <valueHelp> - <format>u32:1-254</format> - <description>Number of hops</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-254"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <leafNode name="update-source"> - <!-- Need to check format interfaces --> - <properties> - <help>Source IP of routing updates</help> - <valueHelp> - <format>ipv4</format> - <description>IPv4 address of route source</description> - </valueHelp> - <valueHelp> - <format>ipv6</format> - <description>IPv6 address of route source</description> - </valueHelp> - <valueHelp> - <format>txt</format> - <description>Interface as route source</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - <validator name="ipv6-address"/> - <regex>(br|bond|dum|en|eth|gnv|lo|peth|tun|vti|vxlan|wg|wlan)[0-9]+</regex> - </constraint> - </properties> - </leafNode> - </children> - </tagNode> - <node name="parameters"> - <properties> - <help>BGP parameters</help> - </properties> - <children> - <leafNode name="always-compare-med"> - <properties> - <help>Always compare MEDs from different neighbors</help> - <valueless/> - </properties> - </leafNode> - <node name="bestpath"> - <properties> - <help>Default bestpath selection mechanism</help> - </properties> - <children> - <node name="as-path"> - <properties> - <help>AS-path attribute comparison parameters</help> - </properties> - <children> - <leafNode name="confed"> - <properties> - <help>Compare AS-path lengths including confederation sets and sequences</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="ignore"> - <properties> - <help>Ignore AS-path length in selecting a route</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="multipath-relax"> - <properties> - <help>Allow load sharing across routes that have different AS paths (but same length)</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <leafNode name="compare-routerid"> - <properties> - <help>Compare the router-id for identical EBGP paths</help> - <valueless/> - </properties> - </leafNode> - <node name="med"> - <properties> - <help>MED attribute comparison parameters</help> - </properties> - <children> - <leafNode name="confed"> - <properties> - <help>Compare MEDs among confederation paths</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="missing-as-worst"> - <properties> - <help>Treat missing route as a MED as the least preferred one</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - </children> - </node> - <leafNode name="cluster-id"> - <properties> - <help>Route-reflector cluster-id</help> - <valueHelp> - <format>ipv4</format> - <description>Route-reflector cluster-id</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - </constraint> - </properties> - </leafNode> - <node name="confederation"> - <properties> - <help>AS confederation parameters</help> - </properties> - <children> - <leafNode name="identifier"> - <properties> - <help>Confederation AS identifier [REQUIRED]</help> - <valueHelp> - <format>u32:1-4294967294</format> - <description>Confederation AS id</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967294"/> - </constraint> - </properties> - </leafNode> - <leafNode name="peers"> - <properties> - <help>Peer ASs in the BGP confederation</help> - <valueHelp> - <format>u32:1-4294967294</format> - <description>Peer AS number</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967294"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <node name="dampening"> - <properties> - <help>Enable route-flap dampening</help> - </properties> - <children> - <leafNode name="half-life"> - <properties> - <help>Half-life time for dampening [REQUIRED]</help> - <valueHelp> - <format>u32:1-45</format> - <description>Half-life penalty in seconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-45"/> - </constraint> - </properties> - </leafNode> - <leafNode name="max-suppress-time"> - <properties> - <help>Maximum duration to suppress a stable route [REQUIRED]</help> - <valueHelp> - <format>u32:1-255</format> - <description>Maximum suppress duration in seconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-255"/> - </constraint> - </properties> - </leafNode> - <leafNode name="re-use"> - <properties> - <help>Time to start reusing a route [REQUIRED]</help> - <valueHelp> - <format>u32:1-20000</format> - <description>Re-use time in seconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-20000"/> - </constraint> - </properties> - </leafNode> - <leafNode name="start-suppress-time"> - <properties> - <help>When to start suppressing a route [REQUIRED]</help> - <valueHelp> - <format>u32:1-20000</format> - <description>Start-suppress-time</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-20000"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <node name="default"> - <properties> - <help>BGP defaults</help> - </properties> - <children> - <leafNode name="local-pref"> - <properties> - <help>Default local preference</help> - <valueHelp> - <format>u32</format> - <description>Local preference</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-4294967295"/> - </constraint> - </properties> - </leafNode> - <leafNode name="no-ipv4-unicast"> - <properties> - <help>Deactivate IPv4 unicast for a peer by default</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <leafNode name="deterministic-med"> - <properties> - <help>Compare MEDs between different peers in the same AS</help> - <valueless/> - </properties> - </leafNode> - <node name="distance"> - <properties> - <help>Administratives distances for BGP routes</help> - </properties> - <children> - <node name="global"> - <properties> - <help>Global administratives distances for BGP routes</help> - </properties> - <children> - <leafNode name="external"> - <properties> - <help>Administrative distance for external BGP routes</help> - <valueHelp> - <format>u32:1-255</format> - <description>Administrative distance for external BGP routes</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-255"/> - </constraint> - </properties> - </leafNode> - <leafNode name="internal"> - <properties> - <help>Administrative distance for internal BGP routes</help> - <valueHelp> - <format>u32:1-255</format> - <description>Administrative distance for internal BGP routes</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-255"/> - </constraint> - </properties> - </leafNode> - <leafNode name="local"> - <properties> - <help>Administrative distance for local BGP routes</help> - <valueHelp> - <format>u32:1-255</format> - <description>Administrative distance for internal BGP routes</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-255"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <tagNode name="prefix"> - <properties> - <help>Administrative distance for a specific BGP prefix</help> - <valueHelp> - <format>ipv4net</format> - <description>Administrative distance for a specific BGP prefix</description> - </valueHelp> - <constraint> - <validator name="ipv4-prefix"/> - </constraint> - </properties> - <children> - <leafNode name="distance"> - <properties> - <help>Administrative distance for prefix</help> - <valueHelp> - <format>u32:1-255</format> - <description>Administrative distance for external BGP routes</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-255"/> - </constraint> - </properties> - </leafNode> - </children> - </tagNode> - </children> - </node> - <node name="graceful-restart"> - <properties> - <help>Graceful restart capability parameters</help> - </properties> - <children> - <leafNode name="stalepath-time"> - <properties> - <help>Maximum time to hold onto restarting neighbors stale paths</help> - <valueHelp> - <format>u32:1-3600</format> - <description>Hold time in seconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-3600"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <leafNode name="log-neighbor-changes"> - <properties> - <help>Log neighbor up/down changes and reset reason</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="network-import-check"> - <properties> - <help>Enable IGP route check for network statements</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="no-client-to-client-reflection"> - <properties> - <help>Disable client to client route reflection</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="no-fast-external-failover"> - <properties> - <help>Disable immediate session reset on peer link down event</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="router-id"> - <properties> - <help>BGP router id</help> - <valueHelp> - <format>ipv4</format> - <description>BGP router id</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <tagNode name="peer-group"> - <properties> - <help>BGP peer-group</help> - </properties> - <children> - <node name="address-family"> - <properties> - <help>BGP peer-group address-family parameters</help> - </properties> - <children> - #include <include/bgp-peer-group-afi-ipv4-unicast.xml.i> - #include <include/bgp-peer-group-afi-ipv6-unicast.xml.i> - </children> - </node> - <leafNode name="bfd"> - <properties> - <help>Enable Bidirectional Forwarding Detection (BFD) support</help> - <valueless/> - </properties> - </leafNode> - <node name="capability"> - <properties> - <help>Advertise capabilities to this peer-group</help> - </properties> - <children> - <leafNode name="dynamic"> - <properties> - <help>Advertise dynamic capability to this peer-group</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="extended-nexthop"> - <properties> - <help>Advertise extended-nexthop capability to this neighbor</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <leafNode name="description"> - <properties> - <help>Description for this peer-group</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="disable-capability-negotiation"> - <properties> - <help>Disable capability negotiation with this peer-group</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="disable-connected-check"> - <properties> - <help>Disable check to see if eBGP peer address is a connected route</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="ebgp-multihop"> - <properties> - <help>Allow this EBGP peer-group to not be on a directly connected network</help> - <valueHelp> - <format>u32:1-255</format> - <description>Number of hops</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-255"/> - </constraint> - </properties> - </leafNode> - <tagNode name="local-as"> - <properties> - <help>Local AS number [REQUIRED]</help> - <valueHelp> - <format>u32:1-4294967294</format> - <description>Local AS number</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967294"/> - </constraint> - </properties> - <children> - <leafNode name="no-prepend"> - <properties> - <help>Disable prepending local-as to updates from EBGP peers</help> - <valueless/> - </properties> - </leafNode> - </children> - </tagNode> - <leafNode name="override-capability"> - <properties> - <help>Ignore capability negotiation with specified peer-group</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="passive"> - <properties> - <help>Do not intiate a session with this peer-group</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="password"> - <properties> - <help>BGP MD5 password</help> - </properties> - </leafNode> - <leafNode name="remote-as"> - <properties> - <help>Neighbor BGP AS number [REQUIRED]</help> - <completionHelp> - <list>external internal</list> - </completionHelp> - <valueHelp> - <format>u32:1-4294967294</format> - <description>Neighbor AS number</description> - </valueHelp> - <valueHelp> - <format>external</format> - <description>Any AS different from the local AS</description> - </valueHelp> - <valueHelp> - <format>internal</format> - <description>Neighbor AS number</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967294"/> - <regex>(external|internal)</regex> - </constraint> - <constraintErrorMessage>Invalid ASN value</constraintErrorMessage> - </properties> - </leafNode> - <leafNode name="shutdown"> - <properties> - <help>Administratively shut down peer-group</help> - <valueless/> - </properties> - </leafNode> - <node name="ttl-security"> - <properties> - <help>Ttl security mechanism</help> - </properties> - <children> - <leafNode name="hops"> - <properties> - <help>Number of the maximum number of hops to the BGP peer</help> - <valueHelp> - <format>u32:1-254</format> - <description>Number of hops</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-254"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <leafNode name="update-source"> - <!-- Need to check format interfaces --> - <properties> - <help>Source IP of routing updates</help> - <valueHelp> - <format>ipv4</format> - <description>IPv4 address of route source</description> - </valueHelp> - <valueHelp> - <format>ipv6</format> - <description>IPv6 address of route source</description> - </valueHelp> - <valueHelp> - <format>txt</format> - <description>Interface as route source</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - <validator name="ipv6-address"/> - <regex>(br|bond|dum|en|eth|gnv|lo|peth|tun|vti|vxlan|wg|wlan)[0-9]+</regex> - </constraint> - </properties> - </leafNode> - </children> - </tagNode> - <leafNode name="route-map"> - <properties> - <help>Filter routes installed in local route map</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> - </leafNode> - <node name="timers"> - <properties> - <help>BGP protocol timers</help> - </properties> - <children> - <leafNode name="holdtime"> - <properties> - <help>BGP holdtime interval</help> - <valueHelp> - <format>u32:4-65535</format> - <description>Hold-time in seconds (default 180)</description> - </valueHelp> - <valueHelp> - <format>0</format> - <description>Do not hold routes</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-65535"/> - </constraint> - </properties> - </leafNode> - <leafNode name="keepalive"> - <properties> - <help>Keepalive interval</help> - <valueHelp> - <format>u32:1-65535</format> - <description>Keep-alive time in seconds (default 60)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - </children> - </node> + #include <include/bgp/bgp-common-config.xml.i> </children> - </tagNode> + </node> </children> </node> </interfaceDefinition> diff --git a/interface-definitions/protocols-isis.xml.in b/interface-definitions/protocols-isis.xml.in index 2340079a6..1bc890446 100644 --- a/interface-definitions/protocols-isis.xml.in +++ b/interface-definitions/protocols-isis.xml.in @@ -1,773 +1,16 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Protocol IS-IS configuration --> <interfaceDefinition> <node name="protocols"> <children> - <tagNode name="isis" owner="${vyos_conf_scripts_dir}/protocols_isis.py"> + <node name="isis" owner="${vyos_conf_scripts_dir}/protocols_isis.py"> <properties> <help>Intermediate System to Intermediate System (IS-IS)</help> - <valueHelp> - <format>text(TAG)</format> - <description>ISO Routing area tag</description> - </valueHelp> + <priority>610</priority> </properties> <children> - <node name="area-password"> - <properties> - <help>Configure the authentication password for an area</help> - </properties> - <children> - <leafNode name="plaintext-password"> - <properties> - <help>Plain-text authentication type</help> - <valueHelp> - <format>txt</format> - <description>Level-wide password</description> - </valueHelp> - </properties> - </leafNode> - <leafNode name="md5"> - <properties> - <help>MD5 authentication type</help> - <valueHelp> - <format>txt</format> - <description>Level-wide password</description> - </valueHelp> - </properties> - </leafNode> - </children> - </node> - <node name="default-information"> - <properties> - <help>Control distribution of default information</help> - </properties> - <children> - <node name="originate"> - <properties> - <help>Distribute a default route</help> - </properties> - <children> - <node name="ipv4"> - <properties> - <help>Distribute default route for IPv4</help> - </properties> - <children> - <leafNode name="level-1"> - <properties> - <help>Distribute default route into level-1</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="level-2"> - <properties> - <help>Distribute default route into level-2</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <node name="ipv6"> - <properties> - <help>Distribute default route for IPv6</help> - </properties> - <children> - <leafNode name="level-1"> - <properties> - <help>Distribute default route into level-1</help> - <completionHelp> - <list>always</list> - </completionHelp> - <valueHelp> - <format>always</format> - <description>Always advertise default route</description> - </valueHelp> - </properties> - </leafNode> - <leafNode name="level-2"> - <properties> - <help>Distribute default route into level-2</help> - <completionHelp> - <list>always</list> - </completionHelp> - <valueHelp> - <format>always</format> - <description>Always advertise default route</description> - </valueHelp> - </properties> - </leafNode> - </children> - </node> - </children> - </node> - </children> - </node> - <node name="domain-password"> - <properties> - <help>Set the authentication password for a routing domain</help> - </properties> - <children> - <leafNode name="plaintext-password"> - <properties> - <help>Plain-text authentication type</help> - <valueHelp> - <format>txt</format> - <description>Level-wide password</description> - </valueHelp> - </properties> - </leafNode> -<!-- - <leafNode name="md5"> - <properties> - <help>MD5 authentication type</help> - <valueHelp> - <format>txt</format> - <description>Level-wide password</description> - </valueHelp> - </properties> - </leafNode> ---> - </children> - </node> - <leafNode name="dynamic-hostname"> - <properties> - <help>Dynamic hostname for IS-IS</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="level"> - <properties> - <help>IS-IS level number</help> - <completionHelp> - <list>level-1 level-1-2 level-2</list> - </completionHelp> - <valueHelp> - <format>level-1</format> - <description>Act as a station router</description> - </valueHelp> - <valueHelp> - <format>level-1-2</format> - <description>Act as both a station and an area router</description> - </valueHelp> - <valueHelp> - <format>level-2</format> - <description>Act as an area router</description> - </valueHelp> - <constraint> - <regex>(level-1|level-1-2|level-2)</regex> - </constraint> - </properties> - </leafNode> - <leafNode name="lsp-gen-interval"> - <properties> - <help>Minimum interval between regenerating same LSP</help> - <valueHelp> - <format>u32:1-120</format> - <description>Minimum interval in seconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-120"/> - </constraint> - </properties> - </leafNode> - <leafNode name="lsp-mtu"> - <properties> - <help>Configure the maximum size of generated LSPs</help> - <valueHelp> - <format>u32:128-4352</format> - <description>Maximum size of generated LSPs</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 128-4352"/> - </constraint> - </properties> - </leafNode> - <leafNode name="lsp-refresh-interval"> - <properties> - <help>LSP refresh interval</help> - <valueHelp> - <format>u32:1-65235</format> - <description>LSP refresh interval in seconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65235"/> - </constraint> - </properties> - </leafNode> - <leafNode name="max-lsp-lifetime"> - <properties> - <help>Maximum LSP lifetime</help> - <valueHelp> - <format>u32:350-65535</format> - <description>LSP lifetime in seconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-65535"/> - </constraint> - </properties> - </leafNode> - <leafNode name="metric-style"> - <properties> - <help>Use old-style (ISO 10589) or new-style packet formats</help> - <completionHelp> - <list>narrow transition wide</list> - </completionHelp> - <valueHelp> - <format>narrow</format> - <description>Use old style of TLVs with narrow metric</description> - </valueHelp> - <valueHelp> - <format>transition</format> - <description>Send and accept both styles of TLVs during transition</description> - </valueHelp> - <valueHelp> - <format>wide</format> - <description>Use new style of TLVs to carry wider metric</description> - </valueHelp> - <constraint> - <regex>(narrow|transition|wide)</regex> - </constraint> - </properties> - </leafNode> - <leafNode name="net"> - <properties> - <help>A Network Entity Title for this process (ISO only)</help> - <valueHelp> - <format>XX.XXXX. ... .XXX.XX</format> - <description>Network entity title (NET)</description> - </valueHelp> - <constraint> - <regex>[a-fA-F0-9]{2}(\.[a-fA-F0-9]{4}){3,9}\.[a-fA-F0-9]{2}</regex> - </constraint> - </properties> - </leafNode> - <leafNode name="purge-originator"> - <properties> - <help>Use the RFC 6232 purge-originator</help> - <valueless/> - </properties> - </leafNode> - <node name="traffic-engineering"> - <properties> - <help>Show IS-IS neighbor adjacencies</help> - </properties> - <children> - <leafNode name="enable"> - <properties> - <help>Enable MPLS traffic engineering extensions</help> - <valueless/> - </properties> - </leafNode> -<!-- - <node name="inter-as"> - <properties> - <help>MPLS traffic engineering inter-AS support</help> - </properties> - <children> - <leafNode name="level-1"> - <properties> - <help>Area native mode self originate inter-AS LSP with L1 only flooding scope</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="level-1-2"> - <properties> - <help>Area native mode self originate inter-AS LSP with L1 and L2 flooding scope</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="level-2"> - <properties> - <help>Area native mode self originate inter-AS LSP with L2 only flooding scope</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <leafNode name="inter-as"> - <properties> - <help>MPLS traffic engineering inter-AS support</help> - <valueless/> - </properties> - </leafNode> ---> - <leafNode name="address"> - <properties> - <help>MPLS traffic engineering router ID</help> - <valueHelp> - <format>ipv4</format> - <description>IPv4 address</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <node name="segment-routing"> - <properties> - <help>Segment-Routing (SPRING) settings</help> - </properties> - <children> - <leafNode name="enable"> - <properties> - <help>Enable segment-routing functionality</help> - <valueless/> - </properties> - </leafNode> - <node name="global-block"> - <properties> - <help>Global block label range</help> - </properties> - <children> - <leafNode name="low-label-value"> - <properties> - <help>The lower bound of the global block</help> - <valueHelp> - <format>u32:16-1048575</format> - <description>MPLS label value</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 16-1048575"/> - </constraint> - </properties> - </leafNode> - <leafNode name="high-label-value"> - <properties> - <help>The upper bound of the global block</help> - <valueHelp> - <format>u32:16-1048575</format> - <description>MPLS label value</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 16-1048575"/> - </constraint> - </properties> - </leafNode> - </children> - </node> -<!-- - <node name="local-block"> - <properties> - <help>Local Block label range</help> - </properties> - <children> - <leafNode name="low-label-value"> - <properties> - <help>The lower bound of the local block</help> - <valueHelp> - <format>u32:16-1048575</format> - <description>MPLS label value</description> - </valueHelp> - <constraint> - <validator name="numeric" argument=" range 16-1048575"/> - </constraint> - </properties> - </leafNode> - <leafNode name="high-label-value"> - <properties> - <help>The upper bound of the local block</help> - <valueHelp> - <format>u32:16-1048575</format> - <description>MPLS label value</description> - </valueHelp> - <constraint> - <validator name="numeric" argument=" range 16-1048575"/> - </constraint> - </properties> - </leafNode> - </children> - </node> ---> - <leafNode name="maximum-label-depth"> - <properties> - <help>Maximum MPLS labels allowed for this router</help> - <valueHelp> - <format>u32:1-16</format> - <description>MPLS label depth</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-16"/> - </constraint> - </properties> - </leafNode> - <tagNode name="prefix"> - <properties> - <help>Static IPv4/IPv6 prefix segment/label mapping</help> - <completionHelp> - <list><x.x.x.x/x> <h:h:h:h:h:h:h:h/h></list> - </completionHelp> - </properties> - <children> - <node name="absolute"> - <properties> - <help>Specify the absolute value of prefix segment/label ID</help> - </properties> - <children> - <leafNode name="value"> - <properties> - <help>Specify the absolute value of prefix segment/label ID</help> - <valueHelp> - <format>u32:16-1048575</format> - <description>The absolute segment/label ID value</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 16-1048575"/> - </constraint> - </properties> - </leafNode> - <leafNode name="explicit-null"> - <properties> - <help>Request upstream neighbor to replace segment/label with explicit null label</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="no-php-flag"> - <properties> - <help>Do not request penultimate hop popping for segment/label</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <node name="index"> - <properties> - <help>Specify the index value of prefix segment/label ID</help> - </properties> - <children> - <leafNode name="value"> - <properties> - <help>Specify the index value of prefix segment/label ID</help> - <valueHelp> - <format>u32:0-65535</format> - <description>The index segment/label ID value</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-65535"/> - </constraint> - </properties> - </leafNode> - <leafNode name="explicit-null"> - <properties> - <help>Request upstream neighbor to replace segment/label with explicit null label</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="no-php-flag"> - <properties> - <help>Do not request penultimate hop popping for segment/label</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - </children> - </tagNode> - </children> - </node> - <node name="redistribute"> - <properties> - <help>Redistribute information from another routing protocol</help> - </properties> - <children> - <node name="ipv4"> - <properties> - <help>Redistribute IPv4 routes</help> - </properties> - <children> - <node name="bgp"> - <properties> - <help>Border Gateway Protocol (BGP)</help> - </properties> - <children> - #include <include/isis-redistribute-ipv4.xml.i> - </children> - </node> - <node name="connected"> - <properties> - <help>Redistribute connected routes into IS-IS</help> - </properties> - <children> - #include <include/isis-redistribute-ipv4.xml.i> - </children> - </node> - <node name="kernel"> - <properties> - <help>Redistribute kernel routes into IS-IS</help> - </properties> - <children> - #include <include/isis-redistribute-ipv4.xml.i> - </children> - </node> - <node name="ospf"> - <properties> - <help>Redistribute OSPF routes into IS-IS</help> - </properties> - <children> - #include <include/isis-redistribute-ipv4.xml.i> - </children> - </node> - <node name="rip"> - <properties> - <help>Redistribute RIP routes into IS-IS</help> - </properties> - <children> - #include <include/isis-redistribute-ipv4.xml.i> - </children> - </node> - <node name="static"> - <properties> - <help>Redistribute static routes into IS-IS</help> - </properties> - <children> - #include <include/isis-redistribute-ipv4.xml.i> - </children> - </node> - </children> - </node> - </children> - </node> - <leafNode name="set-attached-bit"> - <properties> - <help>Set attached bit to identify as L1/L2 router for inter-area traffic</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="set-overload-bit"> - <properties> - <help>Set overload bit to avoid any transit traffic</help> - <valueless/> - </properties> - </leafNode> - <node name="spf-delay-ietf"> - <properties> - <help>IETF SPF delay algorithm</help> - </properties> - <children> - <leafNode name="init-delay"> - <properties> - <help>Delay used while in QUIET state</help> - <valueHelp> - <format>u32:0-60000</format> - <description>Delay used while in QUIET state (in ms)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-60000"/> - </constraint> - </properties> - </leafNode> - <leafNode name="short-delay"> - <properties> - <help>Delay used while in SHORT_WAIT state</help> - <valueHelp> - <format>u32:0-60000</format> - <description>Delay used while in SHORT_WAIT state (in ms)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-60000"/> - </constraint> - </properties> - </leafNode> - <leafNode name="long-delay"> - <properties> - <help>Delay used while in LONG_WAIT</help> - <valueHelp> - <format>u32:0-60000</format> - <description>Delay used while in LONG_WAIT state (in ms)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-60000"/> - </constraint> - </properties> - </leafNode> - <leafNode name="holddown"> - <properties> - <help>Time with no received IGP events before considering IGP stable</help> - <valueHelp> - <format>u32:0-60000</format> - <description>Time with no received IGP events before considering IGP stable (in ms)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-60000"/> - </constraint> - </properties> - </leafNode> - <leafNode name="time-to-learn"> - <properties> - <help>Maximum duration needed to learn all the events related to a single failure</help> - <valueHelp> - <format>u32:0-60000</format> - <description>Maximum duration needed to learn all the events related to a single failure (in ms)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-60000"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <leafNode name="spf-interval"> - <properties> - <help>Minimum interval between SPF calculations</help> - <valueHelp> - <format>u32:1-120</format> - <description>Minimum interval between consecutive SPFs in seconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-120"/> - </constraint> - </properties> - </leafNode> - <tagNode name="interface"> - <!-- (config-if)# ip router isis WORD (same as name of IS-IS process) - if any section of "interface" pesent --> - <properties> - <help>Interface params</help> - <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py</script> - </completionHelp> - </properties> - <children> - <leafNode name="bfd"> - <properties> - <help>Enable BFD support</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="circuit-type"> - <properties> - <help>Configure circuit type for interface</help> - <completionHelp> - <list>level-1 level-1-2 level-2-only</list> - </completionHelp> - <valueHelp> - <format>level-1</format> - <description>Level-1 only adjacencies are formed</description> - </valueHelp> - <valueHelp> - <format>level-1-2</format> - <description>Level-1-2 adjacencies are formed</description> - </valueHelp> - <valueHelp> - <format>level-2-only</format> - <description>Level-2 only adjacencies are formed</description> - </valueHelp> - <constraint> - <regex>(level-1|level-1-2|level-2-only)</regex> - </constraint> - </properties> - </leafNode> - <leafNode name="hello-padding"> - <properties> - <help>Add padding to IS-IS hello packets</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="hello-interval"> - <properties> - <help>Set Hello interval</help> - <valueHelp> - <format>u32:1-600</format> - <description>Set Hello interval</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-600"/> - </constraint> - </properties> - </leafNode> - <leafNode name="hello-multiplier"> - <properties> - <help>Set Hello interval</help> - <valueHelp> - <format>u32:2-100</format> - <description>Set multiplier for Hello holding time</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 2-100"/> - </constraint> - </properties> - </leafNode> - <leafNode name="metric"> - <properties> - <help>Set default metric for circuit</help> - <valueHelp> - <format>u32:0-16777215</format> - <description>Default metric value</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-16777215"/> - </constraint> - </properties> - </leafNode> - <node name="network"> - <properties> - <help>Set network type</help> - </properties> - <children> - <leafNode name="point-to-point"> - <properties> - <help>point-to-point network type</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <leafNode name="passive"> - <properties> - <help>Configure the passive mode for interface</help> - <valueless/> - </properties> - </leafNode> - <node name="password"> - <properties> - <help>Configure the authentication password for a circuit</help> - </properties> - <children> - <leafNode name="plaintext-password"> - <properties> - <help>Plain-text authentication type</help> - <valueHelp> - <format>txt</format> - <description>Circuit password</description> - </valueHelp> - </properties> - </leafNode> - </children> - </node> - <leafNode name="priority"> - <properties> - <help>Set priority for Designated Router election</help> - <valueHelp> - <format>u32:0-127</format> - <description>Priority value</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-127"/> - </constraint> - </properties> - </leafNode> - <leafNode name="psnp-interval"> - <properties> - <help>Set PSNP interval in seconds</help> - <valueHelp> - <format>u32:0-127</format> - <description>Priority value</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-127"/> - </constraint> - </properties> - </leafNode> - <leafNode name="three-way-handshake"> - <properties> - <help>Enable/Disable three-way handshake</help> - <valueless/> - </properties> - </leafNode> - </children> - </tagNode> + #include <include/isis/isis-common-config.xml.i> </children> - </tagNode> + </node> </children> </node> </interfaceDefinition> diff --git a/interface-definitions/protocols-multicast.xml.in b/interface-definitions/protocols-multicast.xml.in index a06f2b287..bf0ead78f 100644 --- a/interface-definitions/protocols-multicast.xml.in +++ b/interface-definitions/protocols-multicast.xml.in @@ -1,5 +1,4 @@ <?xml version="1.0"?> -<!-- Multicast static routing configuration --> <interfaceDefinition> <node name="protocols"> <children> diff --git a/interface-definitions/protocols-ospf.xml.in b/interface-definitions/protocols-ospf.xml.in new file mode 100644 index 000000000..d9c3325ec --- /dev/null +++ b/interface-definitions/protocols-ospf.xml.in @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<interfaceDefinition> + <node name="protocols"> + <children> + <node name="ospf" owner="${vyos_conf_scripts_dir}/protocols_ospf.py"> + <properties> + <help>Open Shortest Path First (OSPF)</help> + <priority>620</priority> + </properties> + <children> + #include <include/ospf/ospf-common-config.xml.i> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/interface-definitions/protocols-ospfv3.xml.in b/interface-definitions/protocols-ospfv3.xml.in new file mode 100644 index 000000000..f4f403e93 --- /dev/null +++ b/interface-definitions/protocols-ospfv3.xml.in @@ -0,0 +1,229 @@ +<?xml version="1.0" encoding="utf-8"?> +<interfaceDefinition> + <node name="protocols"> + <children> + <node name="ospfv3" owner="${vyos_conf_scripts_dir}/protocols_ospfv3.py"> + <properties> + <help>Open Shortest Path First (OSPF) for IPv6</help> + <priority>620</priority> + </properties> + <children> + <tagNode name="area"> + <properties> + <help>OSPFv3 Area</help> + <valueHelp> + <format>u32</format> + <description>Area ID as a decimal value</description> + </valueHelp> + <valueHelp> + <format>ipv4</format> + <description>Area ID in IP address forma</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + <validator name="ip-address"/> + </constraint> + </properties> + <children> + <leafNode name="export-list"> + <properties> + <help>Name of export-list</help> + <completionHelp> + <path>policy access-list6</path> + </completionHelp> + </properties> + </leafNode> + <leafNode name="import-list"> + <properties> + <help>Name of import-list</help> + <completionHelp> + <path>policy access-list6</path> + </completionHelp> + </properties> + </leafNode> + <leafNode name="interface"> + <properties> + <help>Enable routing on an IPv6 interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface used for routing information exchange</description> + </valueHelp> + <constraint> + <validator name="interface-name"/> + </constraint> + <multi/> + </properties> + </leafNode> + <tagNode name="range"> + <properties> + <help>Specify IPv6 prefix (border routers only)</help> + <valueHelp> + <format>ipv6net</format> + <description>Specify IPv6 prefix (border routers only)</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + <children> + <leafNode name="advertise"> + <properties> + <help>Advertise this range</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="not-advertise"> + <properties> + <help>Do not advertise this range</help> + <valueless/> + </properties> + </leafNode> + </children> + </tagNode> + </children> + </tagNode> + <node name="distance"> + <properties> + <help>Administrative distance</help> + </properties> + <children> + #include <include/ospf/ospf-distance-global.xml.i> + <node name="ospfv3"> + <properties> + <help>OSPFv3 administrative distance</help> + </properties> + <children> + #include <include/ospf/ospf-distance-per-protocol.xml.i> + </children> + </node> + </children> + </node> + <tagNode name="interface"> + <properties> + <help>Enable routing on an IPv6 interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface used for routing information exchange</description> + </valueHelp> + <constraint> + <validator name="interface-name"/> + </constraint> + </properties> + <children> + #include <include/ospf/ospf-intervals.xml.i> + #include <include/ospf/ospf-interface-common.xml.i> + <leafNode name="ifmtu"> + <properties> + <help>Interface MTU</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Interface MTU</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + </leafNode> + <leafNode name="instance-id"> + <properties> + <help>Instance Id (default: 0)</help> + <valueHelp> + <format>u32:0-255</format> + <description>Instance Id</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-255"/> + </constraint> + </properties> + <defaultValue>0</defaultValue> + </leafNode> + <leafNode name="network"> + <properties> + <help>Network type</help> + <completionHelp> + <list>broadcast point-to-point</list> + </completionHelp> + <valueHelp> + <format>broadcast</format> + <description>Broadcast network type</description> + </valueHelp> + <valueHelp> + <format>point-to-point</format> + <description>Point-to-point network type</description> + </valueHelp> + <constraint> + <regex>^(broadcast|point-to-point)$</regex> + </constraint> + <constraintErrorMessage>Must be broadcast or point-to-point</constraintErrorMessage> + </properties> + </leafNode> + #include <include/isis/passive.xml.i> + </children> + </tagNode> + <node name="parameters"> + <properties> + <help>OSPFv3 specific parameters</help> + </properties> + <children> + #include <include/ospf/ospf-router-id.xml.i> + </children> + </node> + <node name="redistribute"> + <properties> + <help>Redistribute information from another routing protocol</help> + </properties> + <children> + <node name="bgp"> + <properties> + <help>Redistribute BGP routes</help> + </properties> + <children> + #include <include/route-map.xml.i> + </children> + </node> + <node name="connected"> + <properties> + <help>Redistribute connected routes</help> + </properties> + <children> + #include <include/route-map.xml.i> + </children> + </node> + <node name="kernel"> + <properties> + <help>Redistribute kernel routes</help> + </properties> + <children> + #include <include/route-map.xml.i> + </children> + </node> + <node name="ripng"> + <properties> + <help>Redistribute RIPNG routes</help> + </properties> + <children> + #include <include/route-map.xml.i> + </children> + </node> + <node name="static"> + <properties> + <help>Redistribute static routes</help> + </properties> + <children> + #include <include/route-map.xml.i> + </children> + </node> + </children> + </node> + #include <include/route-map.xml.i> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/interface-definitions/protocols-rip.xml.in b/interface-definitions/protocols-rip.xml.in index 34d0a5a10..fd1a84bb8 100644 --- a/interface-definitions/protocols-rip.xml.in +++ b/interface-definitions/protocols-rip.xml.in @@ -1,10 +1,11 @@ -<!-- Routing Information Protocol (RIP) configuration --> +<?xml version="1.0"?> <interfaceDefinition> <node name="protocols"> <children> <node name="rip" owner="${vyos_conf_scripts_dir}/protocols_rip.py"> <properties> <help>Routing Information Protocol (RIP) parameters</help> + <priority>650</priority> </properties> <children> <leafNode name="default-distance"> @@ -19,73 +20,14 @@ </constraint> </properties> </leafNode> - <node name="default-information"> - <properties> - <help>Control distribution of default route</help> - </properties> - <children> - <leafNode name="originate"> - <properties> - <help>Distribute a default route</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - <leafNode name="default-metric"> - <properties> - <help>Metric of redistributed routes</help> - <valueHelp> - <format>u32:1-16</format> - <description>Redistributed routes metric</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-16"/> - </constraint> - </properties> - </leafNode> + #include <include/rip/rip-default-information.xml.i> + #include <include/rip/rip-default-metric.xml.i> <node name="distribute-list"> <properties> <help>Filter networks in routing updates</help> </properties> <children> - <node name="access-list"> - <properties> - <help>Access-list</help> - </properties> - <children> - <leafNode name="in"> - <properties> - <help>Access list to apply to input packets</help> - <valueHelp> - <format>u32</format> - <description>Access list to apply to input packets</description> - </valueHelp> - <completionHelp> - <path>policy access-list</path> - </completionHelp> - <constraint> - <validator name="numeric" argument="--range 0-4294967295"/> - </constraint> - </properties> - </leafNode> - <leafNode name="out"> - <properties> - <help>Access list to apply to output packets</help> - <valueHelp> - <format>u32</format> - <description>Access list to apply to output packets</description> - </valueHelp> - <completionHelp> - <path>policy access-list</path> - </completionHelp> - <constraint> - <validator name="numeric" argument="--range 0-4294967295"/> - </constraint> - </properties> - </leafNode> - </children> - </node> + #include <include/rip/rip-access-list.xml.i> <tagNode name="interface"> <properties> <help>Apply filtering to an interface</help> @@ -96,124 +38,70 @@ <completionHelp> <script>${vyos_completion_dir}/list_interfaces.py</script> </completionHelp> + <constraint> + <validator name="interface-name"/> + </constraint> + </properties> + <children> + #include <include/rip/rip-access-list.xml.i> + #include <include/rip/rip-prefix-list.xml.i> + </children> + </tagNode> + #include <include/rip/rip-prefix-list.xml.i> + </children> + </node> + #include <include/rip/rip-interface.xml.i> + <tagNode name="interface"> + <children> + <node name="authentication"> + <properties> + <help>Authentication</help> </properties> <children> - <node name="access-list"> + <tagNode name="md5"> <properties> - <help>Access list</help> + <help>MD5 key id</help> + <valueHelp> + <format>u32:1-255</format> + <description>OSPF key id</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> </properties> <children> - <leafNode name="in"> + <leafNode name="password"> <properties> - <help>Access list to apply to input packets</help> + <help>Authentication password</help> <valueHelp> - <format>u32</format> - <description>Access list to apply to input packets</description> - </valueHelp> - <completionHelp> - <path>policy access-list</path> - </completionHelp> - <constraint> - <validator name="numeric" argument="--range 0-4294967295"/> - </constraint> - </properties> - </leafNode> - <leafNode name="out"> - <properties> - <help>Access list to apply to output packets</help> - <valueHelp> - <format>u32</format> - <description>Access list to apply to output packets</description> + <format>txt</format> + <description>MD5 Key (16 characters or less)</description> </valueHelp> - <completionHelp> - <path>policy access-list</path> - </completionHelp> <constraint> - <validator name="numeric" argument="--range 0-4294967295"/> + <regex>^[^[:space:]]{1,16}$</regex> </constraint> + <constraintErrorMessage>Password must be 16 characters or less</constraintErrorMessage> </properties> </leafNode> </children> - </node> - <node name="prefix-list"> + </tagNode> + <leafNode name="plaintext-password"> <properties> - <help>Prefix-list</help> - </properties> - <children> - <leafNode name="in"> - <properties> - <help>Prefix-list to apply to input packets</help> - <valueHelp> - <format>txt</format> - <description>Prefix-list to apply to input packets</description> - </valueHelp> - <completionHelp> - <path>policy prefix-list</path> - </completionHelp> - </properties> - </leafNode> - <leafNode name="out"> - <properties> - <help>Prefix-list to apply to output packets</help> - <valueHelp> - <format>txt</format> - <description>Prefix-list to apply to output packets</description> - </valueHelp> - <completionHelp> - <path>policy prefix-list</path> - </completionHelp> - </properties> - </leafNode> - </children> - </node> - </children> - </tagNode> - <node name="prefix-list"> - <properties> - <help>Prefix-list</help> - </properties> - <children> - <leafNode name="in"> - <properties> - <help>Prefix-list to apply to input packets</help> - <valueHelp> - <format>txt</format> - <description>Prefix-list to apply to input packets</description> - </valueHelp> - <completionHelp> - <path>policy prefix-list</path> - </completionHelp> - </properties> - </leafNode> - <leafNode name="out"> - <properties> - <help>Prefix-list to apply to output packets</help> + <help>Plain text password</help> <valueHelp> <format>txt</format> - <description>Prefix-list to apply to output packets</description> + <description>Plain text password (16 characters or less)</description> </valueHelp> - <completionHelp> - <path>policy prefix-list</path> - </completionHelp> + <constraint> + <regex>^[^[:space:]]{1,16}$</regex> + </constraint> + <constraintErrorMessage>Password must be 16 characters or less</constraintErrorMessage> </properties> </leafNode> </children> </node> </children> - </node> - <leafNode name="interface"> - <properties> - <help>Interface name</help> - <valueHelp> - <format>txt</format> - <description>Apply filtering to an interface</description> - </valueHelp> - <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py</script> - </completionHelp> - <multi/> - </properties> - </leafNode> + </tagNode> <leafNode name="neighbor"> <properties> <help>Neighbor router</help> @@ -264,38 +152,10 @@ </completionHelp> </properties> </leafNode> - <leafNode name="distance"> - <properties> - <help>Administrative distance for network</help> - <valueHelp> - <format>u32:1-255</format> - <description>Administrative distance</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-255"/> - </constraint> - </properties> - </leafNode> + #include <include/static/static-route-distance.xml.i> </children> </tagNode> - <leafNode name="passive-interface"> - <properties> - <help>Passive interface</help> - <valueHelp> - <format>txt</format> - <description>Suppress routing updates on interface</description> - </valueHelp> - <valueHelp> - <format>default</format> - <description>Suppress routing updates on all interfaces by default</description> - </valueHelp> - <completionHelp> - <list>default</list> - <script>${vyos_completion_dir}/list_interfaces.py</script> - </completionHelp> - <multi/> - </properties> - </leafNode> + #include <include/routing-passive-interface-xml.i> <node name="redistribute"> <properties> <help>Redistribute information from another routing protocol</help> @@ -306,7 +166,7 @@ <help>Redistribute BGP routes</help> </properties> <children> - #include <include/rip-redistribute.xml.i> + #include <include/rip/rip-redistribute.xml.i> </children> </node> <node name="connected"> @@ -314,7 +174,15 @@ <help>Redistribute connected routes</help> </properties> <children> - #include <include/rip-redistribute.xml.i> + #include <include/rip/rip-redistribute.xml.i> + </children> + </node> + <node name="isis"> + <properties> + <help>Redistribute IS-IS routes</help> + </properties> + <children> + #include <include/rip/rip-redistribute.xml.i> </children> </node> <node name="kernel"> @@ -322,7 +190,7 @@ <help>Redistribute kernel routes</help> </properties> <children> - #include <include/rip-redistribute.xml.i> + #include <include/rip/rip-redistribute.xml.i> </children> </node> <node name="ospf"> @@ -330,7 +198,7 @@ <help>Redistribute OSPF routes</help> </properties> <children> - #include <include/rip-redistribute.xml.i> + #include <include/rip/rip-redistribute.xml.i> </children> </node> <node name="static"> @@ -338,7 +206,7 @@ <help>Redistribute static routes</help> </properties> <children> - #include <include/rip-redistribute.xml.i> + #include <include/rip/rip-redistribute.xml.i> </children> </node> </children> @@ -356,49 +224,7 @@ <multi/> </properties> </leafNode> - <node name="timers"> - <properties> - <help>RIP timer values</help> - </properties> - <children> - <leafNode name="garbage-collection"> - <properties> - <help>Garbage collection timer</help> - <valueHelp> - <format>u32:5-2147483647</format> - <description>Garbage colletion time (default 120)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 5-2147483647"/> - </constraint> - </properties> - </leafNode> - <leafNode name="timeout"> - <properties> - <help>Routing information timeout timer</help> - <valueHelp> - <format>u32:5-2147483647</format> - <description>Routing information timeout timer (default 180)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 5-2147483647"/> - </constraint> - </properties> - </leafNode> - <leafNode name="update"> - <properties> - <help>Routing table update timer</help> - <valueHelp> - <format>u32:5-2147483647</format> - <description>Routing table update timer in seconds (default 30)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 5-2147483647"/> - </constraint> - </properties> - </leafNode> - </children> - </node> + #include <include/rip/rip-timers.xml.i> </children> </node> </children> diff --git a/interface-definitions/protocols-ripng.xml.in b/interface-definitions/protocols-ripng.xml.in new file mode 100644 index 000000000..fe7411e65 --- /dev/null +++ b/interface-definitions/protocols-ripng.xml.in @@ -0,0 +1,147 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="protocols"> + <children> + <node name="ripng" owner="${vyos_conf_scripts_dir}/protocols_ripng.py"> + <properties> + <help>Routing Information Protocol (RIPng) parameters</help> + <priority>660</priority> + </properties> + <children> + <leafNode name="aggregate-address"> + <properties> + <help>Aggregate RIPng route announcement</help> + <valueHelp> + <format>ipv6net</format> + <description>Aggregate RIPng route announcement</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + <multi/> + </properties> + </leafNode> + #include <include/rip/rip-default-information.xml.i> + #include <include/rip/rip-default-metric.xml.i> + <node name="distribute-list"> + <properties> + <help>Filter networks in routing updates</help> + </properties> + <children> + #include <include/rip/rip-access-list6.xml.i> + <tagNode name="interface"> + <properties> + <help>Apply filtering to an interface</help> + <valueHelp> + <format>txt</format> + <description>Apply filtering to an interface</description> + </valueHelp> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <constraint> + <validator name="interface-name"/> + </constraint> + </properties> + <children> + #include <include/rip/rip-access-list6.xml.i> + #include <include/rip/rip-prefix-list6.xml.i> + </children> + </tagNode> + #include <include/rip/rip-prefix-list6.xml.i> + </children> + </node> + #include <include/rip/rip-interface.xml.i> + <leafNode name="network"> + <properties> + <help>RIPng network</help> + <valueHelp> + <format>ipv6net</format> + <description>RIPng network</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + <multi/> + </properties> + </leafNode> + <leafNode name="passive-interface"> + <properties> + <help>Passive interface</help> + <valueHelp> + <format>txt</format> + <description>Suppress routing updates on interface</description> + </valueHelp> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <multi/> + </properties> + </leafNode> + <node name="redistribute"> + <properties> + <help>Redistribute information from another routing protocol</help> + </properties> + <children> + <node name="bgp"> + <properties> + <help>Redistribute BGP routes</help> + </properties> + <children> + #include <include/rip/rip-redistribute.xml.i> + </children> + </node> + <node name="connected"> + <properties> + <help>Redistribute connected routes</help> + </properties> + <children> + #include <include/rip/rip-redistribute.xml.i> + </children> + </node> + <node name="kernel"> + <properties> + <help>Redistribute kernel routes</help> + </properties> + <children> + #include <include/rip/rip-redistribute.xml.i> + </children> + </node> + <node name="ospfv3"> + <properties> + <help>Redistribute OSPFv3 routes</help> + </properties> + <children> + #include <include/rip/rip-redistribute.xml.i> + </children> + </node> + <node name="static"> + <properties> + <help>Redistribute static routes</help> + </properties> + <children> + #include <include/rip/rip-redistribute.xml.i> + </children> + </node> + </children> + </node> + <leafNode name="route"> + <properties> + <help>RIPng static route</help> + <valueHelp> + <format>ipv6net</format> + <description>RIPng static route</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + <multi/> + </properties> + </leafNode> + #include <include/route-map.xml.i> + #include <include/rip/rip-timers.xml.i> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/interface-definitions/protocols-rpki.xml.in b/interface-definitions/protocols-rpki.xml.in new file mode 100644 index 000000000..94fab54a5 --- /dev/null +++ b/interface-definitions/protocols-rpki.xml.in @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="utf-8"?> +<interfaceDefinition> + <node name="protocols"> + <children> + <node name="rpki" owner="${vyos_conf_scripts_dir}/protocols_rpki.py"> + <properties> + <help>BGP prefix origin validation</help> + </properties> + <children> + <tagNode name="cache"> + <properties> + <help>RPKI cache server address</help> + <valueHelp> + <format>ipv4</format> + <description>IP address of NTP server</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address of NTP server</description> + </valueHelp> + <valueHelp> + <format>hostname</format> + <description>Fully qualified domain name of NTP server</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + <validator name="ipv6-address"/> + <validator name="fqdn"/> + </constraint> + </properties> + <children> + #include <include/port-number.xml.i> + <leafNode name="preference"> + <properties> + <help>Preference of the cache server</help> + <valueHelp> + <format>u32:1-255</format> + <description>Polling period</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> + </leafNode> + <node name="ssh"> + <properties> + <help>RPKI SSH connection settings</help> + </properties> + <children> + <leafNode name="known-hosts-file"> + <properties> + <help>RPKI SSH known hosts file</help> + <constraint> + <validator name="file-exists"/> + </constraint> + </properties> + </leafNode> + <leafNode name="private-key-file"> + <properties> + <help>RPKI SSH private key file</help> + <constraint> + <validator name="file-exists"/> + </constraint> + </properties> + </leafNode> + <leafNode name="public-key-file"> + <properties> + <help>RPKI SSH public key file path</help> + <constraint> + <validator name="file-exists"/> + </constraint> + </properties> + </leafNode> + <leafNode name="username"> + <properties> + <help>RPKI SSH username</help> + </properties> + </leafNode> + </children> + </node> + </children> + </tagNode> + <leafNode name="polling-period"> + <properties> + <help>RPKI cache polling period (default: 300)</help> + <valueHelp> + <format>u32:1-86400</format> + <description>Polling period in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-86400"/> + </constraint> + </properties> + <defaultValue>300</defaultValue> + </leafNode> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/interface-definitions/protocols-static.xml.in b/interface-definitions/protocols-static.xml.in new file mode 100644 index 000000000..2b1b0082a --- /dev/null +++ b/interface-definitions/protocols-static.xml.in @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="protocols"> + <children> + <node name="static" owner="${vyos_conf_scripts_dir}/protocols_static.py"> + <properties> + <help>Static route parameters</help> + <priority>480</priority> + </properties> + <children> + <tagNode name="arp" owner="${vyos_conf_scripts_dir}/arp.py"> + <properties> + <help>Static ARP translation</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 destination address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + <children> + <leafNode name="hwaddr"> + <properties> + <help>Translation MAC address</help> + <valueHelp> + <format>macaddr</format> + <description>Hardware (MAC) address</description> + </valueHelp> + <constraint> + <validator name="mac-address"/> + </constraint> + </properties> + </leafNode> + </children> + </tagNode> + #include <include/static/static-route-map.xml.i> + #include <include/static/static-route.xml.i> + #include <include/static/static-route6.xml.i> + <tagNode name="table"> + <properties> + <help>Policy route table number</help> + <valueHelp> + <format>u32:1-200</format> + <description>Policy route table number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-200"/> + </constraint> + </properties> + <children> + #include <include/static/static-route.xml.i> + #include <include/static/static-route6.xml.i> + </children> + </tagNode> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/interface-definitions/service_console-server.xml.in b/interface-definitions/service_console-server.xml.in index 59a9fe237..78eb2d0ba 100644 --- a/interface-definitions/service_console-server.xml.in +++ b/interface-definitions/service_console-server.xml.in @@ -5,7 +5,6 @@ <node name="console-server" owner="${vyos_conf_scripts_dir}/service_console-server.py"> <properties> <help>Serial Console Server</help> - <priority>990</priority> </properties> <children> <tagNode name="device"> @@ -13,7 +12,7 @@ <help>System serial interface name (ttyS or ttyUSB)</help> <completionHelp> <script>ls -1 /dev | grep ttyS</script> - <script>ls -1 /dev/serial/by-bus</script> + <script>if [ -d /dev/serial/by-bus ]; then ls -1 /dev/serial/by-bus; fi</script> </completionHelp> <valueHelp> <format>ttySxxx</format> @@ -28,7 +27,7 @@ </constraint> </properties> <children> - #include <include/interface-description.xml.i> + #include <include/interface/interface-description.xml.i> <leafNode name="speed"> <properties> <help>Serial port baud rate</help> @@ -36,7 +35,7 @@ <list>300 1200 2400 4800 9600 19200 38400 57600 115200</list> </completionHelp> <constraint> - <regex>(300|1200|2400|4800|9600|19200|38400|57600|115200)</regex> + <regex>^(300|1200|2400|4800|9600|19200|38400|57600|115200)$</regex> </constraint> </properties> </leafNode> @@ -47,7 +46,7 @@ <list>7 8</list> </completionHelp> <constraint> - <regex>(7|8)</regex> + <validator name="numeric" argument="--range 7-8"/> </constraint> </properties> <defaultValue>8</defaultValue> @@ -59,7 +58,7 @@ <list>1 2</list> </completionHelp> <constraint> - <regex>(1|2)</regex> + <validator name="numeric" argument="--range 1-2"/> </constraint> </properties> <defaultValue>1</defaultValue> @@ -71,7 +70,7 @@ <list>even odd none</list> </completionHelp> <constraint> - <regex>(even|odd|none)</regex> + <regex>^(even|odd|none)$</regex> </constraint> </properties> <defaultValue>none</defaultValue> diff --git a/interface-definitions/service_ipoe-server.xml.in b/interface-definitions/service_ipoe-server.xml.in index ee09d01d6..d0a05aea6 100644 --- a/interface-definitions/service_ipoe-server.xml.in +++ b/interface-definitions/service_ipoe-server.xml.in @@ -111,8 +111,8 @@ </leafNode> </children> </tagNode> - #include <include/accel-name-server.xml.i> - #include <include/accel-client-ipv6-pool.xml.i> + #include <include/accel-ppp/name-server.xml.i> + #include <include/accel-ppp/client-ipv6-pool.xml.i> <node name="authentication"> <properties> <help>Client authentication methods</help> @@ -153,7 +153,7 @@ <properties> <help>Client mac address allowed to receive an IP address</help> <valueHelp> - <format>h:h:h:h:h:h</format> + <format>macaddr</format> <description>Hardware (MAC) address</description> </valueHelp> <constraint> @@ -197,8 +197,8 @@ </tagNode> </children> </tagNode> - #include <include/radius-server.xml.i> - #include <include/accel-radius-additions.xml.i> + #include <include/radius-server-ipv4.xml.i> + #include <include/accel-ppp/radius-additions.xml.i> </children> </node> </children> diff --git a/interface-definitions/service_mdns-repeater.xml.in b/interface-definitions/service_mdns-repeater.xml.in index e21b1b27c..33ef9a434 100644 --- a/interface-definitions/service_mdns-repeater.xml.in +++ b/interface-definitions/service_mdns-repeater.xml.in @@ -13,12 +13,7 @@ <priority>990</priority> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Disable mDNS repeater service</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="interface"> <properties> <help>Interface to repeat mDNS advertisements [REQUIRED]</help> diff --git a/interface-definitions/service_pppoe-server.xml.in b/interface-definitions/service_pppoe-server.xml.in index 6d11f41a0..1d3e8ba29 100644 --- a/interface-definitions/service_pppoe-server.xml.in +++ b/interface-definitions/service_pppoe-server.xml.in @@ -23,14 +23,14 @@ <help>Authentication for remote access PPPoE Server</help> </properties> <children> - #include <include/accel-auth-local-users.xml.i> - #include <include/accel-auth-mode.xml.i> - #include <include/accel-auth-protocols.xml.i> - #include <include/radius-server.xml.i> - #include <include/accel-radius-additions.xml.i> + #include <include/accel-ppp/auth-local-users.xml.i> + #include <include/accel-ppp/auth-mode.xml.i> + #include <include/accel-ppp/auth-protocols.xml.i> + #include <include/radius-server-ipv4.xml.i> + #include <include/accel-ppp/radius-additions.xml.i> <node name="radius"> <children> - #include <include/accel-radius-additions-rate-limit.xml.i> + #include <include/accel-ppp/radius-additions-rate-limit.xml.i> <leafNode name="called-sid-format"> <properties> <help>Format of Called-Station-Id attribute</help> @@ -60,12 +60,12 @@ <help>Pool of client IP addresses (must be within a /24)</help> </properties> <children> - #include <include/accel-client-ip-pool-start-stop.xml.i> - #include <include/accel-client-ip-pool-subnet.xml.i> + #include <include/accel-ppp/client-ip-pool-start-stop.xml.i> + #include <include/accel-ppp/client-ip-pool-subnet.xml.i> </children> </node> - #include <include/accel-client-ipv6-pool.xml.i> - #include <include/accel-name-server.xml.i> + #include <include/accel-ppp/client-ipv6-pool.xml.i> + #include <include/accel-ppp/name-server.xml.i> <tagNode name="interface"> <properties> <help>interface(s) to listen on</help> @@ -95,8 +95,8 @@ </leafNode> </children> </tagNode> - #include <include/accel-gateway-address.xml.i> - #include <include/accel-mtu-128-16384.xml.i> + #include <include/accel-ppp/gateway-address.xml.i> + #include <include/accel-ppp/mtu-128-16384.xml.i> <node name="limits"> <properties> <help>Limits the connection rate from a single source</help> @@ -133,7 +133,7 @@ <multi/> </properties> </leafNode> - #include <include/accel-wins-server.xml.i> + #include <include/accel-ppp/wins-server.xml.i> <node name="ppp-options"> <properties> <help>Advanced protocol options</help> @@ -161,9 +161,9 @@ <valueless /> </properties> </leafNode> - #include <include/accel-ppp-mppe.xml.i> - #include <include/accel-lcp-echo-interval-failure.xml.i> - #include <include/accel-lcp-echo-timeout.xml.i> + #include <include/accel-ppp/ppp-mppe.xml.i> + #include <include/accel-ppp/lcp-echo-interval-failure.xml.i> + #include <include/accel-ppp/lcp-echo-timeout.xml.i> <leafNode name="ipv4"> <properties> <help>IPv4 (IPCP) negotiation algorithm</help> diff --git a/interface-definitions/service_webproxy.xml.in b/interface-definitions/service_webproxy.xml.in index 4cd8138ec..7cb0f7ece 100644 --- a/interface-definitions/service_webproxy.xml.in +++ b/interface-definitions/service_webproxy.xml.in @@ -394,12 +394,7 @@ <help>URL filtering settings</help> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Disable URL filtering</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <node name="squidguard"> <properties> <help>URL filtering via squidGuard redirector</help> diff --git a/interface-definitions/snmp.xml.in b/interface-definitions/snmp.xml.in index 0a0a29f4d..f57103eac 100644 --- a/interface-definitions/snmp.xml.in +++ b/interface-definitions/snmp.xml.in @@ -6,7 +6,7 @@ <node name="snmp" owner="${vyos_conf_scripts_dir}/snmp.py"> <properties> <help>Simple Network Management Protocol (SNMP)</help> - <priority>980</priority> + <priority>900</priority> </properties> <children> <tagNode name="community"> @@ -626,7 +626,7 @@ </tagNode> </children> </node> - #include <include/interface-vrf.xml.i> + #include <include/interface/interface-vrf.xml.i> </children> </node> </children> diff --git a/interface-definitions/ssh.xml.in b/interface-definitions/ssh.xml.in index d94e29427..c2d514b09 100644 --- a/interface-definitions/ssh.xml.in +++ b/interface-definitions/ssh.xml.in @@ -6,7 +6,7 @@ <node name="ssh" owner="${vyos_conf_scripts_dir}/ssh.py"> <properties> <help>Secure Shell (SSH)</help> - <priority>500</priority> + <priority>1000</priority> </properties> <children> <node name="access-control"> @@ -182,7 +182,7 @@ </constraint> </properties> </leafNode> - #include <include/interface-vrf.xml.i> + #include <include/interface/interface-vrf.xml.i> </children> </node> </children> diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in index 812a50c8a..86db3f368 100644 --- a/interface-definitions/system-login.xml.in +++ b/interface-definitions/system-login.xml.in @@ -34,6 +34,7 @@ </constraint> <constraintErrorMessage>Invalid encrypted password for $VAR(../../@).</constraintErrorMessage> </properties> + <defaultValue>!</defaultValue> </leafNode> <leafNode name="plaintext-password"> <properties> @@ -44,7 +45,7 @@ <properties> <help>Remote access public keys</help> <valueHelp> - <format>>identifier<</format> + <format>txt</format> <description>Key identifier used by ssh-keygen (usually of form user@host)</description> </valueHelp> </properties> @@ -61,7 +62,7 @@ </leafNode> <leafNode name="type"> <properties> - <help></help> + <help>Public key type</help> <completionHelp> <list>ssh-dss ssh-rsa ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521 ssh-ed25519</list> </completionHelp> @@ -86,14 +87,14 @@ <description/> </valueHelp> <constraint> - <regex>(ssh-dss|ssh-rsa|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521|ssh-ed25519)</regex> + <regex>^(ssh-dss|ssh-rsa|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521|ssh-ed25519)$</regex> </constraint> </properties> </leafNode> </children> </tagNode> </children> - </node> + </node> <leafNode name="full-name"> <properties> <help>Full name of the user (use quotes for names with spaces)</help> @@ -110,7 +111,7 @@ </leafNode> </children> </tagNode> - #include <include/radius-server.xml.i> + #include <include/radius-server-ipv4-ipv6.xml.i> <node name="radius"> <children> <tagNode name="server"> @@ -119,7 +120,7 @@ <properties> <help>Session timeout</help> <valueHelp> - <format>1-30</format> + <format>u32:1-30</format> <description>Session timeout in seconds (default: 2)</description> </valueHelp> <constraint> @@ -127,22 +128,24 @@ </constraint> <constraintErrorMessage>Timeout must be between 1 and 30 seconds</constraintErrorMessage> </properties> + <defaultValue>2</defaultValue> </leafNode> <leafNode name="priority"> <properties> <help>Server priority</help> <valueHelp> - <format>1-255</format> + <format>u32:1-255</format> <description>Server priority (default: 255)</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-255"/> </constraint> </properties> + <defaultValue>255</defaultValue> </leafNode> </children> </tagNode> - #include <include/interface-vrf.xml.i> + #include <include/interface/interface-vrf.xml.i> </children> </node> </children> diff --git a/interface-definitions/system-option.xml.in b/interface-definitions/system-option.xml.in index 26b78c8a4..f73c1ee08 100644 --- a/interface-definitions/system-option.xml.in +++ b/interface-definitions/system-option.xml.in @@ -36,11 +36,11 @@ <properties> <help>System keyboard layout, type ISO2</help> <completionHelp> - <list>us fr de fi no dk</list> + <list>us fr de fi no dk dvorak</list> </completionHelp> <valueHelp> <format>us</format> - <description>United States of America</description> + <description>United States</description> </valueHelp> <valueHelp> <format>fr</format> @@ -62,6 +62,10 @@ <format>dk</format> <description>Denmark</description> </valueHelp> + <valueHelp> + <format>dvorak</format> + <description>Dvorak</description> + </valueHelp> </properties> <defaultValue>us</defaultValue> </leafNode> diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in index daf98a833..426d7e71c 100644 --- a/interface-definitions/vpn_ipsec.xml.in +++ b/interface-definitions/vpn_ipsec.xml.in @@ -1045,12 +1045,7 @@ </constraint> </properties> </leafNode> - <leafNode name="disable"> - <properties> - <help>Option to disable vpn tunnel</help> - <valueless/> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="esp-group"> <properties> <help>ESP group name</help> diff --git a/interface-definitions/vpn_l2tp.xml.in b/interface-definitions/vpn_l2tp.xml.in index 42da75a64..2d8a8503d 100644 --- a/interface-definitions/vpn_l2tp.xml.in +++ b/interface-definitions/vpn_l2tp.xml.in @@ -12,7 +12,7 @@ <help>Remote access L2TP VPN</help> </properties> <children> - #include <include/accel-mtu-128-16384.xml.i> + #include <include/accel-ppp/mtu-128-16384.xml.i> <leafNode name="outside-address"> <properties> <help>External IP address to which VPN clients will connect</help> @@ -21,8 +21,8 @@ </constraint> </properties> </leafNode> - #include <include/accel-gateway-address.xml.i> - #include <include/accel-name-server.xml.i> + #include <include/accel-ppp/gateway-address.xml.i> + #include <include/accel-ppp/name-server.xml.i> <node name="lns"> <properties> <help>L2TP Network Server (LNS)</help> @@ -143,17 +143,17 @@ </leafNode> </children> </node> - #include <include/accel-wins-server.xml.i> + #include <include/accel-ppp/wins-server.xml.i> <node name="client-ip-pool"> <properties> <help>Pool of client IP addresses (must be within a /24)</help> </properties> <children> - #include <include/accel-client-ip-pool-start-stop.xml.i> - #include <include/accel-client-ip-pool-subnet.xml.i> + #include <include/accel-ppp/client-ip-pool-start-stop.xml.i> + #include <include/accel-ppp/client-ip-pool-subnet.xml.i> </children> </node> - #include <include/accel-client-ipv6-pool.xml.i> + #include <include/accel-ppp/client-ipv6-pool.xml.i> <leafNode name="description"> <properties> <help>Description for L2TP remote-access settings</help> @@ -209,15 +209,15 @@ <multi /> </properties> </leafNode> - #include <include/accel-ppp-mppe.xml.i> - #include <include/accel-auth-mode.xml.i> - #include <include/accel-auth-local-users.xml.i> - #include <include/radius-server.xml.i> + #include <include/accel-ppp/ppp-mppe.xml.i> + #include <include/accel-ppp/auth-mode.xml.i> + #include <include/accel-ppp/auth-local-users.xml.i> + #include <include/radius-server-ipv4.xml.i> <node name="radius"> <children> <tagNode name="server"> <children> - #include <include/accel-radius-additions-disable-accounting.xlm.in> + #include <include/accel-ppp/radius-additions-disable-accounting.xml.i> <leafNode name="fail-time"> <properties> <help>Mark server unavailable for <n> seconds on failure</help> @@ -307,7 +307,7 @@ <help>Advanced protocol options</help> </properties> <children> - #include <include/accel-lcp-echo-interval-failure.xml.i> + #include <include/accel-ppp/lcp-echo-interval-failure.xml.i> </children> </node> </children> diff --git a/interface-definitions/vpn_openconnect.xml.in b/interface-definitions/vpn_openconnect.xml.in index ccf537e04..f64aa7f23 100644 --- a/interface-definitions/vpn_openconnect.xml.in +++ b/interface-definitions/vpn_openconnect.xml.in @@ -42,12 +42,7 @@ <help>User name for authentication</help> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Option to disable a SSL VPN Server user</help> - <valueless /> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="password"> <properties> <help>Password for authentication</help> @@ -57,7 +52,7 @@ </tagNode> </children> </node> - #include <include/radius-server.xml.i> + #include <include/radius-server-ipv4.xml.i> <node name="radius"> <children> <leafNode name="timeout"> @@ -195,7 +190,7 @@ </leafNode> </children> </node> - #include <include/accel-name-server.xml.i> + #include <include/accel-ppp/name-server.xml.i> </children> </node> </children> diff --git a/interface-definitions/vpn_pptp.xml.in b/interface-definitions/vpn_pptp.xml.in index b17138e33..7cf584a18 100644 --- a/interface-definitions/vpn_pptp.xml.in +++ b/interface-definitions/vpn_pptp.xml.in @@ -12,7 +12,7 @@ <help>Remote access PPTP VPN</help> </properties> <children> - #include <include/accel-mtu-128-16384.xml.i> + #include <include/accel-ppp/mtu-128-16384.xml.i> <leafNode name="outside-address"> <properties> <help>External IP address to which VPN clients will connect</help> @@ -34,16 +34,16 @@ <multi/> </properties> </leafNode> - #include <include/accel-wins-server.xml.i> + #include <include/accel-ppp/wins-server.xml.i> <node name="client-ip-pool"> <properties> <help>Pool of client IP addresses (must be within a /24)</help> </properties> <children> - #include <include/accel-client-ip-pool-start-stop.xml.i> + #include <include/accel-ppp/client-ip-pool-start-stop.xml.i> </children> </node> - #include <include/accel-gateway-address.xml.i> + #include <include/accel-ppp/gateway-address.xml.i> <node name="authentication"> <properties> <help>Authentication for remote access PPTP VPN</help> @@ -93,7 +93,7 @@ </completionHelp> </properties> </leafNode> - #include <include/accel-auth-mode.xml.i> + #include <include/accel-ppp/auth-mode.xml.i> <node name="local-users"> <properties> <help>Local user authentication for remote access PPTP VPN</help> @@ -104,11 +104,7 @@ <help>User name for authentication</help> </properties> <children> - <leafNode name="disable"> - <properties> - <help>Option to disable a PPTP Server user</help> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <leafNode name="password"> <properties> <help>Password for authentication</help> @@ -123,8 +119,8 @@ </tagNode> </children> </node> - #include <include/radius-server.xml.i> - #include <include/accel-radius-additions.xml.i> + #include <include/radius-server-ipv4.xml.i> + #include <include/accel-ppp/radius-additions.xml.i> </children> </node> </children> diff --git a/interface-definitions/vpn_sstp.xml.in b/interface-definitions/vpn_sstp.xml.in index 134858608..e4ade844d 100644 --- a/interface-definitions/vpn_sstp.xml.in +++ b/interface-definitions/vpn_sstp.xml.in @@ -13,38 +13,38 @@ <help>Authentication for remote access SSTP Server</help> </properties> <children> - #include <include/accel-auth-local-users.xml.i> - #include <include/accel-auth-mode.xml.i> - #include <include/accel-auth-protocols.xml.i> - #include <include/radius-server.xml.i> - #include <include/accel-radius-additions.xml.i> + #include <include/accel-ppp/auth-local-users.xml.i> + #include <include/accel-ppp/auth-mode.xml.i> + #include <include/accel-ppp/auth-protocols.xml.i> + #include <include/radius-server-ipv4.xml.i> + #include <include/accel-ppp/radius-additions.xml.i> <node name="radius"> <children> - #include <include/accel-radius-additions-rate-limit.xml.i> + #include <include/accel-ppp/radius-additions-rate-limit.xml.i> </children> </node> </children> </node> - #include <include/interface-mtu-68-1500.xml.i> - #include <include/accel-gateway-address.xml.i> - #include <include/accel-name-server.xml.i> + #include <include/interface/interface-mtu-68-1500.xml.i> + #include <include/accel-ppp/gateway-address.xml.i> + #include <include/accel-ppp/name-server.xml.i> <node name="client-ip-pool"> <properties> <help>Client IP pools and gateway setting</help> </properties> <children> - #include <include/accel-client-ip-pool-subnet.xml.i> + #include <include/accel-ppp/client-ip-pool-subnet.xml.i> </children> </node> - #include <include/accel-client-ipv6-pool.xml.i> + #include <include/accel-ppp/client-ipv6-pool.xml.i> <node name="ppp-options"> <properties> <help>PPP (Point-to-Point Protocol) settings</help> </properties> <children> - #include <include/accel-ppp-mppe.xml.i> - #include <include/accel-lcp-echo-interval-failure.xml.i> - #include <include/accel-lcp-echo-timeout.xml.i> + #include <include/accel-ppp/ppp-mppe.xml.i> + #include <include/accel-ppp/lcp-echo-interval-failure.xml.i> + #include <include/accel-ppp/lcp-echo-timeout.xml.i> </children> </node> <node name="ssl"> diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in index 159f4ea3e..8a56b1bc0 100644 --- a/interface-definitions/vrf.xml.in +++ b/interface-definitions/vrf.xml.in @@ -3,8 +3,8 @@ <node name="vrf" owner="${vyos_conf_scripts_dir}/vrf.py"> <properties> <help>Virtual Routing and Forwarding</help> - <!-- must be before any interface creation --> - <priority>60</priority> + <!-- must be before any interface, check /opt/vyatta/sbin/priority.pl --> + <priority>299</priority> </properties> <children> <leafNode name="bind-to-all"> @@ -15,17 +15,63 @@ </leafNode> <tagNode name="name"> <properties> - <help>VRF instance name</help> + <help>Virtual Routing and Forwarding instance</help> <constraint> <validator name="vrf-name"/> </constraint> <constraintErrorMessage>VRF instance name must be 15 characters or less and can not\nbe named as regular network interfaces.\n</constraintErrorMessage> <valueHelp> - <format>name</format> - <description>Instance name</description> + <format>txt</format> + <description>VRF instance name</description> </valueHelp> </properties> <children> + #include <include/interface/interface-description.xml.i> + #include <include/interface/interface-disable.xml.i> + <node name="protocols"> + <properties> + <help>Routing protocol parameters</help> + </properties> + <children> + <node name="bgp" owner="${vyos_conf_scripts_dir}/protocols_bgp.py $VAR(../../@)"> + <properties> + <help>Border Gateway Protocol (BGP)</help> + <priority>821</priority> + </properties> + <children> + #include <include/bgp/bgp-common-config.xml.i> + </children> + </node> + <node name="isis" owner="${vyos_conf_scripts_dir}/protocols_isis.py $VAR(../../@)"> + <properties> + <help>Intermediate System to Intermediate System (IS-IS)</help> + <priority>611</priority> + </properties> + <children> + #include <include/isis/isis-common-config.xml.i> + </children> + </node> + <node name="ospf" owner="${vyos_conf_scripts_dir}/protocols_ospf.py $VAR(../../@)"> + <properties> + <help>Open Shortest Path First (OSPF)</help> + <priority>621</priority> + </properties> + <children> + #include <include/ospf/ospf-common-config.xml.i> + </children> + </node> + <node name="static" owner="${vyos_conf_scripts_dir}/protocols_static.py $VAR(../../@)"> + <properties> + <help>Static route parameters</help> + <priority>481</priority> + </properties> + <children> + #include <include/static/static-route.xml.i> + #include <include/static/static-route6.xml.i> + </children> + </node> + </children> + </node> <leafNode name="table"> <properties> <help>Routing table associated with this instance</help> @@ -39,7 +85,6 @@ <constraintErrorMessage>VRF routing table must be in range from 100 to 2147483647</constraintErrorMessage> </properties> </leafNode> - #include <include/interface-description.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/vrrp.xml.in b/interface-definitions/vrrp.xml.in index c6a32930f..3c4c9b83c 100644 --- a/interface-definitions/vrrp.xml.in +++ b/interface-definitions/vrrp.xml.in @@ -73,12 +73,7 @@ <help>Group description</help> </properties> </leafNode> - <leafNode name="disable"> - <properties> - <valueless/> - <help>Disable VRRP group</help> - </properties> - </leafNode> + #include <include/generic-disable-node.xml.i> <node name="health-check"> <properties> <help>Health check script</help> @@ -217,6 +212,15 @@ </constraint> </properties> </leafNode> + <leafNode name="mode-force"> + <properties> + <valueless/> + <help>Disable VRRP state checking (deprecated, will be removed in VyOS 1.4)</help> + <constraint> + <validator name="script"/> + </constraint> + </properties> + </leafNode> </children> </node> <leafNode name="virtual-address"> diff --git a/op-mode-definitions/add-system-image.xml b/op-mode-definitions/add-system-image.xml.in index 3dc1c67ab..67d8aa3b4 100644 --- a/op-mode-definitions/add-system-image.xml +++ b/op-mode-definitions/add-system-image.xml.in @@ -11,7 +11,7 @@ <properties> <help>Add a new image to the system</help> <completionHelp> - <list>/path/to/vyos-image.iso http://example.com/vyos-image.iso</list> + <list>/path/to/vyos-image.iso "http://example.com/vyos-image.iso"</list> </completionHelp> </properties> <command>sudo ${vyatta_sbindir}/install-image --url "${4}"</command> diff --git a/op-mode-definitions/clear-ip.xml b/op-mode-definitions/clear-ip.xml.in index 3c75ed11b..3c75ed11b 100644 --- a/op-mode-definitions/clear-ip.xml +++ b/op-mode-definitions/clear-ip.xml.in diff --git a/op-mode-definitions/clear-ipv6.xml b/op-mode-definitions/clear-ipv6.xml.in index c062102fc..c062102fc 100644 --- a/op-mode-definitions/clear-ipv6.xml +++ b/op-mode-definitions/clear-ipv6.xml.in diff --git a/op-mode-definitions/configure.xml b/op-mode-definitions/configure.xml.in index 3dd5a0f45..3dd5a0f45 100644 --- a/op-mode-definitions/configure.xml +++ b/op-mode-definitions/configure.xml.in diff --git a/op-mode-definitions/connect.xml b/op-mode-definitions/connect.xml.in index 1ec62949a..1ec62949a 100644 --- a/op-mode-definitions/connect.xml +++ b/op-mode-definitions/connect.xml.in diff --git a/op-mode-definitions/date.xml b/op-mode-definitions/date.xml.in index 15a69dbd9..15a69dbd9 100644 --- a/op-mode-definitions/date.xml +++ b/op-mode-definitions/date.xml.in diff --git a/op-mode-definitions/dhcp.xml b/op-mode-definitions/dhcp.xml.in index 48752cfd5..1dacbd5ba 100644 --- a/op-mode-definitions/dhcp.xml +++ b/op-mode-definitions/dhcp.xml.in @@ -123,7 +123,7 @@ <children> <node name="dhcp"> <properties> - <help>Restart DHCP processes</help> + <help>Restart DHCP server processes</help> </properties> <children> <node name="server"> @@ -142,7 +142,7 @@ </node> <node name="dhcpv6"> <properties> - <help>Restart DHCPv6 processes</help> + <help>Restart DHCPv6 server processes</help> </properties> <children> <node name="server"> diff --git a/op-mode-definitions/disconnect.xml b/op-mode-definitions/disconnect.xml.in index bf2c37b89..bf2c37b89 100644 --- a/op-mode-definitions/disconnect.xml +++ b/op-mode-definitions/disconnect.xml.in diff --git a/op-mode-definitions/disks.xml b/op-mode-definitions/disks.xml.in index fb39c4f3c..fb39c4f3c 100644 --- a/op-mode-definitions/disks.xml +++ b/op-mode-definitions/disks.xml.in diff --git a/op-mode-definitions/dns-dynamic.xml b/op-mode-definitions/dns-dynamic.xml.in index 9c37874fb..9c37874fb 100644 --- a/op-mode-definitions/dns-dynamic.xml +++ b/op-mode-definitions/dns-dynamic.xml.in diff --git a/op-mode-definitions/dns-forwarding.xml b/op-mode-definitions/dns-forwarding.xml.in index 23de97704..36fe6b5ef 100644 --- a/op-mode-definitions/dns-forwarding.xml +++ b/op-mode-definitions/dns-forwarding.xml.in @@ -45,7 +45,7 @@ <children> <node name="dns"> <properties> - <help>Restart a DNS service</help> + <help>Restart specific DNS service</help> </properties> <children> <leafNode name="forwarding"> diff --git a/op-mode-definitions/flow-accounting-op.xml b/op-mode-definitions/flow-accounting-op.xml.in index 912805d59..b847338f9 100644 --- a/op-mode-definitions/flow-accounting-op.xml +++ b/op-mode-definitions/flow-accounting-op.xml.in @@ -55,7 +55,7 @@ <children> <leafNode name="flow-accounting"> <properties> - <help>Restart flow-accounting service</help> + <help>Restart (net)flow accounting process</help> </properties> <command>${vyos_op_scripts_dir}/flow_accounting_op.py --action restart</command> </leafNode> diff --git a/op-mode-definitions/force-arp.xml b/op-mode-definitions/force-arp.xml.in index f9f7c7643..f9f7c7643 100644 --- a/op-mode-definitions/force-arp.xml +++ b/op-mode-definitions/force-arp.xml.in diff --git a/op-mode-definitions/force-ipv6-nd.xml b/op-mode-definitions/force-ipv6-nd.xml.in index 49de097f6..49de097f6 100644 --- a/op-mode-definitions/force-ipv6-nd.xml +++ b/op-mode-definitions/force-ipv6-nd.xml.in diff --git a/op-mode-definitions/force-ipv6-rd.xml b/op-mode-definitions/force-ipv6-rd.xml.in index 8c901af25..8c901af25 100644 --- a/op-mode-definitions/force-ipv6-rd.xml +++ b/op-mode-definitions/force-ipv6-rd.xml.in diff --git a/op-mode-definitions/force-mtu-host.xml b/op-mode-definitions/force-mtu-host.xml.in index b92179f11..b92179f11 100644 --- a/op-mode-definitions/force-mtu-host.xml +++ b/op-mode-definitions/force-mtu-host.xml.in diff --git a/op-mode-definitions/generate-macsec-key.xml b/op-mode-definitions/generate-macsec-key.xml.in index 40d2b9061..40d2b9061 100644 --- a/op-mode-definitions/generate-macsec-key.xml +++ b/op-mode-definitions/generate-macsec-key.xml.in diff --git a/op-mode-definitions/generate-ssh-server-key.xml b/op-mode-definitions/generate-ssh-server-key.xml deleted file mode 100644 index a6ebf1b78..000000000 --- a/op-mode-definitions/generate-ssh-server-key.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0"?> -<interfaceDefinition> - <node name="generate"> - <properties> - <help>Generate an object</help> - </properties> - <children> - <node name="ssh-server-key"> - <properties> - <help>Regenerate the host SSH keys and restart the SSH server</help> - </properties> - <command>${vyos_op_scripts_dir}/generate_ssh_server_key.py</command> - </node> - </children> - </node> -</interfaceDefinition> diff --git a/op-mode-definitions/generate-ssh-server-key.xml.in b/op-mode-definitions/generate-ssh-server-key.xml.in new file mode 100644 index 000000000..86bb1b1bd --- /dev/null +++ b/op-mode-definitions/generate-ssh-server-key.xml.in @@ -0,0 +1,32 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="generate"> + <properties> + <help>Generate an object/key</help> + </properties> + <children> + <node name="ssh"> + <properties> + <help>Generate SSH related keypairs</help> + </properties> + <children> + <node name="server-key"> + <properties> + <help>Re-generate SSH host keys and restart SSH server</help> + </properties> + <command>${vyos_op_scripts_dir}/generate_ssh_server_key.py</command> + </node> + <tagNode name="client-key"> + <properties> + <help>Re-generate SSH client keypair</help> + <completionHelp> + <list><filename></list> + </completionHelp> + </properties> + <command>ssh-keygen -t rsa -f "$4" -N ""</command> + </tagNode> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/igmp-proxy.xml b/op-mode-definitions/igmp-proxy.xml.in index 8533138d7..8533138d7 100644 --- a/op-mode-definitions/igmp-proxy.xml +++ b/op-mode-definitions/igmp-proxy.xml.in diff --git a/op-mode-definitions/include/bgp-afi-common.xml.i b/op-mode-definitions/include/bgp-afi-common.xml.i new file mode 100644 index 000000000..06cfc42a5 --- /dev/null +++ b/op-mode-definitions/include/bgp-afi-common.xml.i @@ -0,0 +1,40 @@ +<!-- included start from bgp-afi-common.xml.i --> +<tagNode name="community"> + <properties> + <help>Community number where AA and NN are (0-65535)</help> + <completionHelp> + <list>AA:NN</list> + </completionHelp> + </properties> + <children> + <leafNode name="exact-match"> + <properties> + <help>Exact match of the communities</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</tagNode> +<tagNode name="large-community"> + <properties> + <help>List of large-community numbers</help> + <completionHelp> + <list>AA:BB:CC</list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</tagNode> +<leafNode name="statistics"> + <properties> + <help>RIB advertisement statistics</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<leafNode name="summary"> + <properties> + <help>Summary of BGP neighbor status</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp-afi-ipv4-ipv6-common.xml.i b/op-mode-definitions/include/bgp-afi-ipv4-ipv6-common.xml.i new file mode 100644 index 000000000..dc0926375 --- /dev/null +++ b/op-mode-definitions/include/bgp-afi-ipv4-ipv6-common.xml.i @@ -0,0 +1,243 @@ +<!-- included start from bgp-afi-ipv4-ipv6-common.xml.i --> +<node name="community"> + <properties> + <help>Display routes matching the community</help> + </properties> + <children> + <leafNode name="accept-own"> + <properties> + <help>Should accept local VPN route if exported and imported into different VRF (well-known community)</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="accept-own-nexthop"> + <properties> + <help>Should accept VPN route with local nexthop (well-known community)</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="blackhole"> + <properties> + <help>Inform EBGP peers to blackhole traffic to prefix (well-known community)</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="exact-match"> + <properties> + <help>Exact match of the communities</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="graceful-shutdown"> + <properties> + <help>Graceful shutdown (well-known community)</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="llgr-stale"> + <properties> + <help>Staled Long-lived Graceful Restart VPN route (well-known community)</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="local-AS"> + <properties> + <help>Do not send outside local AS (well-known community)</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="no-advertise"> + <properties> + <help>Do not advertise to any peer (well-known community)</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="no-export"> + <properties> + <help>Do not export to next AS (well-known community)</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="no-llgr"> + <properties> + <help>Removed because Long-lived Graceful Restart was not enabled for VPN route (well-known community)</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="no-peer"> + <properties> + <help>Do not export to any peer (well-known community)</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="route-filter-translated-v4"> + <properties> + <help>RT translated VPNv4 route filtering (well-known community)</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="route-filter-translated-v6"> + <properties> + <help>RT translated VPNv6 route filtering (well-known community)</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="route-filter-v4"> + <properties> + <help>RT VPNv4 route filtering (well-known community)</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="route-filter-v6"> + <properties> + <help>RT VPNv6 route filtering (well-known community)</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</node> +<tagNode name="community-list"> + <properties> + <help>Display routes matching the community-list</help> + <completionHelp> + <list>1-500 name</list> + </completionHelp> + </properties> + <children> + <leafNode name="exact-match"> + <properties> + <help>Show BGP routes exactly matching specified community list</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</tagNode> +<node name="dampening"> + <properties> + <help>Display detailed information about dampening</help> + </properties> + <children> + <leafNode name="dampened-paths"> + <properties> + <help>Display paths suppressed due to dampening</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="flap-statistics"> + <properties> + <help>Display flap statistics of routes</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="parameters"> + <properties> + <help>Display detail of configured dampening parameters</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> +</node> +<tagNode name="filter-list"> + <properties> + <help>Display routes conforming to the filter-list</help> + <completionHelp> + <script>vtysh -c 'show bgp as-path-access-list' | grep 'AS path access list' | awk '{print $NF}'</script> + </completionHelp> + </properties> +</tagNode> +<node name="large-community"> + <properties> + <help>Show BGP routes matching the specified large-communities</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</node> +<leafNode name="neighbors"> + <properties> + <help>Detailed information on TCP and BGP neighbor connections</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<tagNode name="neighbors"> + <properties> + <help>Show detailed BGP IPv4 unicast neighbor information</help> + <completionHelp> + <script>vtysh -c 'show bgp summary' | awk '{print $1'} | grep -e '^[0-9a-f]'</script> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <leafNode name="advertised-routes"> + <properties> + <help>Show routes advertised to a BGP neighbor</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="dampened-routes"> + <properties> + <help>Show dampened routes received from BGP neighbor</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="flap-statistics"> + <properties> + <help>Show flap statistics of the routes learned from BGP neighbor</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="prefix-counts"> + <properties> + <help>Show detailed prefix count information for BGP neighbor</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <node name="received"> + <properties> + <help>Show information received from BGP neighbor</help> + </properties> + <children> + <leafNode name="prefix-filter"> + <properties> + <help>Show prefixlist filter</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </node> + <leafNode name="received-routes"> + <properties> + <help>Show received routes from BGP neighbor</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="routes"> + <properties> + <help>Show routes learned from BGP neighbor</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> +</tagNode> +<tagNode name="prefix-list"> + <properties> + <help>Display routes conforming to the prefix-list</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</tagNode> +<tagNode name="regexp"> + <properties> + <help>Display routes matching the AS path regular expression</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</tagNode> +<tagNode name="route-map"> + <properties> + <help>Show BGP routes matching the specified route map</help> + <completionHelp> + <path>policy route-map</path> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</tagNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp-common.xml.i b/op-mode-definitions/include/bgp-common.xml.i new file mode 100644 index 000000000..a1154d965 --- /dev/null +++ b/op-mode-definitions/include/bgp-common.xml.i @@ -0,0 +1,170 @@ +<!-- included start from bgp-common.xml.i --> +<leafNode name="attribute-info"> + <properties> + <help>Show BGP attribute information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<leafNode name="cidr-only"> + <properties> + <help>Display only routes with non-natural netmasks</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<leafNode name="community-info"> + <properties> + <help>List all bgp community information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +#include <include/bgp-afi-common.xml.i> +#include <include/bgp-afi-ipv4-ipv6-common.xml.i> +<tagNode name="prefix-list"> + <properties> + <completionHelp> + <path>policy prefix-list</path> + </completionHelp> + </properties> +</tagNode> +<node name="ipv4"> + <properties> + <help>Show BGP IPv4 information</help> + </properties> + <children> + <node name="unicast"> + <properties> + <help>Show BGP IPv4 unicast information</help> + </properties> + <children> + <leafNode name="cidr-only"> + <properties> + <help>Display only routes with non-natural netmasks</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <node name="community"> + <properties> + <help>Show BGP routes matching the communities</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </node> + <tagNode name="community"> + <properties> + <help>Display routes matching the specified communities</help> + <completionHelp> +<list><AA:NN> local-AS no-advertise no-export</list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <tagNode name="community-list"> + <properties> + <help>Show BGP routes matching specified community list</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <leafNode name="exact-match"> +<properties> + <help>Show BGP routes exactly matching specified community list</help> +</properties> +<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </tagNode> + <tagNode name="neighbors"> + <properties> + <help>Show detailed BGP IPv4 unicast neighbor information</help> + <completionHelp> +<script>vtysh -c "show ip bgp ipv4 unicast summary" | awk '{print $1}' | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b"</script> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <leafNode name="advertised-routes"> +<properties> + <help>Show routes advertised to a BGP neighbor</help> +</properties> +<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="prefix-counts"> +<properties> + <help>Show detailed prefix count information</help> +</properties> +<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="received-routes"> +<properties> + <help>Show the received routes from neighbor</help> +</properties> +<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="routes"> +<properties> + <help>Show routes learned from neighbor</help> +</properties> +<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </tagNode> + <leafNode name="paths"> + <properties> + <help>Show BGP path information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <tagNode name="prefix-list"> + <properties> + <help>Show BGP routes matching the specified prefix list</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <tagNode name="regexp"> + <properties> + <help>Show BGP routes matching the specified AS path regular expression</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <tagNode name="route-map"> + <properties> + <help>Show BGP routes matching the specified route map</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <leafNode name="summary"> + <properties> + <help>Show summary of BGP information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </node> + <tagNode name="unicast"> + <properties> + <help>Show BGP information for specified IP address or prefix</help> + <completionHelp> + <list><x.x.x.x> <x.x.x.x/x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + </children> +</node> +<leafNode name="large-community-info"> + <properties> + <help>Show BGP large-community information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<leafNode name="memory"> + <properties> + <help>Show BGP memory usage</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<leafNode name="paths"> + <properties> + <help>Show BGP path information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp-prefix-bestpath-multipath.xml.i b/op-mode-definitions/include/bgp-prefix-bestpath-multipath.xml.i new file mode 100644 index 000000000..224fa6b45 --- /dev/null +++ b/op-mode-definitions/include/bgp-prefix-bestpath-multipath.xml.i @@ -0,0 +1,20 @@ +<!-- included start from bgp-prefix-bestpath-multipath.xml.i --> +<leafNode name="bestpath"> + <properties> + <help>Display only the bestpath</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<leafNode name="multipath"> + <properties> + <help>Display only multipaths</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<leafNode name="longer-prefixes"> + <properties> + <help>Display route and more specific routes</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/isis-common.xml.i b/op-mode-definitions/include/isis-common.xml.i new file mode 100644 index 000000000..b1ee3e241 --- /dev/null +++ b/op-mode-definitions/include/isis-common.xml.i @@ -0,0 +1,179 @@ +<!-- included start from isis-common.xml.i -->
+<node name="database">
+ <properties>
+ <help>Show IS-IS link state database</help>
+ </properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show detailed information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
+ </children>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</node>
+<tagNode name="database">
+ <properties>
+ <help>Show IS-IS link state database PDU</help>
+ <completionHelp>
+ <list>lsp-id detail</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</tagNode>
+<leafNode name="hostname">
+ <properties>
+ <help>Show IS-IS dynamic hostname mapping</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</leafNode>
+<node name="interface">
+ <properties>
+ <help>Show IS-IS interfaces</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ </properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show detailed information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
+ </children>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</node>
+<tagNode name="interface">
+ <properties>
+ <help>Show specific IS-IS interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</tagNode>
+<node name="mpls-te">
+ <properties>
+ <help>Show IS-IS MPLS traffic engineering information</help>
+ </properties>
+ <children>
+ <leafNode name="router">
+ <properties>
+ <help>Show router information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
+ <leafNode name="interface">
+ <properties>
+ <help>Show interface information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
+ <tagNode name="interface">
+ <properties>
+ <help>Show specific IS-IS interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </tagNode>
+ </children>
+</node>
+<node name="neighbor">
+ <properties>
+ <help>Show IS-IS neighbor adjacencies</help>
+ </properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show detailed information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
+ </children>
+ <command>vtysh -c "show isis neighbor"</command>
+</node>
+<tagNode name="neighbor">
+ <properties>
+ <help>Show specific IS-IS neighbor adjacency </help>
+ <completionHelp>
+ <list>system-id</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</tagNode>
+<node name="route">
+ <properties>
+ <help>Show IS-IS routing table</help>
+ </properties>
+ <children>
+ <leafNode name="level-1">
+ <properties>
+ <help>Show level-1 routes</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
+ <leafNode name="level-2">
+ <properties>
+ <help>Show level-2 routes</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
+ </children>
+ <command>vtysh -c "show isis route"</command>
+</node>
+<node name="segment-routing">
+ <properties>
+ <help>Show IS-IS Segment-Routing (SPRING) information</help>
+ </properties>
+ <children>
+ <leafNode name="node">
+ <properties>
+ <help>Show node information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
+ <leafNode name="prefix-sids">
+ <properties>
+ <help>Show prefix segment IDs</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
+ </children>
+</node>
+<leafNode name="spf-delay-ietf">
+ <properties>
+ <help>Show IS-IS SPF delay parameters</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</leafNode>
+<leafNode name="summary">
+ <properties>
+ <help>Show IS-IS information summary</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</leafNode>
+<node name="topology">
+ <properties>
+ <help>Show IS-IS paths to Intermediate Systems</help>
+ </properties>
+ <children>
+ <leafNode name="level-1">
+ <properties>
+ <help>Show level-1 routes</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
+ <leafNode name="level-2">
+ <properties>
+ <help>Show level-2 routes</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
+ </children>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</node>
+<!-- included end -->
diff --git a/op-mode-definitions/include/monitor-background.xml.i b/op-mode-definitions/include/monitor-background.xml.i new file mode 100644 index 000000000..9931127e3 --- /dev/null +++ b/op-mode-definitions/include/monitor-background.xml.i @@ -0,0 +1,21 @@ +<!-- included start from monitor-background.xml.i --> +<node name="background"> + <properties> + <help>Monitor in background</help> + </properties> + <children> + <node name="start"> + <properties> + <help>Start background monitoring</help> + </properties> + <command>${vyatta_bindir}/vyatta-monitor-background ${3^^} ${3}</command> + </node> + <node name="stop"> + <properties> + <help>Stop background monitoring</help> + </properties> + <command>${vyatta_bindir}/vyatta-monitor-background-stop ${3^^}</command> + </node> + </children> +</node> +<!-- included end --> diff --git a/op-mode-definitions/include/monitor-no-ospf-packet-detail.xml.i b/op-mode-definitions/include/monitor-no-ospf-packet-detail.xml.i new file mode 100644 index 000000000..8dbb5acea --- /dev/null +++ b/op-mode-definitions/include/monitor-no-ospf-packet-detail.xml.i @@ -0,0 +1,36 @@ +<!-- included start from monitor-ospf-packet-detail.xml.i --> +<node name="detail"> + <properties> + <help>Disable detailed OSPF packet debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:3}"</command> +</node> +<node name="recv"> + <properties> + <help>Disable OSPF recv packet debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:3}"</command> + <children> + <node name="detail"> + <properties> + <help>Disable detailed OSPF recv packet debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:3}"</command> + </node> + </children> +</node> +<node name="send"> + <properties> + <help>Disable OSPF send packet debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:3}"</command> + <children> + <node name="detail"> + <properties> + <help>Disable detailed OSPF send packet debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:3}"</command> + </node> + </children> +</node> +<!-- included end --> diff --git a/op-mode-definitions/include/monitor-ospf-packet-detail.xml.i b/op-mode-definitions/include/monitor-ospf-packet-detail.xml.i new file mode 100644 index 000000000..a4bd33673 --- /dev/null +++ b/op-mode-definitions/include/monitor-ospf-packet-detail.xml.i @@ -0,0 +1,36 @@ +<!-- included start from monitor-ospf-packet-detail.xml.i --> +<node name="detail"> + <properties> + <help>Enable detailed OSPF packet debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:3}"</command> +</node> +<node name="recv"> + <properties> + <help>Enable OSPF recv packet debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:3}"</command> + <children> + <node name="detail"> + <properties> + <help>Enable detailed OSPF recv packet debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:3}"</command> + </node> + </children> +</node> +<node name="send"> + <properties> + <help>Enable OSPF send packet debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:3}"</command> + <children> + <node name="detail"> + <properties> + <help>Enable detailed OSPF send packet debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:3}"</command> + </node> + </children> +</node> +<!-- included end --> diff --git a/op-mode-definitions/include/ospf-common.xml.i b/op-mode-definitions/include/ospf-common.xml.i new file mode 100644 index 000000000..0edc3c37f --- /dev/null +++ b/op-mode-definitions/include/ospf-common.xml.i @@ -0,0 +1,559 @@ +<!-- included start from ospf-common.xml.i --> +<leafNode name="border-routers"> + <properties> + <help>Show IPv4 OSPF border-routers information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<node name="database"> + <properties> + <help>Show IPv4 OSPF database information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <node name="asbr-summary"> + <properties> + <help>Show IPv4 OSPF ASBR summary database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF ASBR summary database for given address of advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <node name="adv-router"> + <properties> + <help>Show IPv4 OSPF ASBR summary database for given address of advertised router</help> + </properties> + </node> + </children> + </node> + <tagNode name="asbr-summary"> + <properties> + <help>Show IPv4 OSPF ASBR summary database information of given address</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <node name="adv-router"> + <properties> + <help>Show advertising router link states</help> + </properties> + </node> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF ASBR summary database of given address for given advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <leafNode name="self-originate"> + <properties> + <help>Show summary of self-originate IPv4 OSPF ASBR database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </tagNode> + <node name="external"> + <properties> + <help>Show IPv4 OSPF external database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF external database for specified IP address of advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <node name="adv-router"> + <properties> + <help>Show IPv4 OSPF external database for specified IP address of advertised router</help> + </properties> + </node> + </children> + </node> + <tagNode name="external"> + <properties> + <help>Show IPv4 OSPF external database information of specified IP address</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <node name="adv-router"> + <properties> + <help>Show advertising router link states</help> + </properties> + </node> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF external database of specified IP address for specified advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <leafNode name="self-originate"> + <properties> + <help>Show self-originate IPv4 OSPF external database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </tagNode> + <leafNode name="max-age"> + <properties> + <help>Show IPv4 OSPF max-age database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <node name="network"> + <properties> + <help>Show IPv4 OSPF network database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF network database for specified IP address of advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <node name="adv-router"> + <properties> + <help>Show IPv4 OSPF network database for given address of advertised router</help> + </properties> + </node> + </children> + </node> + <tagNode name="network"> + <properties> + <help>Show IPv4 OSPF network database information of specified IP address</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <node name="adv-router"> + <properties> + <help>Show advertising router link states</help> + </properties> + </node> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF network database of specified IP address for specified advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <leafNode name="self-originate"> + <properties> + <help>Show self-originate IPv4 OSPF network database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </tagNode> + <node name="nssa-external"> + <properties> + <help>Show IPv4 OSPF NSSA external database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF NSSA external database for specified IP address of advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <node name="adv-router"> + <properties> + <help>Show IPv4 OSPF NSSA external database for specified IP address of advertised router</help> + </properties> + </node> + </children> + </node> + <tagNode name="nssa-external"> + <properties> + <help>Show IPv4 OSPF NSSA external database information of specified IP address</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <node name="adv-router"> + <properties> + <help>Show advertising router link states</help> + </properties> + </node> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF NSSA external database of specified IP address for specified advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <leafNode name="self-originate"> + <properties> + <help>Show self-originate IPv4 OSPF NSSA external database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </tagNode> + <node name="opaque-area"> + <properties> + <help>Show IPv4 OSPF opaque-area database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF opaque-area database for specified IP address of advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <node name="adv-router"> + <properties> + <help>Show IPv4 OSPF opaque-area database for specified IP address of advertised router</help> + </properties> + </node> + </children> + </node> + <tagNode name="opaque-area"> + <properties> + <help>Show IPv4 OSPF opaque-area database information of specified IP address</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <node name="adv-router"> + <properties> + <help>Show advertising router link states</help> + </properties> + </node> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF opaque-area database of specified IP address for specified advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <leafNode name="self-originate"> + <properties> + <help>Show self-originate IPv4 OSPF opaque-area database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </tagNode> + <node name="opaque-as"> + <properties> + <help>Show IPv4 OSPF opaque-as database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF opaque-as database for specified IP address of advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <node name="adv-router"> + <properties> + <help>Show IPv4 OSPF opaque-as database for specified IP address of advertised router</help> + </properties> + </node> + </children> + </node> + <tagNode name="opaque-as"> + <properties> + <help>Show IPv4 OSPF opaque-as database information of specified IP address</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <node name="adv-router"> + <properties> + <help>Show advertising router link states</help> + </properties> + </node> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF opaque-as database of specified IP address for specified advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <leafNode name="self-originate"> + <properties> + <help>Show self-originate IPv4 OSPF opaque-as database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </tagNode> + <node name="opaque-link"> + <properties> + <help>Show IPv4 OSPF opaque-link database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF opaque-link database for specified IP address of advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <node name="adv-router"> + <properties> + <help>Show IPv4 OSPF opaque-link database for specified IP address of advertised router</help> + </properties> + </node> + </children> + </node> + <tagNode name="opaque-link"> + <properties> + <help>Show IPv4 OSPF opaque-link database information of specified IP address</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <node name="adv-router"> + <properties> + <help>Show advertising router link states</help> + </properties> + </node> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF opaque-link database of specified IP address for specified advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <leafNode name="self-originate"> + <properties> + <help>Show self-originate IPv4 OSPF opaque-link database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </tagNode> + <node name="router"> + <properties> + <help>Show IPv4 OSPF router database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF router database for specified IP address of advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <node name="adv-router"> + <properties> + <help>Show IPv4 OSPF router database for specified IP address of advertised router</help> + </properties> + </node> + </children> + </node> + <tagNode name="router"> + <properties> + <help>Show IPv4 OSPF router database information of specified IP address</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <node name="adv-router"> + <properties> + <help>Show advertising router link states</help> + </properties> + </node> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF router database of specified IP address for specified advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <leafNode name="self-originate"> + <properties> + <help>Show self-originate IPv4 OSPF router database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </tagNode> + <leafNode name="self-originate"> + <properties> + <help>Show IPv4 OSPF self-originate database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <node name="summary"> + <properties> + <help>Show summary of IPv4 OSPF database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF summary database for specified IP address of advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <node name="adv-router"> + <properties> + <help>Show IPv4 OSPF summary database for specified IP address of advertised router</help> + </properties> + </node> + </children> + </node> + <tagNode name="summary"> + <properties> + <help>Show IPv4 OSPF summary database information of specified IP address</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <node name="adv-router"> + <properties> + <help>Show advertising router link states</help> + </properties> + </node> + <tagNode name="adv-router"> + <properties> + <help>Show IPv4 OSPF summary database of specified IP address for specified advertised router</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <leafNode name="self-originate"> + <properties> + <help>Show self-originate IPv4 OSPF summary database</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </tagNode> + </children> +</node> +<node name="interface"> + <properties> + <help>Show IPv4 OSPF interface information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</node> +<tagNode name="interface"> + <properties> + <help>Show IPv4 OSPF information for specified interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</tagNode> +<node name="neighbor"> + <properties> + <help>Show IPv4 OSPF neighbor information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <tagNode name="address"> + <properties> + <help>Show IPv4 OSPF neighbor information for specified IP address</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <node name="detail"> + <properties> + <help>Show detailed IPv4 OSPF neighbor information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </node> + </children> +</node> +<tagNode name="neighbor"> + <properties> + <help>Show IPv4 OSPF neighbor information for specified IP address or interface</help> + <completionHelp> + <list><x.x.x.x></list> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</tagNode> +<leafNode name="route"> + <properties> + <help>Show IPv4 OSPF route information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/ospfv3-adv-router-id-node-tag.xml.i b/op-mode-definitions/include/ospfv3-adv-router-id-node-tag.xml.i new file mode 100644 index 000000000..312ce2a4f --- /dev/null +++ b/op-mode-definitions/include/ospfv3-adv-router-id-node-tag.xml.i @@ -0,0 +1,17 @@ +<!-- included start from ospfv3-adv-router-id-node-tag.xml.i --> +<node name="node.tag"> + <properties> + <help>Search by Advertising Router ID</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <!-- FRR uses ospf6 where we use ospfv3, thus alter the command --> + <command>vtysh -c "show ipv6 ospf6 ${@:4}"</command> + <children> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + </children> +</node> +<!-- included end --> diff --git a/op-mode-definitions/include/ospfv3-adv-router.xml.i b/op-mode-definitions/include/ospfv3-adv-router.xml.i new file mode 100644 index 000000000..d17538d4d --- /dev/null +++ b/op-mode-definitions/include/ospfv3-adv-router.xml.i @@ -0,0 +1,16 @@ +<!-- included start from ospfv3-adv-router.xml.i --> +<tagNode name="adv-router"> + <properties> + <help>Search by Advertising Router ID</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <children> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + #include <include/ospfv3-linkstate-id.xml.i> + </children> +</tagNode> +<!-- included end --> diff --git a/op-mode-definitions/include/ospfv3-detail.xml.i b/op-mode-definitions/include/ospfv3-detail.xml.i new file mode 100644 index 000000000..76096fbc8 --- /dev/null +++ b/op-mode-definitions/include/ospfv3-detail.xml.i @@ -0,0 +1,9 @@ +<!-- included start from ospfv3-detail.xml.i --> +<node name="detail"> + <properties> + <help>Show detailed information</help> + </properties> + <!-- FRR uses ospf6 where we use ospfv3, thus alter the command --> + <command>vtysh -c "show ipv6 ospf6 ${@:4}"</command> +</node> +<!-- included end --> diff --git a/op-mode-definitions/include/ospfv3-dump.xml.i b/op-mode-definitions/include/ospfv3-dump.xml.i new file mode 100644 index 000000000..4271aec53 --- /dev/null +++ b/op-mode-definitions/include/ospfv3-dump.xml.i @@ -0,0 +1,9 @@ +<!-- included start from ospfv3-dump.xml.i --> +<node name="dump"> + <properties> + <help>Show dump of LSAs</help> + </properties> + <!-- FRR uses ospf6 where we use ospfv3, thus alter the command --> + <command>vtysh -c "show ipv6 ospf6 ${@:4}"</command> +</node> +<!-- included end --> diff --git a/op-mode-definitions/include/ospfv3-internal.xml.i b/op-mode-definitions/include/ospfv3-internal.xml.i new file mode 100644 index 000000000..8b45e86c1 --- /dev/null +++ b/op-mode-definitions/include/ospfv3-internal.xml.i @@ -0,0 +1,9 @@ +<!-- included start from ospfv3-internal.xml.i --> +<node name="internal"> + <properties> + <help>Show internal LSA information</help> + </properties> + <!-- FRR uses ospf6 where we use ospfv3, thus alter the command --> + <command>vtysh -c "show ipv6 ospf6 ${@:4}"</command> +</node> +<!-- included end --> diff --git a/op-mode-definitions/include/ospfv3-linkstate-id-node-tag.xml.i b/op-mode-definitions/include/ospfv3-linkstate-id-node-tag.xml.i new file mode 100644 index 000000000..24b549d28 --- /dev/null +++ b/op-mode-definitions/include/ospfv3-linkstate-id-node-tag.xml.i @@ -0,0 +1,18 @@ +<!-- included start from ospfv3-linkstate-id-node-tag.xml.i --> +<node name="node.tag"> + <properties> + <help>Search by Link state ID</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <!-- FRR uses ospf6 where we use ospfv3, thus alter the command --> + <command>vtysh -c "show ipv6 ospf6 ${@:4}"</command> + <children> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + #include <include/ospfv3-self-originated.xml.i> + </children> +</node> +<!-- included end --> diff --git a/op-mode-definitions/include/ospfv3-linkstate-id.xml.i b/op-mode-definitions/include/ospfv3-linkstate-id.xml.i new file mode 100644 index 000000000..eab5916f1 --- /dev/null +++ b/op-mode-definitions/include/ospfv3-linkstate-id.xml.i @@ -0,0 +1,15 @@ +<!-- included start from ospfv3-linkstate-id.xml.i --> +<tagNode name="linkstate-id"> + <properties> + <help>Search by Link state ID</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <children> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + </children> +</tagNode> +<!-- included end --> diff --git a/op-mode-definitions/include/ospfv3-self-originated.xml.i b/op-mode-definitions/include/ospfv3-self-originated.xml.i new file mode 100644 index 000000000..180bca6f6 --- /dev/null +++ b/op-mode-definitions/include/ospfv3-self-originated.xml.i @@ -0,0 +1,14 @@ +<!-- included start from ospfv3-self-originated.xml.i --> +<node name="self-originated"> + <properties> + <help>Show Self-originated LSAs</help> + </properties> + <!-- FRR uses ospf6 where we use ospfv3, thus alter the command --> + <command>vtysh -c "show ipv6 ospf6 ${@:4}"</command> + <children> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + </children> +</node> +<!-- included end --> diff --git a/op-mode-definitions/include/vni-tagnode-all.xml.i b/op-mode-definitions/include/vni-tagnode-all.xml.i new file mode 100644 index 000000000..0fedb9371 --- /dev/null +++ b/op-mode-definitions/include/vni-tagnode-all.xml.i @@ -0,0 +1,11 @@ +<!-- included start from vni-tagnode-all.xml.i --> +<tagNode name="vni"> + <properties> + <help>VXLAN network identifier (VNI) number</help> + <completionHelp> + <list>1-16777215 all</list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</tagNode> +<!-- included end --> diff --git a/op-mode-definitions/include/vni-tagnode.xml.i b/op-mode-definitions/include/vni-tagnode.xml.i new file mode 100644 index 000000000..22f2d33bd --- /dev/null +++ b/op-mode-definitions/include/vni-tagnode.xml.i @@ -0,0 +1,11 @@ +<!-- included start from vni-tagnode.xml.i --> +<tagNode name="vni"> + <properties> + <help>VXLAN network identifier (VNI) number</help> + <completionHelp> + <list>1-16777215</list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</tagNode> +<!-- included end --> diff --git a/op-mode-definitions/include/vtysh-generic-detail.xml.i b/op-mode-definitions/include/vtysh-generic-detail.xml.i new file mode 100644 index 000000000..d469f70c7 --- /dev/null +++ b/op-mode-definitions/include/vtysh-generic-detail.xml.i @@ -0,0 +1,8 @@ +<!-- included start from vtysh-generic-detail.xml.i --> +<leafNode name="detail"> + <properties> + <help>Detailed information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/ipoe-server.xml b/op-mode-definitions/ipoe-server.xml.in index c20d3aa2a..89cefa08d 100644 --- a/op-mode-definitions/ipoe-server.xml +++ b/op-mode-definitions/ipoe-server.xml.in @@ -4,17 +4,17 @@ <children> <node name="ipoe-server"> <properties> - <help>Clear ipoe-server sessions or process</help> + <help>Clear IPoE server sessions or process</help> </properties> <children> <node name="session"> <properties> - <help>Clear ipoe-server session</help> + <help>Clear IPoE server session</help> </properties> <children> <tagNode name="username"> <properties> - <help>Clear ipoe-server session by username</help> + <help>Clear IPoE server session by username</help> <completionHelp> <script>${vyos_completion_dir}/list_ipoe.py --selector="username"</script> </completionHelp> @@ -23,7 +23,7 @@ </tagNode> <tagNode name="sid"> <properties> - <help>Clear ipoe-server session by Session ID</help> + <help>Clear IPoE server session by Session ID</help> <completionHelp> <script>${vyos_completion_dir}/list_ipoe.py --selector="sid"</script> </completionHelp> @@ -32,7 +32,7 @@ </tagNode> <tagNode name="interface"> <properties> - <help>Clear ipoe-server session by interface</help> + <help>Clear IPoE server session by interface</help> <completionHelp> <script>${vyos_completion_dir}/list_ipoe.py --selector="ifname"</script> </completionHelp> @@ -49,7 +49,7 @@ <children> <node name="ipoe-server"> <properties> - <help>show ipoe-server status</help> + <help>Show IPoE server status</help> </properties> <children> <leafNode name="sessions"> @@ -72,7 +72,7 @@ <children> <leafNode name="ipoe-server"> <properties> - <help>show ipoe-server status</help> + <help>Restart IPoE server process</help> </properties> <command>${vyos_op_scripts_dir}/ipoe-control.py --action="restart"</command> </leafNode> diff --git a/op-mode-definitions/ipv4-route.xml b/op-mode-definitions/ipv4-route.xml.in index 1bda3ac11..1bda3ac11 100644 --- a/op-mode-definitions/ipv4-route.xml +++ b/op-mode-definitions/ipv4-route.xml.in diff --git a/op-mode-definitions/ipv6-route.xml b/op-mode-definitions/ipv6-route.xml.in index fbf6489ba..7f188fdb2 100644 --- a/op-mode-definitions/ipv6-route.xml +++ b/op-mode-definitions/ipv6-route.xml.in @@ -21,47 +21,9 @@ <properties> <help>Show IPv6 Neighbor Discovery (ND) information</help> </properties> - <command>ip -f inet6 neigh list</command> + <command>${vyos_op_scripts_dir}/show_neigh.py --family inet6</command> </leafNode> - <node name="route"> - <properties> - <help>Show IPv6 routes</help> - </properties> - <children> - <node name="cache"> - <properties> - <help>Show kernel IPv6 route cache</help> - </properties> - <command>ip -s -f inet6 route list cache</command> - </node> - <tagNode name="cache"> - <properties> - <help>Show kernel IPv6 route cache for a given route</help> - <completionHelp> - <list><h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> - </completionHelp> - </properties> - <command>ip -s -f inet6 route list cache $5</command> - </tagNode> - <node name="forward"> - <properties> - <help>Show kernel IPv6 route table</help> - </properties> - <command>ip -f inet6 route list</command> - </node> - <tagNode name="forward"> - <properties> - <help>Show kernel IPv6 route table for a given route</help> - <completionHelp> - <list><h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> - </completionHelp> - </properties> - <command>ip -s -f inet6 route list $5</command> - </tagNode> - </children> - </node> - </children> </node> </children> diff --git a/op-mode-definitions/l2tp-server.xml b/op-mode-definitions/l2tp-server.xml.in index 3e96b9365..3e96b9365 100644 --- a/op-mode-definitions/l2tp-server.xml +++ b/op-mode-definitions/l2tp-server.xml.in diff --git a/op-mode-definitions/lldp.xml b/op-mode-definitions/lldp.xml.in index 297ccf1f4..297ccf1f4 100644 --- a/op-mode-definitions/lldp.xml +++ b/op-mode-definitions/lldp.xml.in diff --git a/op-mode-definitions/monitor-bandwidth-test.xml b/op-mode-definitions/monitor-bandwidth-test.xml.in index 5b36b1da5..5b36b1da5 100644 --- a/op-mode-definitions/monitor-bandwidth-test.xml +++ b/op-mode-definitions/monitor-bandwidth-test.xml.in diff --git a/op-mode-definitions/monitor-bandwidth.xml b/op-mode-definitions/monitor-bandwidth.xml.in index 9af0a9e70..9af0a9e70 100644 --- a/op-mode-definitions/monitor-bandwidth.xml +++ b/op-mode-definitions/monitor-bandwidth.xml.in diff --git a/op-mode-definitions/monitor-log.xml b/op-mode-definitions/monitor-log.xml.in index 99efe5306..99efe5306 100644 --- a/op-mode-definitions/monitor-log.xml +++ b/op-mode-definitions/monitor-log.xml.in diff --git a/op-mode-definitions/monitor-ndp.xml b/op-mode-definitions/monitor-ndp.xml.in index 1ac6ce39b..1ac6ce39b 100644 --- a/op-mode-definitions/monitor-ndp.xml +++ b/op-mode-definitions/monitor-ndp.xml.in diff --git a/op-mode-definitions/monitor-protocol.xml.in b/op-mode-definitions/monitor-protocol.xml.in new file mode 100644 index 000000000..6a6bd50f3 --- /dev/null +++ b/op-mode-definitions/monitor-protocol.xml.in @@ -0,0 +1,1542 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="monitor"> + <children> + <node name="protocol"> + <properties> + <help>Monitor routing protocols</help> + </properties> + <children> + <node name="bgp"> + <properties> + <help>Monitor the Border Gateway Protocol (BGP)</help> + </properties> + <children> + #include <include/monitor-background.xml.i> + <node name="disable"> + <properties> + <help>Disable Border Gateway Protocol (BGP) debugging</help> + </properties> + <children> + <node name="all"> + <properties> + <help>Disable all BGP debugging</help> + </properties> + <command>vtysh -c "no debug bgp"</command> + </node> + <node name="allow-martians"> + <properties> + <help>Disable BGP martians next hops debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </node> + <node name="as4"> + <properties> + <help>Disable BGP allow AS4 actions debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </node> + <node name="bestpath"> + <properties> + <help>Disable BGP allow best path debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </node> + <tagNode name="bestpath"> + <properties> + <help>Disable BGP bestpath IPv4 IPv6</help> + <completionHelp> + <list><x.x.x.x/x> <h:h:h:h:h:h:h:h/h></list> + </completionHelp> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </tagNode> + <node name="flowspec"> + <properties> + <help>Disable BGP allow flowspec debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </node> + <node name="keepalives"> + <properties> + <help>Disable BGP keepalives debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </node> + <node name="labelpool"> + <properties> + <help>Disable BGP label pool debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </node> + <node name="neighbor-events"> + <properties> + <help>Disable BGP Neighbor events debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </node> + <node name="nht"> + <properties> + <help>Disable BGP next hop tracking debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </node> + <node name="pbr"> + <properties> + <help>Disable BGP policy based routing debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </node> + <node name="rib"> + <properties> + <help>Disable BGP rib debugging</help> + </properties> + <command>vtysh -c "no debug bgp zebra"</command> + </node> + <node name="update-groups"> + <properties> + <help>Disable BGP update groups debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </node> + <node name="updates"> + <properties> + <help>Disable BGP updates debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </node> + <node name="vnc"> + <properties> + <help>Disable BGP VNC debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + <children> + <node name="import-bi-attach"> + <properties> + <help>Disable BGP vnc import BI attachment debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </node> + <node name="import-del-remote"> + <properties> + <help>Disable BGP vnc import/delete remote routes debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </node> + <node name="rfapi-query"> + <properties> + <help>Disable BGP vnc rfapi query debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </node> + <node name="verbose"> + <properties> + <help>Disable BGP vnc verbose logging debugging</help> + </properties> + <command>vtysh -c "no debug bgp ${@:5}"</command> + </node> + </children> + </node> + </children> + </node> + <node name="enable"> + <properties> + <help>Enable Border Gateway Protocol (BGP) debugging</help> + </properties> + <children> + <node name="allow-martians"> + <properties> + <help>Enable BGP martians next hops debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </node> + <node name="as4"> + <properties> + <help>Enable BGP allow AS4 actions debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </node> + <node name="bestpath"> + <properties> + <help>Enable BGP allow best path debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </node> + <tagNode name="bestpath"> + <properties> + <help>Debug bestpath IPv4 IPv6</help> + <completionHelp> + <list><x.x.x.x/x> <h:h:h:h:h:h:h:h/h></list> + </completionHelp> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </tagNode> + <node name="flowspec"> + <properties> + <help>Enable BGP allow flowspec debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </node> + <node name="keepalives"> + <properties> + <help>Enable BGP keepalives debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </node> + <node name="labelpool"> + <properties> + <help>Enable BGP label pool debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </node> + <node name="neighbor-events"> + <properties> + <help>Enable BGP Neighbor events debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </node> + <node name="nht"> + <properties> + <help>Enable BGP next hop tracking debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </node> + <node name="pbr"> + <properties> + <help>Enable BGP policy based routing debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </node> + <node name="rib"> + <properties> + <help>Enable BGP rib debugging</help> + </properties> + <command>vtysh -c "debug bgp zebra"</command> + </node> + <node name="update-groups"> + <properties> + <help>Enable BGP update groups debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </node> + <node name="updates"> + <properties> + <help>Enable BGP updates debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </node> + <node name="vnc"> + <properties> + <help>Enable BGP VNC debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + <children> + <node name="import-bi-attach"> + <properties> + <help>Enable BGP vnc import BI attachment debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </node> + <node name="import-del-remote"> + <properties> + <help>Enable BGP vnc import/delete remote routes debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </node> + <node name="rfapi-query"> + <properties> + <help>Enable BGP vnc rfapi query debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </node> + <node name="verbose"> + <properties> + <help>Enable BGP vnc verbose logging debugging</help> + </properties> + <command>vtysh -c "debug bgp ${@:5}"</command> + </node> + </children> + </node> + </children> + </node> + </children> + </node> + <node name="ospf"> + <properties> + <help>Monitor the Open Shortest Path First (OSPF) protocol</help> + </properties> + <children> + #include <include/monitor-background.xml.i> + + + <node name="disable"> + <children> + <node name="event"> + <properties> + <help>Disable OSPF debugging</help> + </properties> + <command>vtysh -c "no debug ospf"</command> + </node> + <node name="event"> + <properties> + <help>Disable OSPF event debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + </node> + <node name="ism"> + <properties> + <help>Disable OSPF ism debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + <children> + <node name="events"> + <properties> + <help>Disable OSPF ism events debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + </node> + <node name="status"> + <properties> + <help>Disable OSPF ism status debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + </node> + <node name="timers"> + <properties> + <help>Disable OSPF ism timers debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + </node> + </children> + </node> + <node name="lsa"> + <properties> + <help>Disable OSPF lsa debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + <children> + <node name="flooding"> + <properties> + <help>Disable OSPF lsa flooding debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + </node> + <node name="generate"> + <properties> + <help>Disable OSPF lsa generate debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + </node> + <node name="install"> + <properties> + <help>Disable OSPF lsa install debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + </node> + <node name="refresh"> + <properties> + <help>Disable OSPF lsa refresh debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + </node> + </children> + </node> + <node name="nsm"> + <properties> + <help>Disable OSPF nsm debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + <children> + <node name="events"> + <properties> + <help>Disable OSPF nsm events debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + </node> + <node name="status"> + <properties> + <help>Disable OSPF nsm status debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + </node> + <node name="timers"> + <properties> + <help>Disable OSPF nsm timers debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + </node> + </children> + </node> + <node name="nssa"> + <properties> + <help>Disable OSPF nssa debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + </node> + <node name="packet"> + <properties> + <help>Disable OSPF packet debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + <children> + <node name="all"> + <properties> + <help>Disable OSPF all packet debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + <children> + #include <include/monitor-no-ospf-packet-detail.xml.i> + </children> + </node> + <node name="dd"> + <properties> + <help>Disable OSPF dd packet debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + <children> + #include <include/monitor-no-ospf-packet-detail.xml.i> + </children> + </node> + <node name="hello"> + <properties> + <help>Disable OSPF hello packet debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + <children> + #include <include/monitor-no-ospf-packet-detail.xml.i> + </children> + </node> + <node name="ls-ack"> + <properties> + <help>Disable OSPF ls-ack packet debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + <children> + #include <include/monitor-no-ospf-packet-detail.xml.i> + </children> + </node> + <node name="ls-request"> + <properties> + <help>Disable OSPF ls-request packet debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + <children> + #include <include/monitor-no-ospf-packet-detail.xml.i> + </children> + </node> + <node name="ls-update"> + <properties> + <help>Disable OSPF ls-update packet debugging</help> + </properties> + <command>vtysh -c "no debug ospf ${@:5}"</command> + <children> + #include <include/monitor-no-ospf-packet-detail.xml.i> + </children> + </node> + </children> + </node> + <node name="rib"> + <properties> + <help>Disable OSPF rib debugging</help> + </properties> + <command>vtysh -c "no debug ospf zebra"</command> + <children> + <node name="interface"> + <properties> + <help>Disable OSPF rib interface debugging</help> + </properties> + <command>vtysh -c "no debug ospf zebra interface"</command> + </node> + <node name="redistribute"> + <properties> + <help>Disable OSPF rib redistribute debugging</help> + </properties> + <command>vtysh -c "no debug ospf zebra redistribute"</command> + </node> + </children> + </node> + </children> + </node> + <node name="enable"> + <children> + <node name="event"> + <properties> + <help>Enable OSPF event debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + </node> + <node name="ism"> + <properties> + <help>Enable OSPF ism debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + <children> + <node name="events"> + <properties> + <help>Enable OSPF ism events debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + </node> + <node name="status"> + <properties> + <help>Enable OSPF ism status debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + </node> + <node name="timers"> + <properties> + <help>Enable OSPF ism timers debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + </node> + </children> + </node> + <node name="lsa"> + <properties> + <help>Enable OSPF lsa debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + <children> + <node name="flooding"> + <properties> + <help>Enable OSPF lsa flooding debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + </node> + <node name="generate"> + <properties> + <help>Enable OSPF lsa generate debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + </node> + <node name="install"> + <properties> + <help>Enable OSPF lsa install debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + </node> + <node name="refresh"> + <properties> + <help>Enable OSPF lsa refresh debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + </node> + </children> + </node> + <node name="nsm"> + <properties> + <help>Enable OSPF nsm debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + <children> + <node name="events"> + <properties> + <help>Enable OSPF nsm events debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + </node> + <node name="status"> + <properties> + <help>Enable OSPF nsm status debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + </node> + <node name="timers"> + <properties> + <help>Enable OSPF nsm timers debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + </node> + </children> + </node> + <node name="nssa"> + <properties> + <help>Enable OSPF nssa debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + </node> + <node name="packet"> + <properties> + <help>Enable OSPF packet debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + <children> + <node name="all"> + <properties> + <help>Enable OSPF all packet debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + <children> + #include <include/monitor-ospf-packet-detail.xml.i> + </children> + </node> + <node name="dd"> + <properties> + <help>Enable OSPF dd packet debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + <children> + #include <include/monitor-ospf-packet-detail.xml.i> + </children> + </node> + <node name="hello"> + <properties> + <help>Enable OSPF hello packet debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + <children> + #include <include/monitor-ospf-packet-detail.xml.i> + </children> + </node> + <node name="ls-ack"> + <properties> + <help>Enable OSPF ls-ack packet debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + <children> + #include <include/monitor-ospf-packet-detail.xml.i> + </children> + </node> + <node name="ls-request"> + <properties> + <help>Enable OSPF ls-request packet debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + <children> + #include <include/monitor-ospf-packet-detail.xml.i> + </children> + </node> + <node name="ls-update"> + <properties> + <help>Enable OSPF ls-update packet debugging</help> + </properties> + <command>vtysh -c "debug ospf ${@:5}"</command> + <children> + #include <include/monitor-ospf-packet-detail.xml.i> + </children> + </node> + </children> + </node> + <node name="rib"> + <properties> + <help>Enable OSPF rib debugging</help> + </properties> + <command>vtysh -c "debug ospf zebra"</command> + <children> + <node name="interface"> + <properties> + <help>Enable OSPF rib interface debugging</help> + </properties> + <command>vtysh -c "debug ospf zebra interface"</command> + </node> + <node name="redistribute"> + <properties> + <help>Enable OSPF rib redistribute debugging</help> + </properties> + <command>vtysh -c "debug ospf zebra redistribute"</command> + </node> + </children> + </node> + </children> + </node> + </children> + </node> + <node name="ospfv3"> + <properties> + <help>Monitor the IPv6 Open Shortest Path First (OSPFv3) protocol</help> + </properties> + <children> + <node name="background"> + <properties> + <help>Monitor in background</help> + </properties> + <children> + <node name="start"> + <properties> + <help>Start background monitoring</help> + </properties> + <command>${vyatta_bindir}/vyatta-monitor-background OSPFv3 ospf6</command> + </node> + <node name="stop"> + <properties> + <help>Stop background monitoring</help> + </properties> + <command>${vyatta_bindir}/vyatta-monitor-background-stop OSPFv3</command> + </node> + </children> + </node> + <node name="disable"> + <properties> + <help>Disable IPv6 Open Shortest Path First (OSPFv3) protocol debugging</help> + </properties> + <children> + <node name="abr"> + <properties> + <help>Disable all OSPFv3 debugging</help> + </properties> + <command>vtysh -c "no debug ospf6"</command> + </node> + <node name="abr"> + <properties> + <help>Disable OSPFv3 ABR debugging</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="asbr"> + <properties> + <help>Disable OSPFv3 ASBR debugging</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="border-routers"> + <properties> + <help>Disable OSPFv3 border router debugging</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + <children> + <node name="area-id"> + <properties> + <help>Disable debug border routers in specific Area</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="router-id"> + <properties> + <help>Disable debug specific border router</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + </children> + </node> + <node name="flooding"> + <properties> + <help>Disable OSPFv3 flooding debugging</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="interface"> + <properties> + <help>Disable OSPFv3 Interface debugging</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="lsa"> + <properties> + <help>Disable OSPFv3 Link State Advertisments debugging</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + <children> + <node name="as-external"> + <properties> + <help>Display As-External LSAs</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="inter-prefix"> + <properties> + <help>Display Inter-Area-Prefix LSAs</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="inter-router"> + <properties> + <help>Display Inter-Router LSAs</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="intra-prefix"> + <properties> + <help>Display Intra-Area-Prefix LSAs</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="link"> + <properties> + <help>Display Link LSAs</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="network"> + <properties> + <help>Display Network LSAs</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="router"> + <properties> + <help>Display Router LSAs</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="unknown"> + <properties> + <help>Display LSAs of unknown origin</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + </children> + </node> + <node name="message"> + <properties> + <help>Disable OSPFv3 message debugging</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + <children> + <node name="all"> + <properties> + <help>Debug All message</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="dbdesc"> + <properties> + <help>Debug Database Description message</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="hello"> + <properties> + <help>Debug Hello message</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="lsack"> + <properties> + <help>Debug Link State Acknowledgement message</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="lsreq"> + <properties> + <help>Debug Link State Request message</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="lsupdate"> + <properties> + <help>Debug Link State Update message</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="unknown"> + <properties> + <help>Debug Unknown message</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + </children> + </node> + <node name="neighbor"> + <properties> + <help>Disable OSPFv3 Neighbor debugging</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + <children> + <node name="event"> + <properties> + <help>Debug OSPFv3 Neighbor Event</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="state"> + <properties> + <help>Debug OSPFv3 Neighbor State Change</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + </children> + </node> + <node name="rib"> + <properties> + <help>Disable OSPFv3 connection to RIB debugging</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + <children> + <node name="recv"> + <properties> + <help>Debug receiving zebra</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="send"> + <properties> + <help>Debug sending zebra</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + </children> + </node> + <node name="route"> + <properties> + <help>Disable OSPFv3 route table calculation debugging</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + <children> + <node name="inter-area"> + <properties> + <help>Debug inter-area route calculation</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="intra-area"> + <properties> + <help>Debug intra-area route calculation</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="memory"> + <properties> + <help>Debug route memory use</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="table"> + <properties> + <help>Debug route table calculation</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + </children> + </node> + <node name="spf"> + <properties> + <help>Disable OSPFv3 SPF calculation debugging</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + <children> + <node name="database"> + <properties> + <help>Log number of LSAs at SPF Calculation time</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="process"> + <properties> + <help>Debug Detailed SPF Process</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + <node name="time"> + <properties> + <help>Measure time taken by SPF Calculation</help> + </properties> + <command>vtysh -c "no debug ospf6 ${@:5}"</command> + </node> + </children> + </node> + </children> + </node> + <node name="enable"> + <properties> + <help>Enable IPv6 Open Shortest Path First (OSPFv3) protocol debugging</help> + </properties> + <children> + <node name="abr"> + <properties> + <help>Enable OSPFv3 ABR debugging</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="asbr"> + <properties> + <help>Enable OSPFv3 ASBR debugging</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="border-routers"> + <properties> + <help>Enable OSPFv3 border router debugging</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + <children> + <node name="area-id"> + <properties> + <help>Debug border routers in specific Area</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="router-id"> + <properties> + <help>Debug specific border router</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + </children> + </node> + <node name="flooding"> + <properties> + <help>Enable OSPFv3 flooding debugging</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="interface"> + <properties> + <help>Enable OSPFv3 Interface debugging</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="lsa"> + <properties> + <help>Enable OSPFv3 Link State Advertisments debugging</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + <children> + <node name="as-external"> + <properties> + <help>Display As-External LSAs</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="inter-prefix"> + <properties> + <help>Display Inter-Area-Prefix LSAs</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="inter-router"> + <properties> + <help>Display Inter-Router LSAs</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="intra-prefix"> + <properties> + <help>Display Intra-Area-Prefix LSAs</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="link"> + <properties> + <help>Display Link LSAs</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="network"> + <properties> + <help>Display Network LSAs</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="router"> + <properties> + <help>Display Router LSAs</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="unknown"> + <properties> + <help>Display LSAs of unknown origin</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + </children> + </node> + <node name="message"> + <properties> + <help>Enable OSPFv3 message debugging</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + <children> + <node name="all"> + <properties> + <help>Debug All message</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="dbdesc"> + <properties> + <help>Debug Database Description message</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="hello"> + <properties> + <help>Debug Hello message</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="lsack"> + <properties> + <help>Debug Link State Acknowledgement message</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="lsreq"> + <properties> + <help>Debug Link State Request message</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="lsupdate"> + <properties> + <help>Debug Link State Update message</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="unknown"> + <properties> + <help>Debug Unknown message</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + </children> + </node> + <node name="neighbor"> + <properties> + <help>Enable OSPFv3 Neighbor debugging</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + <children> + <node name="event"> + <properties> + <help>Debug OSPFv3 Neighbor Event</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="state"> + <properties> + <help>Debug OSPFv3 Neighbor State Change</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + </children> + </node> + <node name="rib"> + <properties> + <help>Enable OSPFv3 connection to RIB debugging</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + <children> + <node name="recv"> + <properties> + <help>Debug receiving zebra</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="send"> + <properties> + <help>Debug sending zebra</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + </children> + </node> + <node name="route"> + <properties> + <help>Enable OSPFv3 route table calculation debugging</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + <children> + <node name="inter-area"> + <properties> + <help>Debug inter-area route calculation</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="intra-area"> + <properties> + <help>Debug intra-area route calculation</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="memory"> + <properties> + <help>Debug route memory use</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="table"> + <properties> + <help>Debug route table calculation</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + </children> + </node> + <node name="spf"> + <properties> + <help>Enable OSPFv3 SPF calculation debugging</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + <children> + <node name="database"> + <properties> + <help>Log number of LSAs at SPF Calculation time</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="process"> + <properties> + <help>Debug Detailed SPF Process</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + <node name="time"> + <properties> + <help>Measure time taken by SPF Calculation</help> + </properties> + <command>vtysh -c "debug ospf6 ${@:5}"</command> + </node> + </children> + </node> + </children> + </node> + </children> + </node> + <node name="rib"> + <properties> + <help>Monitor the Routing Information Base (RIB)</help> + </properties> + <children> + <node name="background"> + <properties> + <help>Monitor in background</help> + </properties> + <children> + <node name="start"> + <properties> + <help>Start background monitoring</help> + </properties> + <command>${vyatta_bindir}/vyatta-monitor-background RIB zebra</command> + </node> + <node name="stop"> + <properties> + <help>Stop background monitoring</help> + </properties> + <command>${vyatta_bindir}/vyatta-monitor-background-stop RIB</command> + </node> + </children> + </node> + <node name="disable"> + <properties> + <help>Disable Route Information Base (RIB) debugging</help> + </properties> + <children> + <node name="events"> + <properties> + <help>Disable RIB events debugging</help> + </properties> + <command>vtysh -c "no debug zebra ${@:5}"</command> + </node> + <node name="kernel"> + <properties> + <help>Disable RIB kernel debugging</help> + </properties> + <command>vtysh -c "no debug zebra ${@:5}"</command> + </node> + <node name="packet"> + <properties> + <help>Disable RIB packet debugging</help> + </properties> + <command>vtysh -c "no debug zebra ${@:5}"</command> + <children> + <node name="detail"> + <properties> + <help>Disable detailed debugging</help> + </properties> + <command>vtysh -c "no debug zebra ${@:5}"</command> + </node> + <node name="recv"> + <properties> + <help>Disable receive packet debugging</help> + </properties> + <command>vtysh -c "no debug zebra ${@:5}"</command> + </node> + <node name="send"> + <properties> + <help>Disable send packet debugging</help> + </properties> + <command>vtysh -c "no debug zebra ${@:5}"</command> + </node> + </children> + </node> + <node name="nexthop"> + <properties> + <help>Disable RIB nexthop debugging</help> + </properties> + <command>vtysh -c "no debug zebra ${@:5}"</command> + </node> + <node name="mpls"> + <properties> + <help>Disable RIP MPLS LSP debugging</help> + </properties> + <command>vtysh -c "no debug zebra ${@:5}"</command> + </node> + <node name="rib"> + <properties> + <help>Disable RIB debugging</help> + </properties> + <command>vtysh -c "no debug zebra ${@:5}"</command> + <children> + <node name="detailed"> + <properties> + <help>Disable detailed debugging</help> + </properties> + <command>vtysh -c "no debug zebra ${@:5}"</command> + </node> + </children> + </node> + </children> + </node> + <node name="enable"> + <properties> + <help>Enable Route Information Base (RIB) debugging</help> + </properties> + <children> + <node name="events"> + <properties> + <help>Enable RIB events debugging</help> + </properties> + <command>vtysh -c "debug zebra ${@:5}"</command> + </node> + <node name="kernel"> + <properties> + <help>Enable RIB kernel debugging</help> + </properties> + <command>vtysh -c "debug zebra ${@:5}"</command> + </node> + <node name="packet"> + <properties> + <help>Enable RIB packet debugging</help> + </properties> + <command>vtysh -c "debug zebra ${@:5}"</command> + <children> + <node name="detail"> + <properties> + <help>Enable detailed debugging</help> + </properties> + <command>vtysh -c "debug zebra ${@:5}"</command> + </node> + <node name="recv"> + <properties> + <help>Enable receive packet debugging</help> + </properties> + <command>vtysh -c "debug zebra ${@:5}"</command> + </node> + <node name="send"> + <properties> + <help>Enable send packet debugging</help> + </properties> + <command>vtysh -c "debug zebra ${@:5}"</command> + </node> + </children> + </node> + <node name="nexthop"> + <properties> + <help>Enable RIB nexthop debugging</help> + </properties> + <command>vtysh -c "debug zebra ${@:5}"</command> + </node> + <node name="mpls"> + <properties> + <help>Enable RIP MPLS LSP debugging</help> + </properties> + <command>vtysh -c "debug zebra ${@:5}"</command> + </node> + <node name="rib"> + <properties> + <help>Enable RIB debugging</help> + </properties> + <command>vtysh -c "debug zebra ${@:5}"</command> + <children> + <node name="detailed"> + <properties> + <help>Enable detailed debugging</help> + </properties> + <command>vtysh -c "debug zebra ${@:5}"</command> + </node> + </children> + </node> + </children> + </node> + </children> + </node> + <node name="rip"> + <properties> + <help>Monitor the Routing Information Protocol (RIP)</help> + </properties> + <children> + #include <include/monitor-background.xml.i> + <node name="disable"> + <properties> + <help>Disable Routing Information Protocol (RIP) debugging</help> + </properties> + <children> + <node name="all"> + <properties> + <help>Disable RIP debugging</help> + </properties> + <command>vtysh -c "no debug rip"</command> + </node> + <node name="events"> + <properties> + <help>Disable RIP events debugging</help> + </properties> + <command>vtysh -c "no debug rip ${@:5}"</command> + </node> + <node name="packet"> + <properties> + <help>Disable RIP packet debugging</help> + </properties> + <command>vtysh -c "no debug rip ${@:5}"</command> + <children> + <node name="recv"> + <properties> + <help>Disable receive packet debugging</help> + </properties> + <command>vtysh -c "no debug rip ${@:5}"</command> + </node> + <node name="send"> + <properties> + <help>Disable send packet debugging</help> + </properties> + <command>vtysh -c "no debug rip ${@:5}"</command> + </node> + </children> + </node> + <node name="rib"> + <properties> + <help>Disable RIB debugging</help> + </properties> + <command>vtysh -c "no debug rip zebra"</command> + </node> + </children> + </node> + <node name="enable"> + <properties> + <help>Enable Routing Information Protocol (RIP) debugging</help> + </properties> + <children> + <node name="events"> + <properties> + <help>Enable RIP events debugging</help> + </properties> + <command>vtysh -c "debug rip ${@:5}"</command> + </node> + <node name="packet"> + <properties> + <help>Enable RIP packet debugging</help> + </properties> + <command>vtysh -c "debug rip ${@:5}"</command> + <children> + <node name="recv"> + <properties> + <help>Enable receive packet debugging</help> + </properties> + <command>vtysh -c "debug rip ${@:5}"</command> + </node> + <node name="send"> + <properties> + <help>Enable send packet debugging</help> + </properties> + <command>vtysh -c "debug rip ${@:5}"</command> + </node> + </children> + </node> + <node name="rib"> + <properties> + <help>Enable RIB debugging</help> + </properties> + <command>vtysh -c "debug rip zebra"</command> + </node> + </children> + </node> + </children> + </node> + <node name="ripng"> + <properties> + <help>Monitor the Routing Information Protocol Next Generation (RIPng) protocol</help> + </properties> + <children> + #include <include/monitor-background.xml.i> + <node name="disable"> + <properties> + <help>Disable Routing Information Protocol Next Generation (RIPNG) debugging</help> + </properties> + <children> + <node name="all"> + <properties> + <help>Disable RIPNG debugging</help> + </properties> + <command>vtysh -c "no debug ripng"</command> + </node> + <node name="events"> + <properties> + <help>Disable RIPNG events debugging</help> + </properties> + <command>vtysh -c "no debug ripng ${@:5}"</command> + </node> + <node name="packet"> + <properties> + <help>Disable RIPNG packet debugging</help> + </properties> + <command>vtysh -c "no debug ripng ${@:5}"</command> + <children> + <node name="recv"> + <properties> + <help>Disable receive packet debugging</help> + </properties> + <command>vtysh -c "no debug ripng ${@:5}"</command> + </node> + <node name="send"> + <properties> + <help>Disable send packet debugging</help> + </properties> + <command>vtysh -c "no debug ripng ${@:5}"</command> + </node> + </children> + </node> + <node name="rib"> + <properties> + <help>Disable RIB debugging</help> + </properties> + <command>vtysh -c "no debug ripng zebra"</command> + </node> + </children> + </node> + <node name="enable"> + <properties> + <help>Enable Routing Information Protocol Next Generation (RIPNG) debugging</help> + </properties> + <children> + <node name="events"> + <properties> + <help>Enable RIPNG events debugging</help> + </properties> + <command>vtysh -c "debug ripng ${@:5}"</command> + </node> + <node name="packet"> + <properties> + <help>Enable RIPNG packet debugging</help> + </properties> + <command>vtysh -c "debug ripng ${@:5}"</command> + <children> + <node name="recv"> + <properties> + <help>Enable receive packet debugging</help> + </properties> + <command>vtysh -c "debug ripng ${@:5}"</command> + </node> + <node name="send"> + <properties> + <help>Enable send packet debugging</help> + </properties> + <command>vtysh -c "debug ripng ${@:5}"</command> + </node> + </children> + </node> + <node name="rib"> + <properties> + <help>Enable RIB debugging</help> + </properties> + <command>vtysh -c "debug ripng zebra"</command> + </node> + </children> + </node> + </children> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/nat.xml b/op-mode-definitions/nat.xml.in index f6c0fa748..084e2e7e3 100644 --- a/op-mode-definitions/nat.xml +++ b/op-mode-definitions/nat.xml.in @@ -4,19 +4,19 @@ <children> <node name="nat"> <properties> - <help>Show Network Address Translation (NAT) information</help> + <help>Show IPv4 to IPv4 Network Address Translation (NAT) information</help> </properties> <children> <node name="source"> <properties> - <help>Show source Network Address Translation (NAT) information</help> + <help>Show source IPv4 to IPv4 Network Address Translation (NAT) information</help> </properties> <children> <node name="rules"> <properties> <help>Show configured source NAT rules</help> </properties> - <command>echo To be migrated to Python - https://phabricator.vyos.net/T2459</command> + <command>${vyos_op_scripts_dir}/show_nat_rules.py --source</command> </node> <node name="statistics"> <properties> @@ -51,14 +51,14 @@ </node> <node name="destination"> <properties> - <help>Show destination Network Address Translation (NAT) information</help> + <help>Show destination IPv4 to IPv4 Network Address Translation (NAT) information</help> </properties> <children> <node name="rules"> <properties> <help>Show configured destination NAT rules</help> </properties> - <command>echo To be migrated to Python - https://phabricator.vyos.net/T2459</command> + <command>${vyos_op_scripts_dir}/show_nat_rules.py --destination</command> </node> <node name="statistics"> <properties> diff --git a/op-mode-definitions/nat66.xml.in b/op-mode-definitions/nat66.xml.in new file mode 100644 index 000000000..1ec46eb11 --- /dev/null +++ b/op-mode-definitions/nat66.xml.in @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interfaceDefinition> + <node name="show"> + <children> + <node name="nat66"> + <properties> + <help>Show IPv6 to IPv6 Network Address Translation (NAT66) information</help> + </properties> + <children> + <node name="source"> + <properties> + <help>Show source IPv6 to IPv6 Network Address Translation (NAT66) information</help> + </properties> + <children> + <node name="rules"> + <properties> + <help>Show configured source NAT66 rules</help> + </properties> + <command>${vyos_op_scripts_dir}/show_nat66_rules.py --source</command> + </node> + <node name="statistics"> + <properties> + <help>Show statistics for configured source NAT66 rules</help> + </properties> + <command>${vyos_op_scripts_dir}/show_nat66_statistics.py --source</command> + </node> + <node name="translations"> + <properties> + <help>Show active source NAT66 translations</help> + </properties> + <children> + <tagNode name="address"> + <properties> + <help>Show active source NAT66 translations for an IPv6 address</help> + <completionHelp> + <list><h:h:h:h:h:h:h:h></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/show_nat66_translations.py --type=source --verbose --ipaddr="$6"</command> + </tagNode> + <node name="detail"> + <properties> + <help>Show active source NAT66 translations detail</help> + </properties> + <command>${vyos_op_scripts_dir}/show_nat66_translations.py --type=source --verbose</command> + </node> + </children> + <command>${vyos_op_scripts_dir}/show_nat66_translations.py --type=source</command> + </node> + </children> + </node> + <node name="destination"> + <properties> + <help>Show destination IPv6 to IPv6 Network Address Translation (NAT66) information</help> + </properties> + <children> + <node name="rules"> + <properties> + <help>Show configured destination NAT66 rules</help> + </properties> + <command>${vyos_op_scripts_dir}/show_nat66_rules.py --destination</command> + </node> + <node name="statistics"> + <properties> + <help>Show statistics for configured destination NAT66 rules</help> + </properties> + <command>${vyos_op_scripts_dir}/show_nat66_statistics.py --destination</command> + </node> + <node name="translations"> + <properties> + <help>Show active destination NAT66 translations</help> + </properties> + <children> + <tagNode name="address"> + <properties> + <help>Show active NAT66 destination translations for an IPv6 address</help> + <completionHelp> + <list><h:h:h:h:h:h:h:h></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/show_nat66_translations.py --type=destination --verbose --ipaddr="$6"</command> + </tagNode> + <node name="detail"> + <properties> + <help>Show active destination NAT66 translations detail</help> + </properties> + <command>${vyos_op_scripts_dir}/show_nat66_translations.py --type=destination --verbose</command> + </node> + </children> + <command>${vyos_op_scripts_dir}/show_nat66_translations.py --type=destination</command> + </node> + </children> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/openconnect.xml b/op-mode-definitions/openconnect.xml.in index 9b82b114e..36f23239e 100644 --- a/op-mode-definitions/openconnect.xml +++ b/op-mode-definitions/openconnect.xml.in @@ -4,12 +4,12 @@ <children> <node name="openconnect-server"> <properties> - <help>show openconnect-server information</help> + <help>Show OpenConnect server information</help> </properties> <children> <leafNode name="sessions"> <properties> - <help>Show active openconnect server sessions</help> + <help>Show active OpenConnect server sessions</help> </properties> <command>${vyos_op_scripts_dir}/openconnect-control.py --action="show_sessions"</command> </leafNode> diff --git a/op-mode-definitions/openvpn.xml b/op-mode-definitions/openvpn.xml.in index b9cb06dca..e9420904a 100644 --- a/op-mode-definitions/openvpn.xml +++ b/op-mode-definitions/openvpn.xml.in @@ -19,7 +19,6 @@ key_path=$4 full_path= - # Prepend /config/auth if the path is not absolute if echo $key_path | egrep -ve '^/.*' > /dev/null; then full_path=/config/auth/$key_path else diff --git a/op-mode-definitions/ping.xml b/op-mode-definitions/ping.xml.in index 4c25a59ab..4c25a59ab 100644 --- a/op-mode-definitions/ping.xml +++ b/op-mode-definitions/ping.xml.in diff --git a/op-mode-definitions/poweroff.xml b/op-mode-definitions/poweroff.xml.in index b4163bcb9..b4163bcb9 100644 --- a/op-mode-definitions/poweroff.xml +++ b/op-mode-definitions/poweroff.xml.in diff --git a/op-mode-definitions/pppoe-server.xml b/op-mode-definitions/pppoe-server.xml.in index 5ac9d9497..6efdc5a48 100644 --- a/op-mode-definitions/pppoe-server.xml +++ b/op-mode-definitions/pppoe-server.xml.in @@ -4,7 +4,7 @@ <children> <node name="pppoe-server"> <properties> - <help>Show pppoe-server status</help> + <help>Show PPPoE server status</help> </properties> <children> <leafNode name="sessions"> @@ -21,7 +21,7 @@ </leafNode> <leafNode name="interfaces"> <properties> - <help>Show interfaces where pppoe-server listens on</help> + <help>Show interfaces where PPPoE server listens on</help> </properties> <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pppoe" --action="pppoe interface show"</command> </leafNode> @@ -33,7 +33,7 @@ <children> <leafNode name="pppoe-server"> <properties> - <help>Restarts pppoe-server</help> + <help>Restart PPPoE server process</help> </properties> <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pppoe" --action="restart"</command> </leafNode> @@ -51,13 +51,13 @@ <children> <leafNode name="all"> <properties> - <help>Terminate all pppoe-server users</help> + <help>Terminate all PPPoE server users</help> </properties> <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pppoe" --action="terminate all"</command> </leafNode> <tagNode name="interface"> <properties> - <help>Terminate a ppp interface</help> + <help>Terminate a PPP interface</help> </properties> <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pppoe" --action="terminate if $4"</command> </tagNode> @@ -85,7 +85,7 @@ <children> <leafNode name="enable"> <properties> - <help>Deny new connections and stop to serve pppoe after disconnect last session</help> + <help>Deny new connections and stop serving PPPoE after disconnecting the last session</help> </properties> <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pppoe" --action="shutdown soft"</command> </leafNode> diff --git a/op-mode-definitions/pptp-server.xml b/op-mode-definitions/pptp-server.xml.in index 59be68611..59be68611 100644 --- a/op-mode-definitions/pptp-server.xml +++ b/op-mode-definitions/pptp-server.xml.in diff --git a/op-mode-definitions/reboot.xml b/op-mode-definitions/reboot.xml.in index 2c8daec5d..2c8daec5d 100644 --- a/op-mode-definitions/reboot.xml +++ b/op-mode-definitions/reboot.xml.in diff --git a/op-mode-definitions/reset-conntrack.xml b/op-mode-definitions/reset-conntrack.xml.in index 827ba4af4..827ba4af4 100644 --- a/op-mode-definitions/reset-conntrack.xml +++ b/op-mode-definitions/reset-conntrack.xml.in diff --git a/op-mode-definitions/reset-ip-bgp.xml b/op-mode-definitions/reset-ip-bgp.xml.in index 931a2a9bc..931a2a9bc 100644 --- a/op-mode-definitions/reset-ip-bgp.xml +++ b/op-mode-definitions/reset-ip-bgp.xml.in diff --git a/op-mode-definitions/reset-ip-igmp.xml b/op-mode-definitions/reset-ip-igmp.xml.in index 143553d33..e79c33da8 100644 --- a/op-mode-definitions/reset-ip-igmp.xml +++ b/op-mode-definitions/reset-ip-igmp.xml.in @@ -13,7 +13,7 @@ <properties> <help>Reset IGMP interfaces</help> </properties> - <command>/usr/bin/vtysh -c "clear ip igmp interfaces"</command> + <command>vtysh -c "clear ip igmp interfaces"</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/reset-ip-multicast.xml b/op-mode-definitions/reset-ip-multicast.xml.in index d610add16..6cca07850 100644 --- a/op-mode-definitions/reset-ip-multicast.xml +++ b/op-mode-definitions/reset-ip-multicast.xml.in @@ -13,7 +13,7 @@ <properties> <help>Clear multicast routing table</help> </properties> - <command>/usr/bin/vtysh -c "clear ip mroute"</command> + <command>vtysh -c "clear ip mroute"</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/reset-ipv6-bgp.xml b/op-mode-definitions/reset-ipv6-bgp.xml.in index 3c4275331..3c4275331 100644 --- a/op-mode-definitions/reset-ipv6-bgp.xml +++ b/op-mode-definitions/reset-ipv6-bgp.xml.in diff --git a/op-mode-definitions/reset-mpls.xml b/op-mode-definitions/reset-mpls.xml.in index 4e5d37d5b..829ed41ab 100644 --- a/op-mode-definitions/reset-mpls.xml +++ b/op-mode-definitions/reset-mpls.xml.in @@ -17,10 +17,10 @@ <help>Reset MPLS LDP neighbor/session</help> <completionHelp> <list><x.x.x.x> <h:h:h:h:h:h:h:h></list> - <script>/usr/bin/vtysh -c "show mpls ldp neighbor" | awk '{print $2}' | egrep -v "ID"</script> + <script>vtysh -c "show mpls ldp neighbor" | awk '{print $2}' | egrep -v "ID"</script> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "clear mpls ldp neighbor $5"</command> + <command>vtysh -c "clear mpls ldp neighbor $5"</command> </tagNode> </children> </node> diff --git a/op-mode-definitions/reset-vpn.xml b/op-mode-definitions/reset-vpn.xml.in index ae553c272..71dbb4ed9 100644 --- a/op-mode-definitions/reset-vpn.xml +++ b/op-mode-definitions/reset-vpn.xml.in @@ -17,31 +17,31 @@ <children> <node name="all"> <properties> - <help>Terminate all user's current remote access VPN session(s)</help> + <help>Terminate all users current remote access VPN session(s)</help> </properties> <children> <node name="protocol"> <properties> - <help>Terminate specified user's current remote access VPN session(s) with specified protocol</help> + <help>Terminate specified users current remote access VPN session(s) with specified protocol</help> </properties> <children> <leafNode name="l2tp"> <properties> - <help>Terminate all user's current remote access VPN session(s) with L2TP protocol</help> + <help>Terminate all users current remote access VPN session(s) with L2TP protocol</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="all_users" --protocol="l2tp"</command> + <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="all_users" --protocol="l2tp"</command> </leafNode> <leafNode name="pptp"> <properties> - <help>Terminate all user's current remote access VPN session(s) with PPTP protocol</help> + <help>Terminate all users current remote access VPN session(s) with PPTP protocol</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="all_users" --protocol="pptp"</command> + <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="all_users" --protocol="pptp"</command> </leafNode> <leafNode name="sstp"> <properties> - <help>Terminate all user's current remote access VPN session(s) with SSTP protocol</help> + <help>Terminate all users current remote access VPN session(s) with SSTP protocol</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="all_users" --protocol="sstp"</command> + <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="all_users" --protocol="sstp"</command> </leafNode> </children> </node> @@ -56,29 +56,29 @@ </tagNode> <tagNode name="user"> <properties> - <help>Terminate specified user's current remote access VPN session(s)</help> + <help>Terminate specified users current remote access VPN session(s)</help> </properties> <children> <node name="protocol"> <properties> - <help>Terminate specified user's current remote access VPN session(s) with specified protocol</help> + <help>Terminate specified users current remote access VPN session(s) with specified protocol</help> </properties> <children> <leafNode name="l2tp"> <properties> - <help>Terminate all user's current remote access VPN session(s) with L2TP protocol</help> + <help>Terminate all users current remote access VPN session(s) with L2TP protocol</help> </properties> <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="$5" --protocol="l2tp"</command> </leafNode> <leafNode name="pptp"> <properties> - <help>Terminate all user's current remote access VPN session(s) with PPTP protocol</help> + <help>Terminate all users current remote access VPN session(s) with PPTP protocol</help> </properties> <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="$5" --protocol="pptp"</command> </leafNode> <leafNode name="sstp"> <properties> - <help>Terminate all user's current remote access VPN session(s) with SSTP protocol</help> + <help>Terminate all users current remote access VPN session(s) with SSTP protocol</help> </properties> <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="$5" --protocol="sstp"</command> </leafNode> diff --git a/op-mode-definitions/restart-frr.xml b/op-mode-definitions/restart-frr.xml.in index 96ad1a650..96ad1a650 100644 --- a/op-mode-definitions/restart-frr.xml +++ b/op-mode-definitions/restart-frr.xml.in diff --git a/op-mode-definitions/restart-snmp.xml.in b/op-mode-definitions/restart-snmp.xml.in new file mode 100644 index 000000000..7de27df64 --- /dev/null +++ b/op-mode-definitions/restart-snmp.xml.in @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="restart"> + <children> + <node name="snmp"> + <properties> + <help>Restart SNMP service</help> + </properties> + <command>if cli-shell-api existsActive service snmp; then sudo systemctl restart snmpd.service; else echo "Service SNMP not configured"; fi</command> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/restart-ssh.xml.in b/op-mode-definitions/restart-ssh.xml.in new file mode 100644 index 000000000..6504cc18a --- /dev/null +++ b/op-mode-definitions/restart-ssh.xml.in @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="restart"> + <children> + <node name="ssh"> + <properties> + <help>Restart SSH service</help> + </properties> + <command>if cli-shell-api existsActive service ssh; then sudo systemctl restart ssh.service; else echo "Service SSH not configured"; fi</command> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/restart.xml b/op-mode-definitions/restart.xml.in index c74ec9013..c74ec9013 100644 --- a/op-mode-definitions/restart.xml +++ b/op-mode-definitions/restart.xml.in diff --git a/op-mode-definitions/show-acceleration.xml b/op-mode-definitions/show-acceleration.xml.in index d0dcea2d6..d0dcea2d6 100644 --- a/op-mode-definitions/show-acceleration.xml +++ b/op-mode-definitions/show-acceleration.xml.in diff --git a/op-mode-definitions/show-bgp.xml.in b/op-mode-definitions/show-bgp.xml.in new file mode 100644 index 000000000..0d0a88dfb --- /dev/null +++ b/op-mode-definitions/show-bgp.xml.in @@ -0,0 +1,257 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="show"> + <children> + <node name="bgp"> + <properties> + <help>BGP information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + #include <include/bgp-afi-ipv4-ipv6-common.xml.i> + <tagNode name="ipv4"> + <properties> + <help>Network in the BGP routing table to display</help> + <completionHelp> + <list><x.x.x.x> <x.x.x.x/x> <h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> + </completionHelp> + </properties> + <children> + #include <include/bgp-prefix-bestpath-multipath.xml.i> + </children> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <node name="ipv4"> + <properties> + <help>IPv4 Address Family</help> + </properties> + <children> + #include <include/bgp-afi-common.xml.i> + #include <include/bgp-afi-ipv4-ipv6-common.xml.i> + </children> + </node> + <tagNode name="ipv6"> + <properties> + <help>Network in the BGP routing table to display</help> + <completionHelp> + <list><x.x.x.x> <x.x.x.x/x> <h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> + </completionHelp> + </properties> + <children> + #include <include/bgp-prefix-bestpath-multipath.xml.i> + </children> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <node name="ipv6"> + <properties> + <help>IPv6 Address Family</help> + </properties> + <children> + #include <include/bgp-afi-common.xml.i> + #include <include/bgp-afi-ipv4-ipv6-common.xml.i> + </children> + </node> + <node name="l2vpn"> + <properties> + <help>Layer 2 Virtual Private Network</help> + </properties> + <children> + <tagNode name="evpn"> + <properties> + <help>Network in the BGP routing table to display</help> + <completionHelp> + <list><x.x.x.x> <x.x.x.x/x> <h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <node name="evpn"> + <properties> + <help>Ethernet Virtual Private Network</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + #include <include/bgp-afi-common.xml.i> + <node name="all"> + <properties> + <help>Display information about all EVPN NLRIs</help> + </properties> + <children> + <leafNode name="overlay"> + <properties> + <help>Display BGP Overlay Information for prefixes</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="tags"> + <properties> + <help>Display BGP tags for prefixes</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </node> + <node name="es"> + <properties> + <help>Ethernet Segment</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + #include <include/vtysh-generic-detail.xml.i> + </children> + </node> + <node name="es-evi"> + <properties> + <help>Ethernet Segment per EVI</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + #include <include/vtysh-generic-detail.xml.i> + #include <include/vni-tagnode.xml.i> + </children> + </node> + <leafNode name="import-rt"> + <properties> + <help>Show import route target</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <tagNode name="neighbors"> + <properties> + <help>Show detailed BGP neighbor information</help> + <completionHelp> + <script>vtysh -c 'show bgp summary' | awk '{print $1'} | grep -e '^[0-9a-f]'</script> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <leafNode name="advertised-routes"> + <properties> + <help>Show routes advertised to a BGP neighbor</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="routes"> + <properties> + <help>Show routes learned from BGP neighbor</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </tagNode> + <tagNode name="rd"> + <properties> + <help>Show detailed BGP neighbor information</help> + <completionHelp> + <list>ASN:NN IPADDRESS:NN</list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <leafNode name="overlay"> + <properties> + <help>Display BGP Overlay Information for prefixes</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="tags"> + <properties> + <help>Display BGP tags for prefixes</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </tagNode> + <node name="route"> + <properties> + <help>EVPN route information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + #include <include/vtysh-generic-detail.xml.i> + <node name="type"> + <properties> + <help>Specify Route type</help> + </properties> + <children> + <leafNode name="1"> + <properties> + <help>EAD (Type-1) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="2"> + <properties> + <help>MAC-IP (Type-2) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="3"> + <properties> + <help>Multicast (Type-3) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="4"> + <properties> + <help>Ethernet Segment (Type-4) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="5"> + <properties> + <help>Prefix (Type-5) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="ead"> + <properties> + <help>EAD (Type-1) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="es"> + <properties> + <help>Ethernet Segment (Type-4) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="macip"> + <properties> + <help>MAC-IP (Type-2) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="multicast"> + <properties> + <help>Multicast (Type-3) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + <leafNode name="prefix"> + <properties> + <help>Prefix (Type-5) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </node> + #include <include/vni-tagnode-all.xml.i> + </children> + </node> + #include <include/vni-tagnode.xml.i> + <leafNode name="vni"> + <properties> + <help>VXLAN network identifier (VNI)</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> + </node> + </children> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/show-bridge.xml b/op-mode-definitions/show-bridge.xml.in index 8c1f7c398..78c350e44 100644 --- a/op-mode-definitions/show-bridge.xml +++ b/op-mode-definitions/show-bridge.xml.in @@ -29,6 +29,12 @@ </properties> <command>/sbin/brctl showstp $3</command> </leafNode> + <leafNode name="fdb"> + <properties> + <help>Show the forwarding database of the bridge</help> + </properties> + <command>/usr/sbin/bridge -c fdb show br $3</command> + </leafNode> </children> </tagNode> </children> diff --git a/op-mode-definitions/show-configuration.xml b/op-mode-definitions/show-configuration.xml.in index 318942ab0..318942ab0 100644 --- a/op-mode-definitions/show-configuration.xml +++ b/op-mode-definitions/show-configuration.xml.in diff --git a/op-mode-definitions/show-console-server.xml b/op-mode-definitions/show-console-server.xml.in index 77a7f3376..77a7f3376 100644 --- a/op-mode-definitions/show-console-server.xml +++ b/op-mode-definitions/show-console-server.xml.in diff --git a/op-mode-definitions/show-environment.xml b/op-mode-definitions/show-environment.xml.in index 95b658785..95b658785 100644 --- a/op-mode-definitions/show-environment.xml +++ b/op-mode-definitions/show-environment.xml.in diff --git a/op-mode-definitions/show-evpn.xml.in b/op-mode-definitions/show-evpn.xml.in new file mode 100644 index 000000000..0bdb41e7a --- /dev/null +++ b/op-mode-definitions/show-evpn.xml.in @@ -0,0 +1,46 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="show"> + <children> + <node name="evpn"> + <properties> + <help>Show Ethernet VPN (EVPN) information</help> + </properties> + <children> + <node name="arp-cache"> + <properties> + <help>ARP and ND cache</help> + </properties> + <children> + #include <include/vni-tagnode-all.xml.i> + </children> + </node> + <node name="mac"> + <properties> + <help>MAC addresses</help> + </properties> + <children> + #include <include/vni-tagnode-all.xml.i> + </children> + </node> + <node name="next-hops"> + <properties> + <help>Remote VTEPs</help> + </properties> + <children> + #include <include/vni-tagnode-all.xml.i> + </children> + </node> + <node name="rmac"> + <properties> + <help>RMAC</help> + </properties> + <children> + #include <include/vni-tagnode-all.xml.i> + </children> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/show-hardware.xml b/op-mode-definitions/show-hardware.xml.in index c3ff3a60f..0df2e4404 100644 --- a/op-mode-definitions/show-hardware.xml +++ b/op-mode-definitions/show-hardware.xml.in @@ -21,7 +21,7 @@ </node> <node name="summary"> <properties> - <help>Show CPU's on system</help> + <help>Show system CPUs</help> </properties> <command>${vyos_op_scripts_dir}/cpu_summary.py</command> </node> diff --git a/op-mode-definitions/show-history.xml b/op-mode-definitions/show-history.xml.in index 7fb286264..7fb286264 100644 --- a/op-mode-definitions/show-history.xml +++ b/op-mode-definitions/show-history.xml.in diff --git a/op-mode-definitions/show-host.xml b/op-mode-definitions/show-host.xml.in index eee1288a1..eee1288a1 100644 --- a/op-mode-definitions/show-host.xml +++ b/op-mode-definitions/show-host.xml.in diff --git a/op-mode-definitions/show-interfaces-bonding.xml b/op-mode-definitions/show-interfaces-bonding.xml.in index f6d9b3508..f6d9b3508 100644 --- a/op-mode-definitions/show-interfaces-bonding.xml +++ b/op-mode-definitions/show-interfaces-bonding.xml.in diff --git a/op-mode-definitions/show-interfaces-bridge.xml b/op-mode-definitions/show-interfaces-bridge.xml.in index cc4b248b6..cc4b248b6 100644 --- a/op-mode-definitions/show-interfaces-bridge.xml +++ b/op-mode-definitions/show-interfaces-bridge.xml.in diff --git a/op-mode-definitions/show-interfaces-dummy.xml b/op-mode-definitions/show-interfaces-dummy.xml.in index 7c24c6921..7c24c6921 100644 --- a/op-mode-definitions/show-interfaces-dummy.xml +++ b/op-mode-definitions/show-interfaces-dummy.xml.in diff --git a/op-mode-definitions/show-interfaces-ethernet.xml b/op-mode-definitions/show-interfaces-ethernet.xml.in index fc79f44bf..fc79f44bf 100644 --- a/op-mode-definitions/show-interfaces-ethernet.xml +++ b/op-mode-definitions/show-interfaces-ethernet.xml.in diff --git a/op-mode-definitions/show-interfaces-input.xml b/op-mode-definitions/show-interfaces-input.xml.in index 15e8203e5..15e8203e5 100644 --- a/op-mode-definitions/show-interfaces-input.xml +++ b/op-mode-definitions/show-interfaces-input.xml.in diff --git a/op-mode-definitions/show-interfaces-l2tpv3.xml b/op-mode-definitions/show-interfaces-l2tpv3.xml.in index 60fee34a1..60fee34a1 100644 --- a/op-mode-definitions/show-interfaces-l2tpv3.xml +++ b/op-mode-definitions/show-interfaces-l2tpv3.xml.in diff --git a/op-mode-definitions/show-interfaces-loopback.xml b/op-mode-definitions/show-interfaces-loopback.xml.in index b30b57909..b30b57909 100644 --- a/op-mode-definitions/show-interfaces-loopback.xml +++ b/op-mode-definitions/show-interfaces-loopback.xml.in diff --git a/op-mode-definitions/show-interfaces-macsec.xml b/op-mode-definitions/show-interfaces-macsec.xml.in index 6aeab66af..6aeab66af 100644 --- a/op-mode-definitions/show-interfaces-macsec.xml +++ b/op-mode-definitions/show-interfaces-macsec.xml.in diff --git a/op-mode-definitions/show-interfaces-pppoe.xml b/op-mode-definitions/show-interfaces-pppoe.xml.in index 393ca912f..393ca912f 100644 --- a/op-mode-definitions/show-interfaces-pppoe.xml +++ b/op-mode-definitions/show-interfaces-pppoe.xml.in diff --git a/op-mode-definitions/show-interfaces-pseudo-ethernet.xml b/op-mode-definitions/show-interfaces-pseudo-ethernet.xml.in index 195944745..195944745 100644 --- a/op-mode-definitions/show-interfaces-pseudo-ethernet.xml +++ b/op-mode-definitions/show-interfaces-pseudo-ethernet.xml.in diff --git a/op-mode-definitions/show-interfaces-tunnel.xml b/op-mode-definitions/show-interfaces-tunnel.xml.in index 416de0299..416de0299 100644 --- a/op-mode-definitions/show-interfaces-tunnel.xml +++ b/op-mode-definitions/show-interfaces-tunnel.xml.in diff --git a/op-mode-definitions/show-interfaces-vti.xml b/op-mode-definitions/show-interfaces-vti.xml.in index f51be2d19..f51be2d19 100644 --- a/op-mode-definitions/show-interfaces-vti.xml +++ b/op-mode-definitions/show-interfaces-vti.xml.in diff --git a/op-mode-definitions/show-interfaces-vxlan.xml b/op-mode-definitions/show-interfaces-vxlan.xml.in index 4e3cb93cd..4e3cb93cd 100644 --- a/op-mode-definitions/show-interfaces-vxlan.xml +++ b/op-mode-definitions/show-interfaces-vxlan.xml.in diff --git a/op-mode-definitions/show-interfaces-wirelessmodem.xml b/op-mode-definitions/show-interfaces-wirelessmodem.xml.in index c0ab9c66f..c0ab9c66f 100644 --- a/op-mode-definitions/show-interfaces-wirelessmodem.xml +++ b/op-mode-definitions/show-interfaces-wirelessmodem.xml.in diff --git a/op-mode-definitions/show-interfaces.xml b/op-mode-definitions/show-interfaces.xml.in index 39b0f0a2c..39b0f0a2c 100644 --- a/op-mode-definitions/show-interfaces.xml +++ b/op-mode-definitions/show-interfaces.xml.in diff --git a/op-mode-definitions/show-ip-access-paths-prefix-community-lists.xml b/op-mode-definitions/show-ip-access-paths-prefix-community-lists.xml.in index a5ec65c94..d7222516a 100644 --- a/op-mode-definitions/show-ip-access-paths-prefix-community-lists.xml +++ b/op-mode-definitions/show-ip-access-paths-prefix-community-lists.xml.in @@ -11,7 +11,7 @@ <properties> <help>Show all IP access-lists</help> </properties> - <command>/usr/bin/vtysh -c "show ip access-list"</command> + <command>vtysh -c "show ip access-list"</command> </leafNode> <tagNode name="access-list"> <properties> @@ -20,13 +20,13 @@ <path>policy access-list</path> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show ip access-list $4"</command> + <command>vtysh -c "show ip access-list $4"</command> </tagNode> <leafNode name="as-path-access-list"> <properties> <help>Show all as-path-access-lists</help> </properties> - <command>/usr/bin/vtysh -c "show ip as-path-access-list"</command> + <command>vtysh -c "show ip as-path-access-list"</command> </leafNode> <tagNode name="as-path-access-list"> <properties> @@ -35,13 +35,13 @@ <path>policy as-path-list</path> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show ip as-path-access-list $4"</command> + <command>vtysh -c "show ip as-path-access-list $4"</command> </tagNode> <leafNode name="community-list"> <properties> <help>Show IP community-lists</help> </properties> - <command>/usr/bin/vtysh -c "show bgp community-list"</command> + <command>vtysh -c "show bgp community-list"</command> </leafNode> <tagNode name="community-list"> <properties> @@ -50,13 +50,13 @@ <path>policy community-list</path> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show bgp community-list $4 detail"</command> + <command>vtysh -c "show bgp community-list $4 detail"</command> </tagNode> <leafNode name="extcommunity-list"> <properties> <help>Show extended IP community-lists</help> </properties> - <command>/usr/bin/vtysh -c "show bgp extcommunity-list"</command> + <command>vtysh -c "show bgp extcommunity-list"</command> </leafNode> <tagNode name="extcommunity-list"> <properties> @@ -65,19 +65,19 @@ <path>policy extcommunity-list</path> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show bgp extcommunity-list $4 detail"</command> + <command>vtysh -c "show bgp extcommunity-list $4 detail"</command> </tagNode> <leafNode name="forwarding"> <properties> <help>Show IP forwarding status</help> </properties> - <command>/usr/bin/vtysh -c "show ip forwarding"</command> + <command>vtysh -c "show ip forwarding"</command> </leafNode> <leafNode name="large-community-list"> <properties> <help>Show IP large-community-lists</help> </properties> - <command>/usr/bin/vtysh -c "show bgp large-community-list"</command> + <command>vtysh -c "show bgp large-community-list"</command> </leafNode> <tagNode name="large-community-list"> <properties> @@ -86,13 +86,13 @@ <path>policy large-community-list</path> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show bgp large-community-list $4 detail"</command> + <command>vtysh -c "show bgp large-community-list $4 detail"</command> </tagNode> <leafNode name="prefix-list"> <properties> <help>Show all IP prefix-lists</help> </properties> - <command>/usr/bin/vtysh -c "show ip prefix-list"</command> + <command>vtysh -c "show ip prefix-list"</command> </leafNode> <tagNode name="prefix-list"> <properties> @@ -101,13 +101,13 @@ <path>policy prefix-list</path> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show ip prefix-list $4"</command> + <command>vtysh -c "show ip prefix-list $4"</command> </tagNode> <leafNode name="protocol"> <properties> <help>Show IP route-maps per protocol</help> </properties> - <command>/usr/bin/vtysh -c "show ip protocol"</command> + <command>vtysh -c "show ip protocol"</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-ip-bgp.xml b/op-mode-definitions/show-ip-bgp.xml deleted file mode 100644 index 5eb2ae56e..000000000 --- a/op-mode-definitions/show-ip-bgp.xml +++ /dev/null @@ -1,342 +0,0 @@ -<?xml version="1.0"?> -<interfaceDefinition> - <node name="show"> - <children> - <node name="ip"> - <children> - <node name="bgp"> - <properties> - <help>Show Border Gateway Protocol (BGP) information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp"</command> - <children> - <leafNode name="attribute-info"> - <properties> - <help>Show BGP attribute information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp attribute-info"</command> - </leafNode> - <leafNode name="cidr-only"> - <properties> - <help>Display only routes with non-natural netmasks</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp cidr-only"</command> - </leafNode> - <node name="community"> - <properties> - <help>Show BGP routes matching the communities</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp community"</command> - </node> - <tagNode name="community"> - <properties> - <help>Display routes matching the specified communities</help> - <completionHelp> - <list><AA:NN> local-AS no-advertise no-export</list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp community $5"</command> - </tagNode> - <leafNode name="community-info"> - <properties> - <help>List all bgp community information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp community-info"</command> - </leafNode> - <tagNode name="community-list"> - <properties> - <help>Show BGP routes matching specified community list</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp community-list $5"</command> - <children> - <leafNode name="exact-match"> - <properties> - <help>Show BGP routes exactly matching specified community list</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp community-list $5 exact-match"</command> - </leafNode> - </children> - </tagNode> - <leafNode name="dampened-paths"> - <properties> - <help>Show dampened BGP paths</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp dampening dampened-paths"</command> - </leafNode> - <tagNode name="filter-list"> - <properties> - <help>Show BGP information for specified word</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp filter-list $5"</command> - </tagNode> - <leafNode name="flap-statistics"> - <properties> - <help>Show flap statistics of routes</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp dampening flap-statistics"</command> - </leafNode> - <node name="ipv4"> - <properties> - <help>Show BGP IPv4 information</help> - </properties> - <children> - <node name="unicast"> - <properties> - <help>Show BGP IPv4 unicast information</help> - </properties> - <children> - <leafNode name="cidr-only"> - <properties> - <help>Display only routes with non-natural netmasks</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast cidr-only"</command> - </leafNode> - <node name="community"> <!-- START new code --> - <properties> - <help>Show BGP routes matching the communities</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast community"</command> - </node> - <tagNode name="community"> - <properties> - <help>Display routes matching the specified communities</help> - <completionHelp> - <list><AA:NN> local-AS no-advertise no-export</list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast community $7"</command> - </tagNode> - <tagNode name="community-list"> - <properties> - <help>Show BGP routes matching specified community list</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast community-list $7"</command> - <children> - <leafNode name="exact-match"> - <properties> - <help>Show BGP routes exactly matching specified community list</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast community-list $7 exact-match"</command> - </leafNode> - </children> - </tagNode> - <tagNode name="filter-list"> - <properties> - <help>Show BGP information for specified word</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp filter-list $5"</command> - </tagNode> - <tagNode name="neighbors"> - <properties> - <help>Show detailed BGP IPv4 unicast neighbor information</help> - <completionHelp> - <script>vtysh -c "show ip bgp ipv4 unicast summary" | awk '{print $1}' | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b"</script> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast neighbors $7"</command> - <children> - <leafNode name="advertised-routes"> - <properties> - <help>Show routes advertised to a BGP neighbor</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast neighbor $7 advertised-routes"</command> - </leafNode> - <leafNode name="prefix-counts"> - <properties> - <help>Show detailed prefix count information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast neighbor $7 prefix-counts"</command> - </leafNode> - <leafNode name="received-routes"> - <properties> - <help>Show the received routes from neighbor</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast neighbor $7 received-routes"</command> - </leafNode> - <leafNode name="routes"> - <properties> - <help>Show routes learned from neighbor</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast neighbor $7 routes"</command> - </leafNode> - </children> - </tagNode> - <leafNode name="paths"> - <properties> - <help>Show BGP path information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast paths"</command> - </leafNode> - <tagNode name="prefix-list"> - <properties> - <help>Show BGP routes matching the specified prefix list</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast prefix-list $7"</command> - </tagNode> - <tagNode name="regexp"> - <properties> - <help>Show BGP routes matching the specified AS path regular expression</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast regexp $5"</command> - </tagNode> - <tagNode name="route-map"> - <properties> - <help>Show BGP routes matching the specified route map</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp route-map $5"</command> - </tagNode> - <leafNode name="summary"> - <properties> - <help>Show summary of BGP information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp summary"</command> - </leafNode> - </children> - </node> - <tagNode name="unicast"> - <properties> - <help>Show BGP information for specified IP address or prefix</help> - <completionHelp> - <list><x.x.x.x> <x.x.x.x/x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp $6"</command> - </tagNode> - </children> - </node> - <node name="large-community"> - <properties> - <help>Show BGP routes matching the specified large-communities</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp large-community"</command> - </node> - <leafNode name="large-community-info"> - <properties> - <help>Show BGP large-community information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp large-community-info"</command> - </leafNode> - <tagNode name="large-community-list"> - <properties> - <help>Show BGP routes matching the specified large-community list</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp large-community-list $5"</command> - </tagNode> - <leafNode name="memory"> - <properties> - <help>Show BGP memory usage</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp memory"</command> - </leafNode> - <tagNode name="neighbors"> - <properties> - <help>Show detailed BGP IPv4 unicast neighbor information</help> - <completionHelp> - <script>vtysh -c "show ip bgp summary" | awk '{print $1}' | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b"</script> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp neighbors $5"</command> - <children> - <leafNode name="advertised-routes"> - <properties> - <help>Show routes advertised to a BGP neighbor</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp neighbor $5 advertised-routes"</command> - </leafNode> - <leafNode name="dampened-routes"> - <properties> - <help>Show dampened routes received from BGP neighbor</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp neighbor $5 dampened-routes"</command> - </leafNode> - <leafNode name="flap-statistics"> - <properties> - <help>Show flap statistics of the routes learned from BGP neighbor</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp neighbor $5 flap-statistics"</command> - </leafNode> - <leafNode name="prefix-counts"> - <properties> - <help>Show detailed prefix count information for BGP neighbor</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp neighbor $5 prefix-counts"</command> - </leafNode> - <node name="received"> - <properties> - <help>Show information received from BGP neighbor</help> - </properties> - <children> - <leafNode name="prefix-filter"> - <properties> - <help>Show prefixlist filter</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp neighbor $5 received prefix-filter"</command> - </leafNode> - </children> - </node> - <leafNode name="received-routes"> - <properties> - <help>Show received routes from BGP neighbor</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp neighbor $5 received-routes"</command> - </leafNode> - <leafNode name="routes"> - <properties> - <help>Show routes learned from BGP neighbor</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp neighbor $5 routes"</command> - </leafNode> - </children> - </tagNode> - <leafNode name="paths"> - <properties> - <help>Show BGP path information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp paths"</command> - </leafNode> - <tagNode name="prefix-list"> - <properties> - <help>Show BGP routes matching the specified prefix list</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp prefix-list $5"</command> - </tagNode> - <tagNode name="regexp"> - <properties> - <help>Show BGP routes matching the specified AS path regular expression</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp regexp $5"</command> - </tagNode> - <tagNode name="route-map"> - <properties> - <help>Show BGP routes matching the specified route map</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp route-map $5"</command> - </tagNode> - <leafNode name="statistics"> - <properties> - <help>Show summary of BGP information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp statistics"</command> - </leafNode> - <leafNode name="summary"> - <properties> - <help>Show summary of BGP information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp summary"</command> - </leafNode> - </children> - </node> - <tagNode name="bgp"> - <properties> - <help>Show BGP information for specified IP address or prefix</help> - <completionHelp> - <list><x.x.x.x> <x.x.x.x/x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip bgp $4"</command> - </tagNode> - </children> - </node> - </children> - </node> -</interfaceDefinition> diff --git a/op-mode-definitions/show-ip-bgp.xml.in b/op-mode-definitions/show-ip-bgp.xml.in new file mode 100644 index 000000000..9a271b4a5 --- /dev/null +++ b/op-mode-definitions/show-ip-bgp.xml.in @@ -0,0 +1,32 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="show"> + <children> + <node name="ip"> + <children> + <node name="bgp"> + <properties> + <help>Show Border Gateway Protocol (BGP) information</help> + </properties> + <command>vtysh -c "show ip bgp"</command> + <children> + #include <include/bgp-common.xml.i> + <tagNode name="vrf"> + <properties> + <help>Show bgp routing protocol for given VRF</help> + <completionHelp> + <path>vrf name</path> + <list>all</list> + </completionHelp> + </properties> + <children> + #include <include/bgp-common.xml.i> + </children> + </tagNode> + </children> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/show-ip-igmp.xml b/op-mode-definitions/show-ip-igmp.xml.in index b8f2f9107..855c5d508 100644 --- a/op-mode-definitions/show-ip-igmp.xml +++ b/op-mode-definitions/show-ip-igmp.xml.in @@ -13,31 +13,31 @@ <properties> <help>IGMP groups information</help> </properties> - <command>/usr/bin/vtysh -c "show ip igmp groups"</command> + <command>vtysh -c "show ip igmp groups"</command> </leafNode> <leafNode name="interfaces"> <properties> <help>IGMP interfaces information</help> </properties> - <command>/usr/bin/vtysh -c "show ip igmp interface"</command> + <command>vtysh -c "show ip igmp interface"</command> </leafNode> <leafNode name="join"> <properties> <help>IGMP static join information</help> </properties> - <command>/usr/bin/vtysh -c "show ip igmp join"</command> + <command>vtysh -c "show ip igmp join"</command> </leafNode> <leafNode name="sources"> <properties> <help>IGMP sources information</help> </properties> - <command>/usr/bin/vtysh -c "show ip igmp sources"</command> + <command>vtysh -c "show ip igmp sources"</command> </leafNode> <leafNode name="statistics"> <properties> <help>IGMP statistics</help> </properties> - <command>/usr/bin/vtysh -c "show ip igmp statistics"</command> + <command>vtysh -c "show ip igmp statistics"</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-ip-multicast.xml b/op-mode-definitions/show-ip-multicast.xml.in index 5331d2e35..80d83b424 100644 --- a/op-mode-definitions/show-ip-multicast.xml +++ b/op-mode-definitions/show-ip-multicast.xml.in @@ -25,13 +25,13 @@ <properties> <help>IP multicast information</help> </properties> - <command>/usr/bin/vtysh -c "show ip multicast"</command> + <command>vtysh -c "show ip multicast"</command> </leafNode> <leafNode name="route"> <properties> <help>IP multicast routing table</help> </properties> - <command>/usr/bin/vtysh -c "show ip mroute"</command> + <command>vtysh -c "show ip mroute"</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-ip-ospf.xml b/op-mode-definitions/show-ip-ospf.xml deleted file mode 100644 index 99441d185..000000000 --- a/op-mode-definitions/show-ip-ospf.xml +++ /dev/null @@ -1,579 +0,0 @@ -<?xml version="1.0"?> -<interfaceDefinition> - <node name="show"> - <children> - <node name="ip"> - <properties> - <help>Show IPv4 routing information</help> - </properties> - <children> - <node name="ospf"> - <properties> - <help>Show IPv4 Open Shortest Path First (OSPF) routing information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf"</command> - <children> - <leafNode name="border-routers"> - <properties> - <help>Show IPv4 OSPF border-routers information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf border-routers"</command> - </leafNode> - <node name="database"> - <properties> - <help>Show IPv4 OSPF database information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database"</command> - <children> - <node name="asbr-summary"> - <properties> - <help>Show IPv4 OSPF ASBR summary database</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database asbr-summary"</command> - <children> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF ASBR summary database for given address of advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database asbr-summary adv-router $7"</command> - </tagNode> - <node name="adv-router"> - <properties> - <help>Show IPv4 OSPF ASBR summary database for given address of advertised router</help> - </properties> - </node> - </children> - </node> - <tagNode name="asbr-summary"> - <properties> - <help>Show IPv4 OSPF ASBR summary database information of given address</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database asbr-summary"</command> - <children> - <node name="adv-router"> - <properties> - <help>Show advertising router link states</help> - </properties> - </node> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF ASBR summary database of given address for given advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database asbr-summary $6 adv-router $8"</command> - </tagNode> - <leafNode name="self-originate"> - <properties> - <help>Show summary of self-originate IPv4 OSPF ASBR database</help> - </properties> - <command>show ip ospf database asbr-summary $6 self-originate</command> - </leafNode> - </children> - </tagNode> - <node name="external"> - <properties> - <help>Show IPv4 OSPF external database</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database external"</command> - <children> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF external database for specified IP address of advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database external adv-router $7"</command> - </tagNode> - <node name="adv-router"> - <properties> - <help>Show IPv4 OSPF external database for specified IP address of advertised router</help> - </properties> - </node> - </children> - </node> - <tagNode name="external"> - <properties> - <help>Show IPv4 OSPF external database information of specified IP address</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database external"</command> - <children> - <node name="adv-router"> - <properties> - <help>Show advertising router link states</help> - </properties> - </node> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF external database of specified IP address for specified advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database external $6 adv-router $8"</command> - </tagNode> - <leafNode name="self-originate"> - <properties> - <help>Show self-originate IPv4 OSPF external database</help> - </properties> - <command>show ip ospf database external $6 self-originate</command> - </leafNode> - </children> - </tagNode> - <leafNode name="max-age"> - <properties> - <help>Show IPv4 OSPF max-age database</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database max-age"</command> - </leafNode> - <node name="network"> - <properties> - <help>Show IPv4 OSPF network database</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database network"</command> - <children> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF network database for specified IP address of advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database network adv-router $7"</command> - </tagNode> - <node name="adv-router"> - <properties> - <help>Show IPv4 OSPF network database for given address of advertised router</help> - </properties> - </node> - </children> - </node> - <tagNode name="network"> - <properties> - <help>Show IPv4 OSPF network database information of specified IP address</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database network"</command> - <children> - <node name="adv-router"> - <properties> - <help>Show advertising router link states</help> - </properties> - </node> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF network database of specified IP address for specified advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database network $6 adv-router $8"</command> - </tagNode> - <leafNode name="self-originate"> - <properties> - <help>Show self-originate IPv4 OSPF network database</help> - </properties> - <command>show ip ospf database network $6 self-originate</command> - </leafNode> - </children> - </tagNode> - <node name="nssa-external"> - <properties> - <help>Show IPv4 OSPF NSSA external database</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database nssa-external"</command> - <children> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF NSSA external database for specified IP address of advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database nssa-external adv-router $7"</command> - </tagNode> - <node name="adv-router"> - <properties> - <help>Show IPv4 OSPF NSSA external database for specified IP address of advertised router</help> - </properties> - </node> - </children> - </node> - <tagNode name="nssa-external"> - <properties> - <help>Show IPv4 OSPF NSSA external database information of specified IP address</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database nssa-external"</command> - <children> - <node name="adv-router"> - <properties> - <help>Show advertising router link states</help> - </properties> - </node> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF NSSA external database of specified IP address for specified advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database nssa-external $6 adv-router $8"</command> - </tagNode> - <leafNode name="self-originate"> - <properties> - <help>Show self-originate IPv4 OSPF NSSA external database</help> - </properties> - <command>show ip ospf database nssa-external $6 self-originate</command> - </leafNode> - </children> - </tagNode> - <node name="opaque-area"> - <properties> - <help>Show IPv4 OSPF opaque-area database</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database opaque-area"</command> - <children> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF opaque-area database for specified IP address of advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database opaque-area adv-router $7"</command> - </tagNode> - <node name="adv-router"> - <properties> - <help>Show IPv4 OSPF opaque-area database for specified IP address of advertised router</help> - </properties> - </node> - </children> - </node> - <tagNode name="opaque-area"> - <properties> - <help>Show IPv4 OSPF opaque-area database information of specified IP address</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database opaque-area"</command> - <children> - <node name="adv-router"> - <properties> - <help>Show advertising router link states</help> - </properties> - </node> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF opaque-area database of specified IP address for specified advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database opaque-area $6 adv-router $8"</command> - </tagNode> - <leafNode name="self-originate"> - <properties> - <help>Show self-originate IPv4 OSPF opaque-area database</help> - </properties> - <command>show ip ospf database opaque-area $6 self-originate</command> - </leafNode> - </children> - </tagNode> - <node name="opaque-as"> - <properties> - <help>Show IPv4 OSPF opaque-as database</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database opaque-as"</command> - <children> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF opaque-as database for specified IP address of advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database opaque-as adv-router $7"</command> - </tagNode> - <node name="adv-router"> - <properties> - <help>Show IPv4 OSPF opaque-as database for specified IP address of advertised router</help> - </properties> - </node> - </children> - </node> - <tagNode name="opaque-as"> - <properties> - <help>Show IPv4 OSPF opaque-as database information of specified IP address</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database opaque-as"</command> - <children> - <node name="adv-router"> - <properties> - <help>Show advertising router link states</help> - </properties> - </node> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF opaque-as database of specified IP address for specified advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database opaque-as $6 adv-router $8"</command> - </tagNode> - <leafNode name="self-originate"> - <properties> - <help>Show self-originate IPv4 OSPF opaque-as database</help> - </properties> - <command>show ip ospf database opaque-as $6 self-originate</command> - </leafNode> - </children> - </tagNode> - <node name="opaque-link"> - <properties> - <help>Show IPv4 OSPF opaque-link database</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database opaque-link"</command> - <children> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF opaque-link database for specified IP address of advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database opaque-link adv-router $7"</command> - </tagNode> - <node name="adv-router"> - <properties> - <help>Show IPv4 OSPF opaque-link database for specified IP address of advertised router</help> - </properties> - </node> - </children> - </node> - <tagNode name="opaque-link"> - <properties> - <help>Show IPv4 OSPF opaque-link database information of specified IP address</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database opaque-link"</command> - <children> - <node name="adv-router"> - <properties> - <help>Show advertising router link states</help> - </properties> - </node> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF opaque-link database of specified IP address for specified advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database opaque-link $6 adv-router $8"</command> - </tagNode> - <leafNode name="self-originate"> - <properties> - <help>Show self-originate IPv4 OSPF opaque-link database</help> - </properties> - <command>show ip ospf database opaque-link $6 self-originate</command> - </leafNode> - </children> - </tagNode> - <node name="router"> - <properties> - <help>Show IPv4 OSPF router database</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database router"</command> - <children> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF router database for specified IP address of advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database router adv-router $7"</command> - </tagNode> - <node name="adv-router"> - <properties> - <help>Show IPv4 OSPF router database for specified IP address of advertised router</help> - </properties> - </node> - </children> - </node> - <tagNode name="router"> - <properties> - <help>Show IPv4 OSPF router database information of specified IP address</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database router"</command> - <children> - <node name="adv-router"> - <properties> - <help>Show advertising router link states</help> - </properties> - </node> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF router database of specified IP address for specified advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database router $6 adv-router $8"</command> - </tagNode> - <leafNode name="self-originate"> - <properties> - <help>Show self-originate IPv4 OSPF router database</help> - </properties> - <command>show ip ospf database router $6 self-originate</command> - </leafNode> - </children> - </tagNode> - <leafNode name="self-originate"> - <properties> - <help>Show IPv4 OSPF self-originate database</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database self-originate"</command> - </leafNode> - <node name="summary"> - <properties> - <help>Show summary of IPv4 OSPF database</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database summary"</command> - <children> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF summary database for specified IP address of advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database summary adv-router $7"</command> - </tagNode> - <node name="adv-router"> - <properties> - <help>Show IPv4 OSPF summary database for specified IP address of advertised router</help> - </properties> - </node> - </children> - </node> - <tagNode name="summary"> - <properties> - <help>Show IPv4 OSPF summary database information of specified IP address</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database summary"</command> - <children> - <node name="adv-router"> - <properties> - <help>Show advertising router link states</help> - </properties> - </node> - <tagNode name="adv-router"> - <properties> - <help>Show IPv4 OSPF summary database of specified IP address for specified advertised router</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf database summary $6 adv-router $8"</command> - </tagNode> - <leafNode name="self-originate"> - <properties> - <help>Show self-originate IPv4 OSPF summary database</help> - </properties> - <command>show ip ospf database summary $6 self-originate</command> - </leafNode> - </children> - </tagNode> - </children> - </node> - <node name="interface"> - <properties> - <help>Show IPv4 OSPF interface information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf interface"</command> - </node> - <tagNode name="interface"> - <properties> - <help>Show IPv4 OSPF information for specified interface</help> - <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py</script> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf interface $5"</command> - </tagNode> - <node name="neighbor"> - <properties> - <help>Show IPv4 OSPF neighbor information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf neighbor"</command> - <children> - <tagNode name="address"> - <properties> - <help>Show IPv4 OSPF neighbor information for specified IP address</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf neighbor $6"</command> - </tagNode> - <node name="detail"> - <properties> - <help>Show detailed IPv4 OSPF neighbor information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf neighbor detail"</command> - </node> - </children> - </node> - <tagNode name="neighbor"> - <properties> - <help>Show IPv4 OSPF neighbor information for specified IP address or interface</help> - <completionHelp> - <list><x.x.x.x></list> - <script>${vyos_completion_dir}/list_interfaces.py</script> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf neighbor $5"</command> - </tagNode> - <leafNode name="route"> - <properties> - <help>Show IPv4 OSPF route information</help> - </properties> - <command>/usr/bin/vtysh -c "show ip ospf route"</command> - </leafNode> - </children> - </node> - </children> - </node> - </children> - </node> -</interfaceDefinition> diff --git a/op-mode-definitions/show-ip-ospf.xml.in b/op-mode-definitions/show-ip-ospf.xml.in new file mode 100644 index 000000000..704ed984f --- /dev/null +++ b/op-mode-definitions/show-ip-ospf.xml.in @@ -0,0 +1,36 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="show"> + <children> + <node name="ip"> + <properties> + <help>Show IPv4 routing information</help> + </properties> + <children> + <node name="ospf"> + <properties> + <help>Show IPv4 Open Shortest Path First (OSPF) routing information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + #include <include/ospf-common.xml.i> + <tagNode name="vrf"> + <properties> + <help>Show OSPF routing protocol for given VRF</help> + <completionHelp> + <path>vrf name</path> + <list>all</list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + #include <include/ospf-common.xml.i> + </children> + </tagNode> + </children> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/show-ip-pim.xml b/op-mode-definitions/show-ip-pim.xml.in index 3f4edc779..fa317a944 100644 --- a/op-mode-definitions/show-ip-pim.xml +++ b/op-mode-definitions/show-ip-pim.xml.in @@ -13,55 +13,55 @@ <properties> <help>PIM interfaces information</help> </properties> - <command>/usr/bin/vtysh -c "show ip pim interface"</command> + <command>vtysh -c "show ip pim interface"</command> </leafNode> <leafNode name="join"> <properties> <help>PIM join information</help> </properties> - <command>/usr/bin/vtysh -c "show ip pim join"</command> + <command>vtysh -c "show ip pim join"</command> </leafNode> <leafNode name="neighbor"> <properties> <help>PIM neighbor information</help> </properties> - <command>/usr/bin/vtysh -c "show ip pim neighbor"</command> + <command>vtysh -c "show ip pim neighbor"</command> </leafNode> <leafNode name="nexthop"> <properties> <help>PIM cached nexthop rpf information</help> </properties> - <command>/usr/bin/vtysh -c "show ip pim nexthop"</command> + <command>vtysh -c "show ip pim nexthop"</command> </leafNode> <leafNode name="state"> <properties> <help>PIM state information</help> </properties> - <command>/usr/bin/vtysh -c "show ip pim state"</command> + <command>vtysh -c "show ip pim state"</command> </leafNode> <leafNode name="statistics"> <properties> <help>PIM statistics</help> </properties> - <command>/usr/bin/vtysh -c "show ip pim statistics"</command> + <command>vtysh -c "show ip pim statistics"</command> </leafNode> <leafNode name="rp"> <properties> <help>PIM RP (Rendevous Point) information</help> </properties> - <command>/usr/bin/vtysh -c "show ip pim rp-info"</command> + <command>vtysh -c "show ip pim rp-info"</command> </leafNode> <leafNode name="rpf"> <properties> <help>PIM cached source rpf information</help> </properties> - <command>/usr/bin/vtysh -c "show ip pim rpf"</command> + <command>vtysh -c "show ip pim rpf"</command> </leafNode> <leafNode name="upstream"> <properties> <help>PIM upstream information</help> </properties> - <command>/usr/bin/vtysh -c "show ip pim upstream"</command> + <command>vtysh -c "show ip pim upstream"</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-ip-ports.xml b/op-mode-definitions/show-ip-ports.xml.in index a74b68ffc..a74b68ffc 100644 --- a/op-mode-definitions/show-ip-ports.xml +++ b/op-mode-definitions/show-ip-ports.xml.in diff --git a/op-mode-definitions/show-ip-rip.xml b/op-mode-definitions/show-ip-rip.xml.in index b61ab10a7..768a86ca1 100644 --- a/op-mode-definitions/show-ip-rip.xml +++ b/op-mode-definitions/show-ip-rip.xml.in @@ -11,13 +11,13 @@ <properties> <help>Show Routing Information Protocol (RIP) information</help> </properties> - <command>/usr/bin/vtysh -c "show ip rip"</command> + <command>vtysh -c "show ip rip"</command> <children> <leafNode name="status"> <properties> <help>Show RIP protocol status</help> </properties> - <command>/usr/bin/vtysh -c "show ip rip status"</command> + <command>vtysh -c "show ip rip status"</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-ip-route.xml b/op-mode-definitions/show-ip-route.xml.in index a98048785..729572b4a 100644 --- a/op-mode-definitions/show-ip-route.xml +++ b/op-mode-definitions/show-ip-route.xml.in @@ -11,13 +11,13 @@ <properties> <help>Show IP routes</help> </properties> - <command>/usr/bin/vtysh -c "show ip route"</command> + <command>vtysh -c "show ip route"</command> <children> <leafNode name="bgp"> <properties> <help>Show IP BGP routes</help> </properties> - <command>/usr/bin/vtysh -c "show ip route bgp"</command> + <command>vtysh -c "show ip route bgp"</command> </leafNode> <node name="cache"> <properties> @@ -38,7 +38,7 @@ <properties> <help>Show IP connected routes</help> </properties> - <command>/usr/bin/vtysh -c "show ip route connected"</command> + <command>vtysh -c "show ip route connected"</command> </leafNode> <node name="forward"> <properties> @@ -59,43 +59,43 @@ <properties> <help>Show IP IS-IS routes</help> </properties> - <command>/usr/bin/vtysh -c "show ip route isis"</command> + <command>vtysh -c "show ip route isis"</command> </leafNode> <leafNode name="kernel"> <properties> <help>Show IP kernel routes</help> </properties> - <command>/usr/bin/vtysh -c "show ip route kernel"</command> + <command>vtysh -c "show ip route kernel"</command> </leafNode> <leafNode name="ospf"> <properties> <help>Show IP OSPF routes</help> </properties> - <command>/usr/bin/vtysh -c "show ip route ospf"</command> + <command>vtysh -c "show ip route ospf"</command> </leafNode> <leafNode name="rip"> <properties> <help>Show IP RIP routes</help> </properties> - <command>/usr/bin/vtysh -c "show ip route rip"</command> + <command>vtysh -c "show ip route rip"</command> </leafNode> <leafNode name="static"> <properties> <help>Show IP static routes</help> </properties> - <command>/usr/bin/vtysh -c "show ip route static"</command> + <command>vtysh -c "show ip route static"</command> </leafNode> <leafNode name="summary"> <properties> <help>Show IP routes summary</help> </properties> - <command>/usr/bin/vtysh -c "show ip route summary"</command> + <command>vtysh -c "show ip route summary"</command> </leafNode> <leafNode name="supernets-only"> <properties> <help>Show IP supernet routes</help> </properties> - <command>/usr/bin/vtysh -c "show ip route supernets-only"</command> + <command>vtysh -c "show ip route supernets-only"</command> </leafNode> <node name="table"> <properties> @@ -109,7 +109,7 @@ <list><1-200></list> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show ip route table $5"</command> + <command>vtysh -c "show ip route table $5"</command> </tagNode> <node name="tag"> <properties> @@ -123,7 +123,7 @@ <list><1-4294967295></list> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show ip route tag $5"</command> + <command>vtysh -c "show ip route tag $5"</command> </tagNode> <node name="vrf"> <properties> @@ -138,7 +138,7 @@ <path>vrf name</path> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show ip route vrf $5"</command> + <command>vtysh -c "show ip route vrf $5"</command> </tagNode> </children> </node> @@ -149,13 +149,13 @@ <list><x.x.x.x> <x.x.x.x/x></list> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show ip route $4"</command> + <command>vtysh -c "show ip route $4"</command> <children> <leafNode name="longer-prefixes"> <properties> <help>Show longer prefixes of routes for specified IP address or prefix</help> </properties> - <command>/usr/bin/vtysh -c "show ip route $4 longer-prefixes"</command> + <command>vtysh -c "show ip route $4 longer-prefixes"</command> </leafNode> </children> </tagNode> diff --git a/op-mode-definitions/show-ip.xml.in b/op-mode-definitions/show-ip.xml.in new file mode 100644 index 000000000..91564440d --- /dev/null +++ b/op-mode-definitions/show-ip.xml.in @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="show"> + <children> + <node name="ip"> + <properties> + <help>Show IPv4 routing information</help> + </properties> + <children> + <node name="neighbors"> + <properties> + <help>Show IPv4 Neighbor Discovery (ND) information</help> + </properties> + <command>${vyos_op_scripts_dir}/show_neigh.py --family inet</command> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/show-ipv6-bgp.xml b/op-mode-definitions/show-ipv6-bgp.xml deleted file mode 100644 index aad61b97a..000000000 --- a/op-mode-definitions/show-ipv6-bgp.xml +++ /dev/null @@ -1,203 +0,0 @@ -<?xml version="1.0"?> -<interfaceDefinition> - <node name="show"> - <children> - <node name="ipv6"> - <properties> - <help>Show IPv6 routing information</help> - </properties> - <children> - <node name="bgp"> - <properties> - <help>Show Border Gateway Protocol (BGP) information</help> - </properties> - <command>vtysh -c "show bgp ipv6"</command> - <children> - <leafNode name="summary"> - <properties> - <help>Show summary of BGP neighbor status</help> - </properties> - <command>vtysh -c "show bgp ipv6 summary"</command> - </leafNode> - <tagNode name="regexp"> - <properties> - <help>Show routes matching AS path regular expression</help> - </properties> - <command>vtysh -c "show bgp ipv6 regexp $5"</command> - </tagNode> - <tagNode name="prefix-list"> - <properties> - <help>Show routes matching the IPv6 prefix-list name</help> - <completionHelp> - <path>policy prefix-list6</path> - </completionHelp> - </properties> - <command>vtysh -c "show bgp ipv6 prefix-list $5"</command> - </tagNode> - <tagNode name="neighbors"> - <properties> - <help>Show detailed information on TCP and BGP neighbor connections for given address</help> - <completionHelp> - <script>vtysh -c "show bgp ipv6 summary" | awk '{print $1}' | grep -oE "\b([0-9a-f]{1,4}\:{0,2}){0,20}\b"</script> - </completionHelp> - </properties> - <command>vtysh -c "show bgp ipv6 neighbor $5"</command> - <children> - <leafNode name="advertised-routes"> - <properties> - <help>Show routes advertised to a BGP neighbor</help> - </properties> - <command>vtysh -c "show bgp ipv6 neighbor $5 advertised-routes"</command> - </leafNode> - <leafNode name="filtered-routes"> - <properties> - <help>Show routes filtered from a BGP neighbor</help> - </properties> - <command>vtysh -c "show bgp ipv6 neighbor $5 filtered-routes"</command> - </leafNode> - <leafNode name="dampened-routes"> - <properties> - <help>Show dampened routes received from BGP neighbor</help> - </properties> - <command>vtysh -c "show bgp ipv6 neighbor $5 dampened-routes"</command> - </leafNode> - <leafNode name="flap-statistics"> - <properties> - <help>Show flap statistics of the routes learned from BGP neighbor</help> - </properties> - <command>vtysh -c "show bgp ipv6 neighbor $5 flap-statistics"</command> - </leafNode> - <leafNode name="prefix-counts"> - <properties> - <help>Show detailed prefix count information for BGP neighbor</help> - </properties> - <command>vtysh -c "show bgp ipv6 neighbor $5 prefix-counts"</command> - </leafNode> - <node name="received"> - <properties> - <help>Show information received from BGP neighbor</help> - </properties> - <children> - <leafNode name="prefix-filter"> - <properties> - <help>Show prefixlist filter</help> - </properties> - <command>vtysh -c "show bgp ipv6 neighbor $5 received prefix-filter"</command> - </leafNode> - </children> - </node> - <leafNode name="received-routes"> - <properties> - <help>Show received routes from BGP neighbor</help> - </properties> - <command>vtysh -c "show bgp ipv6 neighbor $5 received-routes"</command> - </leafNode> - <leafNode name="routes"> - <properties> - <help>Show routes learned from BGP neighbor</help> - </properties> - <command>vtysh -c "show bgp ipv6 neighbor $5 routes"</command> - </leafNode> - </children> - </tagNode> - <tagNode name="large-community"> - <properties> - <help>Show routes matching the large-community-list number or name</help> - </properties> - <command>vtysh -c "show bgp ipv6 large-community-list $5"</command> - <children> - <node name="exact-match"> - <properties> - <help>Show routes matching the large-community-list number or name</help> - </properties> - <command>vtysh -c "show bgp ipv6 large-community-list $5 exact-match"</command> - </node> - </children> - </tagNode> - <tagNode name="large-community-list"> - <properties> - <help>Show routes matching the large-community-list number or name</help> - </properties> - <command>vtysh -c "show bgp ipv6 large-community-list $5"</command> - <children> - <node name="exact-match"> - <properties> - <help>Show routes matching the large-community-list number or name</help> - </properties> - <command>vtysh -c "show bgp ipv6 large-community-list $5 exact-match"</command> - </node> - </children> - </tagNode> - <tagNode name="filter-list"> - <properties> - <help>Show routes conforming to regular expression access list name</help> - </properties> - <command>vtysh -c "show bgp ipv6 filter-list $5"</command> - </tagNode> - <tagNode name="community"> - <properties> - <help>Show BGP information for specified community number</help> - <completionHelp> - <list><AA:NN> local-AS no-advertise no-export</list> - </completionHelp> - </properties> - <command>vtysh -c "show bgp ipv6 community $5"</command> - <children> - <node name="exact-match"> - <properties> - <help>Show routes from community that exactly matches the community number</help> - </properties> - <command>vtysh -c "show bgp ipv6 community $5 exact-match"</command> - </node> - </children> - </tagNode> - <tagNode name="community-list"> - <properties> - <help>Show routes matching the community-list number or name</help> - </properties> - <command>vtysh -c "show bgp ipv6 community-list $5"</command> - <children> - <node name="exact-match"> - <properties> - <help>Show routes exactly matching the community-list name or number</help> - </properties> - <command>vtysh -c "show bgp ipv6 community-list $5 exact-match"</command> - </node> - </children> - </tagNode> - <tagNode name="route-map"> - <properties> - <help>Show BGP routes matching the specified route map</help> - <completionHelp> - <path>policy route-map</path> - </completionHelp> - </properties> - <command>vtysh -c "show bgp ipv6 route-map $5"</command> - </tagNode> - </children> - </node> - <tagNode name="bgp"> - <properties> - <help>Show BGP information for specified IP address or prefix</help> - <completionHelp> - <list><h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> - </completionHelp> - </properties> - <command>vtysh -c "show bgp ipv6 $4"</command> - <children> - <node name="longer-prefixes"> - <properties> - <help>Show route and more specific routes</help> - <completionHelp> - <list><h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> - </completionHelp> - </properties> - <command>vtysh -c "show bgp ipv6 $4 longer-prefixes"</command> - </node> - </children> - </tagNode> - </children> - </node> - </children> - </node> -</interfaceDefinition> diff --git a/op-mode-definitions/show-ipv6-ospfv3.xml b/op-mode-definitions/show-ipv6-ospfv3.xml deleted file mode 100644 index 36bb5b40e..000000000 --- a/op-mode-definitions/show-ipv6-ospfv3.xml +++ /dev/null @@ -1,777 +0,0 @@ -<?xml version="1.0"?> -<interfaceDefinition> - <node name="show"> - <children> - <node name="ipv6"> - <properties> - <help>Show IPv6 routing information</help> - </properties> - <children> - <node name="ospfv3"> - <properties> - <help>Show IPv6 Open Shortest Path First (OSPF)</help> - </properties> - <command>vtysh -c "show ipv6 ospf6"</command> - <children> - <node name="area"> - <properties> - <help>Show Shortest Path First tree information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 spf tree"</command> - </node> - <tagNode name="area"> - <properties> - <help>Area ID (as an IPv4 notation)</help> - <completionHelp> - <path>protocols ospfv3 area</path> - </completionHelp> - </properties> - <command>vtysh -c "show ipv6 ospf6 area $4 spf tree"</command> - <children> - <tagNode name="router"> - <properties> - <help> Simulate view point (Router ID)</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>vtysh -c "show ipv6 ospf6 simulate spf-tree $7 $4 $5"</command> - </tagNode> - </children> - </tagNode> - <node name="border-routers"> - <properties> - <help>Show OSPFv3 border-router (ABR and ASBR) information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 border-routers"</command> - <children> - <node name="detail"> - <properties> - <help>Show OSPFv3 detailed border-router information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 border-routers detail"</command> - </node> - </children> - </node> - <tagNode name="border-routers"> - <properties> - <help>Border router ID</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>vtysh -c "show ipv6 ospf6 border-routers $5"</command> - </tagNode> - <node name="database"> - <properties> - <help>Show OSPFv3 Link state database information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database"</command> - <children> - <tagNode name="adv-router"> - <properties> - <help>Search by Advertising Router ID</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <children> - <tagNode name="linkstate-id"> - <properties> - <help>Search by Link state ID</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <children> - <node name="detail"> - <properties> - <help>Show details of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database adv-router $6 linkstate-id $8 detail"</command> - </node> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database adv-router $6 linkstate-id $8 dump"</command> - </node> - <node name="internal"> - <properties> - <help>Show LSAs internal information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database adv-router $6 linkstate-id $8 internal"</command> - </node> - </children> - </tagNode> - </children> - </tagNode> - <node name="any"> - <properties> - <help>Search by Any Link state Type</help> - </properties> - <children> - <tagNode name="any"> - <properties> - <help>Search by Link state ID</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <children> - <node name="detail"> - <properties> - <help>Show details of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database * * $7 detail"</command> - </node> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database * * $7 dump"</command> - </node> - <node name="internal"> - <properties> - <help>Show LSAs internal information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database * * $7 internal"</command> - </node> - </children> - </tagNode> - </children> - </node> - <tagNode name="any"> - <properties> - <help>Search by Link state ID</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>vtysh -c "show ipv6 ospf6 database * $6"</command> - <children> - <node name="detail"> - <properties> - <help>Show details of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database * $6 detail"</command> - </node> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database * $6 dump"</command> - </node> - <node name="internal"> - <properties> - <help>Show LSAs internal information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database * $6 internal"</command> - </node> - <node name="node.tag"> - <properties> - <help>Search by Advertising Router ID</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>vtysh -c "show ipv6 ospf6 database * $6 $7"</command> - <children> - <node name="detail"> - <properties> - <help>Show details of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database * $6 $7 detail"</command> - </node> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database * $6 $7 dump"</command> - </node> - <node name="internal"> - <properties> - <help>Show LSAs internal information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database * $6 $7 internal"</command> - </node> - </children> - </node> - </children> - </tagNode> - - - - - - <node name="as-external"> - <properties> - <help>Show AS-External LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external"</command> - <children> - <tagNode name="adv-router"> - <properties> - <help>Search by Advertising Router ID</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <children> - <tagNode name="linkstate-id"> - <properties> - <help>Search by Link state ID</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <children> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external adv-router $7 linkstate-id $9 dump"</command> - </node> - <node name="internal"> - <properties> - <help>Show LSAs internal information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external adv-router $7 linkstate-id $9 internal"</command> - </node> - </children> - </tagNode> - </children> - </tagNode> - <tagNode name="any"> - <properties> - <help>Search by Advertising Router ID</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external * $7"</command> - <children> - <node name="detail"> - <properties> - <help>Show details of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external * $7 detail"</command> - </node> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external * $7 dump"</command> - </node> - <node name="internal"> - <properties> - <help>Show LSAs internal information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external * $7 internal"</command> - </node> - </children> - </tagNode> - <node name="detail"> - <properties> - <help>Show details of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external detail"</command> - </node> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external dump"</command> - </node> - <node name="internal"> - <properties> - <help>Show LSAs internal information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external internal"</command> - </node> - <tagNode name="linkstate-id"> - <properties> - <help>Search by Link state ID</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <children> - <node name="detail"> - <properties> - <help>Show details of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external linkstate-id $7 detail"</command> - </node> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external linkstate-id $7 dump"</command> - </node> - <node name="internal"> - <properties> - <help>Show LSAs internal information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external linkstate-id $7 internal"</command> - </node> - </children> - </tagNode> - <node name="self-originated"> - <properties> - <help>Show Self-originated LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external self-originated"</command> - <children> - <node name="detail"> - <properties> - <help>Show details of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external self-originated detail"</command> - </node> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external self-originated dump"</command> - </node> - <node name="internal"> - <properties> - <help>Show LSAs internal information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external self-originated internal"</command> - </node> - <tagNode name="linkstate-id"> - <properties> - <help>Search by Link state ID</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <children> - <node name="detail"> - <properties> - <help>Show details of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external self-originated linkstate-id $8 detail"</command> - </node> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external self-originated linkstate-id $8 dump"</command> - </node> - <node name="internal"> - <properties> - <help>Show LSAs internal information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external self-originated linkstate-id $8 internal"</command> - </node> - </children> - </tagNode> - </children> - </node> - </children> - </node> - <tagNode name="as-external"> - <properties> - <help>Search by Advertising Router IDs</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <children> - <node name="detail"> - <properties> - <help>Show details of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external $6 detail"</command> - </node> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external $6 dump"</command> - </node> - <node name="internal"> - <properties> - <help>Show LSAs internal information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external $6 internal"</command> - </node> - <node name="self-originated"> - <properties> - <help>Show Self-originated LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external $6 self-originated"</command> - <children> - <node name="detail"> - <properties> - <help>Show details of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external $6 self-originated detail"</command> - </node> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external $6 self-originated dump"</command> - </node> - <node name="internal"> - <properties> - <help>Show LSAs internal information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database as-external $6 self-originated internal"</command> - </node> - </children> - </node> - <node name="node.tag"> - <properties> - <help>Search by Advertising Router ID</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <children> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>echo vtysh -c "show ipv6 ospf6 database as-external $6 $7 dump"</command> - </node> - <node name="internal"> - <properties> - <help>Show LSAs internal information</help> - </properties> - <command>echo vtysh -c "show ipv6 ospf6 database as-external $6 $7 internal"</command> - </node> - </children> - </node> - </children> - </tagNode> - <node name="detail"> - <properties> - <help>Show details of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database detail"</command> - </node> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database dump"</command> - </node> - <tagNode name="linkstate-id"> - <properties> - <help>Search by Link state ID</help> - <completionHelp> - <list><x.x.x.x></list> - </completionHelp> - </properties> - <children> - <node name="detail"> - <properties> - <help>Show details of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database linkstate-id $6 detail"</command> - </node> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database linkstate-id $6 dump"</command> - </node> - <node name="internal"> - <properties> - <help>Show LSAs internal information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database linkstate-id $6 internal"</command> - </node> - </children> - </tagNode> - <node name="self-originated"> - <properties> - <help>Show Self-originated LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database self-originated"</command> - <children> - <node name="detail"> - <properties> - <help>Show details of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database self-originated detail"</command> - </node> - <node name="dump"> - <properties> - <help>Show dump of LSAs</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database self-originated dump"</command> - </node> - <node name="internal"> - <properties> - <help>Show LSAs internal information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 database self-originated internal"</command> - </node> - </children> - </node> - </children> - </node> - <node name="interface"> - <properties> - <help>Show OSPFv3 interface information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 interface"</command> - <children> - <node name="prefix"> - <properties> - <help>Show connected prefixes to advertise</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 interface prefix"</command> - <children> - <node name="detail"> - <properties> - <help>More detailed interface prefix information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 interface prefix detail"</command> - </node> - </children> - </node> - <tagNode name="prefix"> - <properties> - <help>Show interface prefix route specific information</help> - <completionHelp> - <list><h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> - </completionHelp> - </properties> - <command>vtysh -c "show ipv6 ospf6 interface prefix $6"</command> - <children> - <node name="detail"> - <properties> - <help>More detailed information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 interface prefix $6 detail"</command> - </node> - <node name="match"> - <properties> - <help>Matched interface prefix information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 interface prefix $6 match"</command> - </node> - </children> - </tagNode> - </children> - </node> - <tagNode name="interface"> - <properties> - <help>Specific insterface to examine</help> - <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py</script> - </completionHelp> - </properties> - <command>vtysh -c "show ipv6 ospf6 interface $5"</command> - <children> - <node name="prefix"> - <properties> - <help>Show connected prefixes to advertise</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 interface $5 prefix"</command> - <children> - <node name="detail"> - <properties> - <help>More detailed interface prefix information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 interface $5 prefix detail"</command> - </node> - </children> - </node> - <tagNode name="prefix"> - <properties> - <help>Show interface prefix route specific information</help> - <completionHelp> - <list><h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> - </completionHelp> - </properties> - <command>vtysh -c "show ipv6 ospf6 interface $5 prefix $7"</command> - <children> - <node name="detail"> - <properties> - <help>More detailed information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 interface $5 prefix $7 detail"</command> - </node> - <node name="match"> - <properties> - <help>Matched interface prefix information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 interface $5 prefix $7 match"</command> - </node> - </children> - </tagNode> - </children> - </tagNode> - <node name="linkstate"> - <properties> - <help>Show OSPFv3 linkstate routing information</help> - </properties> - <children> - <node name="detail"> - <properties> - <help>Show detailed linkstate information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 linkstate detail"</command> - </node> - <node name="network"> - <properties> - <help>Show linkstate Network information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 linkstate network"</command> - </node> - <node name="router"> - <properties> - <help>Show linkstate Router information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 linkstate router"</command> - </node> - </children> - </node> - <node name="neighbor"> - <properties> - <help>Show OSPFv3 neighbor information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 neighbor"</command> - <children> - <node name="detail"> - <properties> - <help>Show detailed neighbor information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 neighbor detail"</command> - </node> - <node name="drchoice"> - <properties> - <help>Show neighbor DR choice information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 neighbor drchoice"</command> - </node> - </children> - </node> - <node name="redistribute"> - <properties> - <help>Show OSPFv3 redistribute external information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 redistribute"</command> - </node> - <node name="route"> - <properties> - <help>Show OSPFv3 routing table information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 route"</command> - <children> - <node name="external-1"> - <properties> - <help>Show Type-1 External route information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 route external-1"</command> - <children> - <node name="detail"> - <properties> - <help>Show detailed Type-1 External route information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 route external-1 detail"</command> - </node> - </children> - </node> - <node name="external-2"> - <properties> - <help>Show Type-2 External route information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 route external-2"</command> - <children> - <node name="detail"> - <properties> - <help>Show detailed Type-2 External route information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 route external-2 detail"</command> - </node> - </children> - </node> - <node name="inter-area"> - <properties> - <help>Show Inter-Area route information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 route inter-area"</command> - <children> - <node name="detail"> - <properties> - <help>Show detailed Inter-Area route information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 route inter-area detail"</command> - </node> - </children> - </node> - <node name="intra-area"> - <properties> - <help>Show Intra-Area route information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 route intra-area"</command> - <children> - <node name="detail"> - <properties> - <help>Show detailed Intra-Area route information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 route intra-area detail"</command> - </node> - </children> - </node> - <node name="detail"> - <properties> - <help>Show detailed route information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 route detail"</command> - </node> - <node name="summary"> - <properties> - <help>Show route table summary</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 route summary"</command> - </node> - </children> - </node> - <tagNode name="route"> - <properties> - <help>Show specified route/prefix information</help> - <completionHelp> - <list><h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> - </completionHelp> - </properties> - <command>vtysh -c "show ipv6 ospf6 route $5"</command> - <children> - <node name="longer"> - <properties> - <help>Show routes longer than specified prefix</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 route $5 longer"</command> - </node> - <node name="match"> - <properties> - <help>Show routes matching specified prefix</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 route $5 match"</command> - <children> - <node name="detail"> - <properties> - <help>Detailed information</help> - </properties> - <command>vtysh -c "show ipv6 ospf6 route $5 match detail"</command> - </node> - </children> - </node> - </children> - </tagNode> - </children> - </node> - </children> - </node> - </children> - </node> -</interfaceDefinition> diff --git a/op-mode-definitions/show-ipv6-ospfv3.xml.in b/op-mode-definitions/show-ipv6-ospfv3.xml.in new file mode 100644 index 000000000..9227fdae1 --- /dev/null +++ b/op-mode-definitions/show-ipv6-ospfv3.xml.in @@ -0,0 +1,502 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="show"> + <children> + <node name="ipv6"> + <properties> + <help>Show IPv6 routing information</help> + </properties> + <children> + <node name="ospfv3"> + <properties> + <help>Show IPv6 Open Shortest Path First (OSPF)</help> + </properties> + <command>vtysh -c "show ipv6 ospf6"</command> + <children> + <node name="area"> + <properties> + <help>Show Shortest Path First tree information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 spf tree"</command> + </node> + <tagNode name="area"> + <properties> + <help>Area ID (as an IPv4 notation)</help> + <completionHelp> + <path>protocols ospfv3 area</path> + </completionHelp> + </properties> + <command>vtysh -c "show ipv6 ospf6 area $4 spf tree"</command> + <children> + <tagNode name="router"> + <properties> + <help> Simulate view point (Router ID)</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>vtysh -c "show ipv6 ospf6 simulate spf-tree $7 $4 $5"</command> + </tagNode> + </children> + </tagNode> + <node name="border-routers"> + <properties> + <help>Show OSPFv3 border-router (ABR and ASBR) information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 border-routers"</command> + <children> + #include <include/ospfv3-detail.xml.i> + </children> + </node> + <tagNode name="border-routers"> + <properties> + <help>Border router ID</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>vtysh -c "show ipv6 ospf6 border-routers $5"</command> + </tagNode> + <node name="database"> + <properties> + <help>Show OSPFv3 Link state database information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 database"</command> + <children> + <tagNode name="adv-router"> + <properties> + <help>Search by Advertising Router ID</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <children> + #include <include/ospfv3-linkstate-id.xml.i> + </children> + </tagNode> + <node name="any"> + <properties> + <help>Search by Any Link state Type</help> + </properties> + <children> + <tagNode name="any"> + <properties> + <help>Search by Link state ID</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <children> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + </children> + </tagNode> + </children> + </node> + <tagNode name="any"> + <properties> + <help>Search by Link state ID</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>vtysh -c "show ipv6 ospf6 database * $6"</command> + <children> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + #include <include/ospfv3-adv-router-id-node-tag.xml.i> + </children> + </tagNode> + <node name="as-external"> + <properties> + <help>Show AS-External LSAs</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 database as-external"</command> + <children> + #include <include/ospfv3-adv-router.xml.i> + <tagNode name="any"> + <properties> + <help>Search by Advertising Router ID</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>vtysh -c "show ipv6 ospf6 database as-external * $7"</command> + <children> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + </children> + </tagNode> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + #include <include/ospfv3-linkstate-id.xml.i> + #include <include/ospfv3-self-originated.xml.i> + </children> + </node> + <tagNode name="as-external"> + <properties> + <help>Search by Advertising Router IDs</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <children> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + #include <include/ospfv3-self-originated.xml.i> + #include <include/ospfv3-adv-router-id-node-tag.xml.i> + </children> + </tagNode> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-internal.xml.i> + #include <include/ospfv3-linkstate-id.xml.i> + #include <include/ospfv3-self-originated.xml.i> + <node name="group-membership"> + <properties> + <help>Show Group-Membership LSAs</help> + </properties> + <!-- FRR uses ospf6 where we use ospfv3, thus alter the command --> + <command>vtysh -c "show ipv6 ospf6 ${@:4}"</command> + <children> + #include <include/ospfv3-adv-router.xml.i> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + #include <include/ospfv3-linkstate-id.xml.i> + #include <include/ospfv3-linkstate-id-node-tag.xml.i> + #include <include/ospfv3-self-originated.xml.i> + </children> + </node> + <node name="inter-prefix"> + <properties> + <help>Show Inter-Area-Prefix LSAs</help> + </properties> + <!-- FRR uses ospf6 where we use ospfv3, thus alter the command --> + <command>vtysh -c "show ipv6 ospf6 ${@:4}"</command> + <children> + #include <include/ospfv3-adv-router.xml.i> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + #include <include/ospfv3-linkstate-id.xml.i> + #include <include/ospfv3-linkstate-id-node-tag.xml.i> + #include <include/ospfv3-self-originated.xml.i> + </children> + </node> + <node name="inter-router"> + <properties> + <help>Show Inter-Area-Router LSAs</help> + </properties> + <!-- FRR uses ospf6 where we use ospfv3, thus alter the command --> + <command>vtysh -c "show ipv6 ospf6 ${@:4}"</command> + <children> + #include <include/ospfv3-adv-router.xml.i> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + #include <include/ospfv3-linkstate-id.xml.i> + #include <include/ospfv3-linkstate-id-node-tag.xml.i> + #include <include/ospfv3-self-originated.xml.i> + </children> + </node> + <node name="intra-prefix"> + <properties> + <help>Show Intra-Area-Prefix LSAs</help> + </properties> + <!-- FRR uses ospf6 where we use ospfv3, thus alter the command --> + <command>vtysh -c "show ipv6 ospf6 ${@:4}"</command> + <children> + #include <include/ospfv3-adv-router.xml.i> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + #include <include/ospfv3-linkstate-id.xml.i> + #include <include/ospfv3-linkstate-id-node-tag.xml.i> + #include <include/ospfv3-self-originated.xml.i> + </children> + </node> + <node name="link"> + <properties> + <help>Show Link LSAs</help> + </properties> + <!-- FRR uses ospf6 where we use ospfv3, thus alter the command --> + <command>vtysh -c "show ipv6 ospf6 ${@:4}"</command> + <children> + #include <include/ospfv3-adv-router.xml.i> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + #include <include/ospfv3-linkstate-id.xml.i> + #include <include/ospfv3-linkstate-id-node-tag.xml.i> + #include <include/ospfv3-self-originated.xml.i> + </children> + </node> + <node name="network"> + <properties> + <help>Show Network LSAs</help> + </properties> + <!-- FRR uses ospf6 where we use ospfv3, thus alter the command --> + <command>vtysh -c "show ipv6 ospf6 ${@:4}"</command> + <children> + #include <include/ospfv3-adv-router.xml.i> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + #include <include/ospfv3-linkstate-id.xml.i> + #include <include/ospfv3-linkstate-id-node-tag.xml.i> + #include <include/ospfv3-self-originated.xml.i> + </children> + </node> + <node name="node.tag"> + <properties> + <help>Show LSAs</help> + </properties> + <!-- FRR uses ospf6 where we use ospfv3, thus alter the command --> + <command>vtysh -c "show ipv6 ospf6 ${@:4}"</command> + <children> + #include <include/ospfv3-adv-router.xml.i> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + #include <include/ospfv3-linkstate-id.xml.i> + #include <include/ospfv3-linkstate-id-node-tag.xml.i> + #include <include/ospfv3-self-originated.xml.i> + </children> + </node> + <node name="router"> + <properties> + <help>Show router LSAs</help> + </properties> + <!-- FRR uses ospf6 where we use ospfv3, thus alter the command --> + <command>vtysh -c "show ipv6 ospf6 ${@:4}"</command> + <children> + #include <include/ospfv3-adv-router.xml.i> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + #include <include/ospfv3-linkstate-id.xml.i> + #include <include/ospfv3-linkstate-id-node-tag.xml.i> + #include <include/ospfv3-self-originated.xml.i> + </children> + </node> + <node name="type-7"> + <properties> + <help>Show Type-7 LSAs</help> + </properties> + <!-- FRR uses ospf6 where we use ospfv3, thus alter the command --> + <command>vtysh -c "show ipv6 ospf6 ${@:4}"</command> + <children> + #include <include/ospfv3-adv-router.xml.i> + #include <include/ospfv3-detail.xml.i> + #include <include/ospfv3-dump.xml.i> + #include <include/ospfv3-internal.xml.i> + #include <include/ospfv3-linkstate-id.xml.i> + #include <include/ospfv3-linkstate-id-node-tag.xml.i> + #include <include/ospfv3-self-originated.xml.i> + </children> + </node> + </children> + </node> + <node name="interface"> + <properties> + <help>Show OSPFv3 interface information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 interface"</command> + <children> + <node name="prefix"> + <properties> + <help>Show connected prefixes to advertise</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 interface prefix"</command> + <children> + #include <include/ospfv3-detail.xml.i> + </children> + </node> + <tagNode name="prefix"> + <properties> + <help>Show interface prefix route specific information</help> + <completionHelp> + <list><h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> + </completionHelp> + </properties> + <command>vtysh -c "show ipv6 ospf6 interface prefix $6"</command> + <children> + #include <include/ospfv3-detail.xml.i> + <node name="match"> + <properties> + <help>Matched interface prefix information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 interface prefix $6 match"</command> + </node> + </children> + </tagNode> + </children> + </node> + <tagNode name="interface"> + <properties> + <help>Specific insterface to examine</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + </properties> + <command>vtysh -c "show ipv6 ospf6 interface $5"</command> + <children> + <node name="prefix"> + <properties> + <help>Show connected prefixes to advertise</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 interface $5 prefix"</command> + <children> + #include <include/ospfv3-detail.xml.i> + </children> + </node> + <tagNode name="prefix"> + <properties> + <help>Show interface prefix route specific information</help> + <completionHelp> + <list><h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> + </completionHelp> + </properties> + <command>vtysh -c "show ipv6 ospf6 interface $5 prefix $7"</command> + <children> + #include <include/ospfv3-detail.xml.i> + <node name="match"> + <properties> + <help>Matched interface prefix information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 interface $5 prefix $7 match"</command> + </node> + </children> + </tagNode> + </children> + </tagNode> + <node name="linkstate"> + <properties> + <help>Show OSPFv3 linkstate routing information</help> + </properties> + <children> + #include <include/ospfv3-detail.xml.i> + <node name="network"> + <properties> + <help>Show linkstate Network information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 linkstate network"</command> + </node> + <node name="router"> + <properties> + <help>Show linkstate Router information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 linkstate router"</command> + </node> + </children> + </node> + <node name="neighbor"> + <properties> + <help>Show OSPFv3 neighbor information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 neighbor"</command> + <children> + #include <include/ospfv3-detail.xml.i> + <node name="drchoice"> + <properties> + <help>Show neighbor DR choice information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 neighbor drchoice"</command> + </node> + </children> + </node> + <node name="redistribute"> + <properties> + <help>Show OSPFv3 redistribute external information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 redistribute"</command> + </node> + <node name="route"> + <properties> + <help>Show OSPFv3 routing table information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 route"</command> + <children> + <node name="external-1"> + <properties> + <help>Show Type-1 External route information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 route external-1"</command> + <children> + #include <include/ospfv3-detail.xml.i> + </children> + </node> + <node name="external-2"> + <properties> + <help>Show Type-2 External route information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 route external-2"</command> + <children> + #include <include/ospfv3-detail.xml.i> + </children> + </node> + <node name="inter-area"> + <properties> + <help>Show Inter-Area route information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 route inter-area"</command> + <children> + #include <include/ospfv3-detail.xml.i> + </children> + </node> + <node name="intra-area"> + <properties> + <help>Show Intra-Area route information</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 route intra-area"</command> + <children> + #include <include/ospfv3-detail.xml.i> + </children> + </node> + #include <include/ospfv3-detail.xml.i> + <node name="summary"> + <properties> + <help>Show route table summary</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 route summary"</command> + </node> + </children> + </node> + <tagNode name="route"> + <properties> + <help>Show specified route/prefix information</help> + <completionHelp> + <list><h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> + </completionHelp> + </properties> + <command>vtysh -c "show ipv6 ospf6 route $5"</command> + <children> + <node name="longer"> + <properties> + <help>Show routes longer than specified prefix</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 route $5 longer"</command> + </node> + <node name="match"> + <properties> + <help>Show routes matching specified prefix</help> + </properties> + <command>vtysh -c "show ipv6 ospf6 route $5 match"</command> + <children> + #include <include/ospfv3-detail.xml.i> + </children> + </node> + </children> + </tagNode> + </children> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/show-ipv6-prefix-list.xml b/op-mode-definitions/show-ipv6-prefix-list.xml.in index e003ad110..e003ad110 100644 --- a/op-mode-definitions/show-ipv6-prefix-list.xml +++ b/op-mode-definitions/show-ipv6-prefix-list.xml.in diff --git a/op-mode-definitions/show-ipv6-route.xml b/op-mode-definitions/show-ipv6-route.xml.in index fafd615ea..065ea6f1f 100644 --- a/op-mode-definitions/show-ipv6-route.xml +++ b/op-mode-definitions/show-ipv6-route.xml.in @@ -7,23 +7,6 @@ <help>Show IPv6 routing information</help> </properties> <children> - <tagNode name="route"> - <properties> - <help>Show IPv6 routes of given address or prefix</help> - <completionHelp> - <list><h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> - </completionHelp> - </properties> - <children> - <node name="longer-prefixes"> - <properties> - <help>Show longer prefixes of routes for given address or prefix</help> - </properties> - <command>vtysh -c "show ipv6 route $4 longer-prefixes"</command> - </node> - </children> - <command>vtysh -c "show ipv6 route $4"</command> - </tagNode> <node name="route"> <properties> <help>Show IPv6 routes</help> @@ -36,12 +19,42 @@ </properties> <command>vtysh -c "show ipv6 route bgp"</command> </node> + <node name="cache"> + <properties> + <help>Show kernel IPv6 route cache</help> + </properties> + <command>ip -s -f inet6 route list cache</command> + </node> + <tagNode name="cache"> + <properties> + <help>Show kernel IPv6 route cache for a given route</help> + <completionHelp> + <list><h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> + </completionHelp> + </properties> + <command>ip -s -f inet6 route list cache $5</command> + </tagNode> <node name="connected"> <properties> <help>Show IPv6 connected routes</help> </properties> <command>vtysh -c "show ipv6 route connected"</command> </node> + <node name="forward"> + <properties> + <help>Show kernel IPv6 route table</help> + </properties> + <command>ip -f inet6 route list</command> + </node> + <tagNode name="forward"> + <properties> + <help>Show kernel IPv6 route table for a given route</help> + <completionHelp> + <list><h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> + </completionHelp> + </properties> + <command>ip -s -f inet6 route list $5</command> + </tagNode> <node name="isis"> <properties> <help>Show IPv6 IS-IS routes</help> @@ -110,6 +123,23 @@ </tagNode> </children> </node> + <tagNode name="route"> + <properties> + <help>Show IPv6 routes of given address or prefix</help> + <completionHelp> + <list><h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> + </completionHelp> + </properties> + <children> + <node name="longer-prefixes"> + <properties> + <help>Show longer prefixes of routes for given address or prefix</help> + </properties> + <command>vtysh -c "show ipv6 route $4 longer-prefixes"</command> + </node> + </children> + <command>vtysh -c "show ipv6 route $4"</command> + </tagNode> </children> </node> </children> diff --git a/op-mode-definitions/show-ipv6.xml b/op-mode-definitions/show-ipv6.xml.in index a59c8df0c..a59c8df0c 100644 --- a/op-mode-definitions/show-ipv6.xml +++ b/op-mode-definitions/show-ipv6.xml.in diff --git a/op-mode-definitions/show-isis.xml b/op-mode-definitions/show-isis.xml deleted file mode 100644 index 4e308730f..000000000 --- a/op-mode-definitions/show-isis.xml +++ /dev/null @@ -1,191 +0,0 @@ -<?xml version="1.0"?> -<interfaceDefinition> - <node name="show"> - <children> - <node name="isis"> - <properties> - <help>IS-IS routing protocol</help> - </properties> - <children> - <node name="database"> - <properties> - <help>Show IS-IS link state database</help> - </properties> - <children> - <leafNode name="detail"> - <properties> - <help>Show detailed information</help> - </properties> - <command>/usr/bin/vtysh -c "show isis database detail"</command> - </leafNode> - </children> - <command>/usr/bin/vtysh -c "show isis database"</command> - </node> - <tagNode name="database"> - <properties> - <help>Show IS-IS link state database PDU</help> - <completionHelp> - <list>lsp-id detail</list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show isis database $4"</command> - </tagNode> - <leafNode name="hostname"> - <properties> - <help>Show IS-IS dynamic hostname mapping</help> - </properties> - <command>/usr/bin/vtysh -c "show isis hostname"</command> - </leafNode> - <node name="interface"> - <properties> - <help>Show IS-IS interfaces</help> - <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py</script> - </completionHelp> - </properties> - <children> - <leafNode name="detail"> - <properties> - <help>Show detailed information</help> - </properties> - <command>/usr/bin/vtysh -c "show isis interface detail"</command> - </leafNode> - </children> - <command>/usr/bin/vtysh -c "show isis interface"</command> - </node> - <tagNode name="interface"> - <properties> - <help>Show specific IS-IS interface</help> - <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py</script> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show isis interface $4"</command> - </tagNode> - <node name="traffic-engineering"> - <properties> - <help>Show IS-IS MPLS traffic engineering information</help> - </properties> - <children> - <leafNode name="router"> - <properties> - <help>Show router information</help> - </properties> - <command>/usr/bin/vtysh -c "show isis mpls-te router"</command> - </leafNode> - <leafNode name="interface"> - <properties> - <help>Show interface information</help> - </properties> - <command>/usr/bin/vtysh -c "show isis mpls-te interface"</command> - </leafNode> - <tagNode name="interface"> - <properties> - <help>Show specific IS-IS interface</help> - <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py</script> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show isis mpls-te interface $5"</command> - </tagNode> - </children> - </node> - <node name="neighbor"> - <properties> - <help>Show IS-IS neighbor adjacencies</help> - </properties> - <children> - <leafNode name="detail"> - <properties> - <help>Show detailed information</help> - </properties> - <command>/usr/bin/vtysh -c "show isis neighbor detail"</command> - </leafNode> - </children> - <command>/usr/bin/vtysh -c "show isis neighbor"</command> - </node> - <tagNode name="neighbor"> - <properties> - <help>Show specific IS-IS neighbor adjacency </help> - <completionHelp> - <list>system-id</list> - </completionHelp> - </properties> - <command>/usr/bin/vtysh -c "show isis neighbor $4"</command> - </tagNode> - <node name="route"> - <properties> - <help>Show IS-IS routing table</help> - </properties> - <children> - <leafNode name="level-1"> - <properties> - <help>Show level-1 routes</help> - </properties> - <command>/usr/bin/vtysh -c "show isis route level-1"</command> - </leafNode> - <leafNode name="level-2"> - <properties> - <help>Show level-2 routes</help> - </properties> - <command>/usr/bin/vtysh -c "show isis route level-2"</command> - </leafNode> - </children> - <command>/usr/bin/vtysh -c "show isis route"</command> - </node> - <node name="segment-routing"> - <properties> - <help>Show IS-IS Segment-Routing (SPRING) information</help> - </properties> - <children> - <leafNode name="node"> - <properties> - <help>Show node information</help> - </properties> - <command>/usr/bin/vtysh -c "show isis segment-routing node"</command> - </leafNode> - <leafNode name="prefix-sids"> - <properties> - <help>Show prefix segment IDs</help> - </properties> - <command>/usr/bin/vtysh -c "show isis segment-routing prefix-sids"</command> - </leafNode> - </children> - </node> - <leafNode name="spf-delay"> - <properties> - <help>Show IS-IS SPF delay parameters</help> - </properties> - <command>/usr/bin/vtysh -c "show isis spf-delay-ietf"</command> - </leafNode> - <leafNode name="summary"> - <properties> - <help>Show IS-IS information summary</help> - </properties> - <command>/usr/bin/vtysh -c "show isis summary"</command> - </leafNode> - <node name="topology"> - <properties> - <help>Show IS-IS paths to Intermediate Systems</help> - </properties> - <children> - <leafNode name="level-1"> - <properties> - <help>Show level-1 routes</help> - </properties> - <command>/usr/bin/vtysh -c "show isis topology level-1"</command> - </leafNode> - <leafNode name="level-2"> - <properties> - <help>Show level-2 routes</help> - </properties> - <command>/usr/bin/vtysh -c "show isis topology level-2"</command> - </leafNode> - </children> - <command>/usr/bin/vtysh -c "show isis topology"</command> - </node> - </children> - </node> - </children> - </node> -</interfaceDefinition> diff --git a/op-mode-definitions/show-isis.xml.in b/op-mode-definitions/show-isis.xml.in new file mode 100644 index 000000000..202e3214b --- /dev/null +++ b/op-mode-definitions/show-isis.xml.in @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="show"> + <children> + <node name="isis"> + <properties> + <help>Show IS-IS routing protocol</help> + </properties> + <children> + #include <include/isis-common.xml.i> + <tagNode name="vrf"> + <properties> + <help>Show IS-IS routing protocol for given VRF</help> + <completionHelp> + <path>vrf name</path> + <list>all</list> + </completionHelp> + </properties> + <children> + #include <include/isis-common.xml.i> + </children> + </tagNode> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/show-license.xml b/op-mode-definitions/show-license.xml.in index 2ce11567d..2ce11567d 100644 --- a/op-mode-definitions/show-license.xml +++ b/op-mode-definitions/show-license.xml.in diff --git a/op-mode-definitions/show-log.xml b/op-mode-definitions/show-log.xml.in index b00e4cfec..58216bfd1 100644 --- a/op-mode-definitions/show-log.xml +++ b/op-mode-definitions/show-log.xml.in @@ -12,7 +12,7 @@ <properties> <help>Show contents of all master log files</help> </properties> - <command>eval $(lesspipe); less $_vyatta_less_options --prompt=".log?m, file %i of %m., page %dt of %D" -- `printf "%s\n" /var/log/messages* | sort -nr`</command> + <command>sudo bash -c 'eval $(lesspipe); less $_vyatta_less_options --prompt=".logm, file %i of %m., page %dt of %D" -- `printf "%s\n" /var/log/messages* | sort -nr`'</command> </leafNode> <leafNode name="authorization"> <properties> diff --git a/op-mode-definitions/show-login.xml b/op-mode-definitions/show-login.xml.in index 6d8c782c4..6d8c782c4 100644 --- a/op-mode-definitions/show-login.xml +++ b/op-mode-definitions/show-login.xml.in diff --git a/op-mode-definitions/show-monitoring.xml b/op-mode-definitions/show-monitoring.xml.in index 2651b3438..2651b3438 100644 --- a/op-mode-definitions/show-monitoring.xml +++ b/op-mode-definitions/show-monitoring.xml.in diff --git a/op-mode-definitions/show-mpls.xml b/op-mode-definitions/show-mpls.xml.in index 833ac98eb..86f6f1bcc 100644 --- a/op-mode-definitions/show-mpls.xml +++ b/op-mode-definitions/show-mpls.xml.in @@ -16,41 +16,41 @@ <properties> <help>Label Information Base</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding"</command> + <command>vtysh -c "show mpls ldp binding"</command> <children> <node name="detail"> <properties> <help>Show detailed information</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding detail"</command> + <command>vtysh -c "show mpls ldp binding detail"</command> </node> <tagNode name="neighbor"> <properties> <help>Display labels from LDP neighbor</help> <completionHelp> <list><x.x.x.x> <h:h:h:h:h:h:h:h></list> - <script>/usr/bin/vtysh -c "show mpls ldp neighbor" | awk '{print $2}' | egrep -v "ID"</script> + <script>vtysh -c "show mpls ldp neighbor" | awk '{print $2}' | egrep -v "ID"</script> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding neighbor $6"</command> + <command>vtysh -c "show mpls ldp binding neighbor $6"</command> <children> <leafNode name="detail"> <properties> <help>Show detailed information</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding neighbor $6 detail"</command> + <command>vtysh -c "show mpls ldp binding neighbor $6 detail"</command> </leafNode> <tagNode name="local-label"> <properties> <help>Match locally assigned label value</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding neighbor $6 local-label $8"</command> + <command>vtysh -c "show mpls ldp binding neighbor $6 local-label $8"</command> </tagNode> <tagNode name="remote-label"> <properties> <help>Match remotely assigned label value</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding neighbor $6 remote-label $8"</command> + <command>vtysh -c "show mpls ldp binding neighbor $6 remote-label $8"</command> </tagNode> </children> </tagNode> @@ -58,29 +58,29 @@ <properties> <help>Match locally assigned label value</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding local-label $6"</command> + <command>vtysh -c "show mpls ldp binding local-label $6"</command> <children> <leafNode name="detail"> <properties> <help>Show detailed information</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding local-label $6 detail"</command> + <command>vtysh -c "show mpls ldp binding local-label $6 detail"</command> </leafNode> <tagNode name="neighbor"> <properties> <help>Match LDP neighbor</help> <completionHelp> <list><x.x.x.x> <h:h:h:h:h:h:h:h></list> - <script>/usr/bin/vtysh -c "show mpls ldp neighbor" | awk '{print $2}' | egrep -v "ID"</script> + <script>vtysh -c "show mpls ldp neighbor" | awk '{print $2}' | egrep -v "ID"</script> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding local-label $6 neighbor $8"</command> + <command>vtysh -c "show mpls ldp binding local-label $6 neighbor $8"</command> </tagNode> <tagNode name="remote-label"> <properties> <help>Match remotely assigned label value</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding local-label $6 remote-label $8"</command> + <command>vtysh -c "show mpls ldp binding local-label $6 remote-label $8"</command> </tagNode> </children> </tagNode> @@ -88,29 +88,29 @@ <properties> <help>Match remotely assigned label value</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding remote-label $6"</command> + <command>vtysh -c "show mpls ldp binding remote-label $6"</command> <children> <leafNode name="detail"> <properties> <help>Show detailed information</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding remote-label $6 detail"</command> + <command>vtysh -c "show mpls ldp binding remote-label $6 detail"</command> </leafNode> <tagNode name="neighbor"> <properties> <help>Match LDP neighbor</help> <completionHelp> <list><x.x.x.x> <h:h:h:h:h:h:h:h></list> - <script>/usr/bin/vtysh -c "show mpls ldp neighbor" | awk '{print $2}' | egrep -v "ID"</script> + <script>vtysh -c "show mpls ldp neighbor" | awk '{print $2}' | egrep -v "ID"</script> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding remote-label $6 neighbor $8"</command> + <command>vtysh -c "show mpls ldp binding remote-label $6 neighbor $8"</command> </tagNode> <tagNode name="local-label"> <properties> <help>Match locally assigned label value</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding remote-label $6 local-label $8"</command> + <command>vtysh -c "show mpls ldp binding remote-label $6 local-label $8"</command> </tagNode> </children> </tagNode> @@ -123,13 +123,13 @@ <list><x.x.x.x/x> <h:h:h:h:h:h:h:h/h></list> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding $5"</command> + <command>vtysh -c "show mpls ldp binding $5"</command> <children> <leafNode name="detail"> <properties> <help>Show detailed information</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp binding $5 detail"</command> + <command>vtysh -c "show mpls ldp binding $5 detail"</command> </leafNode> </children> </tagNode> @@ -137,13 +137,13 @@ <properties> <help>Discovery hello information</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp discovery"</command> + <command>vtysh -c "show mpls ldp discovery"</command> <children> <leafNode name="detail"> <properties> <help>Show detailed information</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp discovery detail"</command> + <command>vtysh -c "show mpls ldp discovery detail"</command> </leafNode> </children> </node> @@ -151,25 +151,25 @@ <properties> <help>LDP interface information</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp interface"</command> + <command>vtysh -c "show mpls ldp interface"</command> </node> <node name="neighbor"> <properties> <help>LDP neighbor information</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp neighbor"</command> + <command>vtysh -c "show mpls ldp neighbor"</command> <children> <leafNode name="detail"> <properties> <help>Show detailed information</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp neighbor detail"</command> + <command>vtysh -c "show mpls ldp neighbor detail"</command> </leafNode> <leafNode name="capabilities"> <properties> <help>Show neighbor capability information</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp neighbor capabilities"</command> + <command>vtysh -c "show mpls ldp neighbor capabilities"</command> </leafNode> </children> </node> @@ -178,22 +178,22 @@ <help>LDP neighbor</help> <completionHelp> <list><x.x.x.x> <h:h:h:h:h:h:h:h></list> - <script>/usr/bin/vtysh -c "show mpls ldp neighbor" | awk '{print $2}' | egrep -v "ID"</script> + <script>vtysh -c "show mpls ldp neighbor" | awk '{print $2}' | egrep -v "ID"</script> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp neighbor $5"</command> + <command>vtysh -c "show mpls ldp neighbor $5"</command> <children> <leafNode name="detail"> <properties> <help>Show detailed information</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp neighbor $5 detail"</command> + <command>vtysh -c "show mpls ldp neighbor $5 detail"</command> </leafNode> <leafNode name="capabilities"> <properties> <help>Show neighbor capability information</help> </properties> - <command>/usr/bin/vtysh -c "show mpls ldp neighbor $5 capabilities"</command> + <command>vtysh -c "show mpls ldp neighbor $5 capabilities"</command> </leafNode> </children> </tagNode> @@ -203,13 +203,13 @@ <properties> <help>Show MPLS pseudowire interfaces</help> </properties> - <command>/usr/bin/vtysh -c "show mpls pseudowires"</command> + <command>vtysh -c "show mpls pseudowires"</command> </node> <node name="table"> <properties> <help>Show MPLS table</help> </properties> - <command>/usr/bin/vtysh -c "show mpls table"</command> + <command>vtysh -c "show mpls table"</command> </node> </children> </node> diff --git a/op-mode-definitions/show-ntp.xml b/op-mode-definitions/show-ntp.xml.in index b7f0acdf8..01f4477d8 100644 --- a/op-mode-definitions/show-ntp.xml +++ b/op-mode-definitions/show-ntp.xml.in @@ -6,7 +6,7 @@ <properties> <help>Show peer status of NTP daemon</help> </properties> - <command>if ps -C ntpd &>/dev/null; then ntpq -n -c peers; else echo NTP daemon disabled; fi</command> + <command>${vyos_op_scripts_dir}/show_ntp.sh --basic</command> <children> <tagNode name="server"> <properties> @@ -15,15 +15,14 @@ <script>${vyos_completion_dir}/list_ntp_servers.sh</script> </completionHelp> </properties> - <command>/usr/sbin/ntpdate -q "$4"</command> + <command>${vyos_op_scripts_dir}/show_ntp.sh --server "$4"</command> </tagNode> <node name="info"> <properties> <help>Show NTP operational summary</help> </properties> - <command>if ps -C ntpd &>/dev/null; then ntpq -n -c sysinfo; ntpq -n -c kerninfo; else echo NTP daemon disabled; fi</command> - </node> - + <command>${vyos_op_scripts_dir}/show_ntp.sh --info</command> + </node> </children> </node> </children> diff --git a/op-mode-definitions/show-poweroff.xml b/op-mode-definitions/show-poweroff.xml.in index 1fd2afcc3..1fd2afcc3 100644 --- a/op-mode-definitions/show-poweroff.xml +++ b/op-mode-definitions/show-poweroff.xml.in diff --git a/op-mode-definitions/show-protocols-bfd.xml b/op-mode-definitions/show-protocols-bfd.xml.in index 3d9b67c67..886b01e51 100644 --- a/op-mode-definitions/show-protocols-bfd.xml +++ b/op-mode-definitions/show-protocols-bfd.xml.in @@ -13,13 +13,13 @@ <properties> <help>Show all Bidirectional Forwarding Detection (BFD) peer status</help> </properties> - <command>/usr/bin/vtysh -c "show bfd peers"</command> + <command>vtysh -c "show bfd peers"</command> <children> <leafNode name="counters"> <properties> <help>Show Bidirectional Forwarding Detection (BFD) peer counters</help> </properties> - <command>/usr/bin/vtysh -c "show bfd peers counters"</command> + <command>vtysh -c "show bfd peers counters"</command> </leafNode> </children> </node> @@ -27,16 +27,16 @@ <properties> <help>Show Bidirectional Forwarding Detection (BFD) peer status</help> <completionHelp> - <script>/usr/bin/vtysh -c "show bfd peers" | awk '/[:blank:]*peer/ { printf "%s\n", $2 }'</script> + <script>vtysh -c "show bfd peers" | awk '/[:blank:]*peer/ { printf "%s\n", $2 }'</script> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show bfd peers" | awk -v BFD_PEER=$5 'BEGIN { regex = sprintf("(peer %s.*)vrf", BFD_PEER) } { if (match($0, regex, bfd_peer_value)) peer=bfd_peer_value[1] } END { if (peer) system("/usr/bin/vtysh -c \"show bfd " peer "\"") }'</command> + <command>vtysh -c "show bfd peers" | awk -v BFD_PEER=$5 'BEGIN { regex = sprintf("(peer %s.*)vrf", BFD_PEER) } { if (match($0, regex, bfd_peer_value)) peer=bfd_peer_value[1] } END { if (peer) system("vtysh -c \"show bfd " peer "\"") }'</command> <children> <leafNode name="counters"> <properties> <help>Show Bidirectional Forwarding Detection (BFD) peer counters</help> </properties> - <command>/usr/bin/vtysh -c "show bfd peers" | awk -v BFD_PEER=$5 'BEGIN { regex = sprintf("(peer %s.*)vrf", BFD_PEER) } { if (match($0, regex, bfd_peer_value)) peer=bfd_peer_value[1] } END { if (peer) system("/usr/bin/vtysh -c \"show bfd " peer " counters\"") }'</command> + <command>vtysh -c "show bfd peers" | awk -v BFD_PEER=$5 'BEGIN { regex = sprintf("(peer %s.*)vrf", BFD_PEER) } { if (match($0, regex, bfd_peer_value)) peer=bfd_peer_value[1] } END { if (peer) system("vtysh -c \"show bfd " peer " counters\"") }'</command> </leafNode> </children> </tagNode> @@ -44,7 +44,7 @@ <properties> <help>Show Bidirectional Forwarding Detection (BFD) peers brief</help> </properties> - <command>/usr/bin/vtysh -c "show bfd peers brief"</command> + <command>vtysh -c "show bfd peers brief"</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-protocols-static.xml b/op-mode-definitions/show-protocols-static.xml.in index aaf875072..aaf875072 100644 --- a/op-mode-definitions/show-protocols-static.xml +++ b/op-mode-definitions/show-protocols-static.xml.in diff --git a/op-mode-definitions/show-raid.xml b/op-mode-definitions/show-raid.xml.in index 8bf394552..8bf394552 100644 --- a/op-mode-definitions/show-raid.xml +++ b/op-mode-definitions/show-raid.xml.in diff --git a/op-mode-definitions/show-reboot.xml b/op-mode-definitions/show-reboot.xml.in index c85966bcb..c85966bcb 100644 --- a/op-mode-definitions/show-reboot.xml +++ b/op-mode-definitions/show-reboot.xml.in diff --git a/op-mode-definitions/show-route-map.xml b/op-mode-definitions/show-route-map.xml.in index 0e376757b..633b2a4cb 100644 --- a/op-mode-definitions/show-route-map.xml +++ b/op-mode-definitions/show-route-map.xml.in @@ -6,7 +6,7 @@ <properties> <help>Show route-map information</help> </properties> - <command>/usr/bin/vtysh -c "show route-map"</command> + <command>vtysh -c "show route-map"</command> </node> <tagNode name="route-map"> <properties> @@ -15,7 +15,7 @@ <path>policy route-map</path> </completionHelp> </properties> - <command>/usr/bin/vtysh -c "show route-map $3"</command> + <command>vtysh -c "show route-map $3"</command> </tagNode> </children> </node> diff --git a/op-mode-definitions/show-rpki.xml b/op-mode-definitions/show-rpki.xml.in index d68c3b862..f593e4803 100644 --- a/op-mode-definitions/show-rpki.xml +++ b/op-mode-definitions/show-rpki.xml.in @@ -11,19 +11,19 @@ <properties> <help>Show RPKI cache connections</help> </properties> - <command>/usr/bin/vtysh -c "show rpki cache-connection"</command> + <command>vtysh -c "show rpki cache-connection"</command> </leafNode> <leafNode name="cache-server"> <properties> <help>Show RPKI cache servers information</help> </properties> - <command>/usr/bin/vtysh -c "show rpki cache-server"</command> + <command>vtysh -c "show rpki cache-server"</command> </leafNode> <leafNode name="prefix-table"> <properties> <help>Show RPKI-validated prefixes</help> </properties> - <command>/usr/bin/vtysh -c "show rpki prefix-table"</command> + <command>vtysh -c "show rpki prefix-table"</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-system.xml b/op-mode-definitions/show-system.xml.in index 0623e3b62..5e9bf719e 100644 --- a/op-mode-definitions/show-system.xml +++ b/op-mode-definitions/show-system.xml.in @@ -128,7 +128,7 @@ <properties> <help>Show memory usage of all routing protocols</help> </properties> - <command>/usr/bin/vtysh -c "show memory"</command> + <command>vtysh -c "show memory"</command> </leafNode> </children> </node> @@ -162,7 +162,7 @@ <properties> <help>Show Quagga routing daemons</help> </properties> - <command>/usr/bin/vtysh -c "show daemons"</command> + <command>vtysh -c "show daemons"</command> </leafNode> <leafNode name="storage"> <properties> diff --git a/op-mode-definitions/show-table.xml b/op-mode-definitions/show-table.xml.in index b093a5de7..c7998e35d 100644 --- a/op-mode-definitions/show-table.xml +++ b/op-mode-definitions/show-table.xml.in @@ -6,7 +6,7 @@ <properties> <help>Show routing tables</help> </properties> - <command>/usr/bin/vtysh -c "show zebra router table summary"</command> + <command>vtysh -c "show zebra router table summary"</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-users.xml b/op-mode-definitions/show-users.xml.in index a026e47e7..a026e47e7 100644 --- a/op-mode-definitions/show-users.xml +++ b/op-mode-definitions/show-users.xml.in diff --git a/op-mode-definitions/show-version.xml b/op-mode-definitions/show-version.xml.in index 2202d27b3..6bc49b8cf 100644 --- a/op-mode-definitions/show-version.xml +++ b/op-mode-definitions/show-version.xml.in @@ -18,13 +18,13 @@ <properties> <help>Show system version and versions of all packages</help> </properties> - <command>echo "Package versions:"; dpkg -l | awk '$0~/>/{exit}1'</command> + <command>echo "Package versions:"; dpkg -l | cat</command> </leafNode> <leafNode name="frr"> <properties> <help>Show Quagga version information</help> </properties> - <command>/usr/bin/vtysh -c "show version"</command> + <command>vtysh -c "show version"</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-vpn.xml b/op-mode-definitions/show-vpn.xml.in index 0e7fc38e9..3fbc74ad1 100644 --- a/op-mode-definitions/show-vpn.xml +++ b/op-mode-definitions/show-vpn.xml.in @@ -11,7 +11,7 @@ <properties> <help>Show active VPN server sessions</help> </properties> - <command>${vyos_op_scripts_dir}/show_vpn_ra.py</command> + <command>${vyos_op_scripts_dir}/show_vpn_ra.py</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-vrf.xml b/op-mode-definitions/show-vrf.xml.in index 438e7c334..438e7c334 100644 --- a/op-mode-definitions/show-vrf.xml +++ b/op-mode-definitions/show-vrf.xml.in diff --git a/op-mode-definitions/show-zebra.xml.in b/op-mode-definitions/show-zebra.xml.in new file mode 100644 index 000000000..b0ad37f49 --- /dev/null +++ b/op-mode-definitions/show-zebra.xml.in @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="show"> + <children> + <node name="zebra"> + <properties> + <help>Zebra routing information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <node name="client"> + <properties> + <help>Client information </help> + </properties> + <children> + <node name="summary"> + <properties> + <help>Brief Summary</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </node> + </children> + </node> + <node name="dplane"> + <properties> + <help>Zebra dataplane information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </node> + <node name="router"> + <properties> + <help>Zebra Router Information</help> + </properties> + <children> + <node name="table"> + <properties> + <help>Table Information about this Zebra Router</help> + </properties> + <children> + <node name="summary"> + <properties> + <help>Summary Information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </node> + </children> + </node> + </children> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/snmp.xml b/op-mode-definitions/snmp.xml.in index a0a47da40..a0a47da40 100644 --- a/op-mode-definitions/snmp.xml +++ b/op-mode-definitions/snmp.xml.in diff --git a/op-mode-definitions/sstp-server.xml b/op-mode-definitions/sstp-server.xml.in index 03dfc4262..03dfc4262 100644 --- a/op-mode-definitions/sstp-server.xml +++ b/op-mode-definitions/sstp-server.xml.in diff --git a/op-mode-definitions/telnet.xml b/op-mode-definitions/telnet.xml.in index c5bb6d283..c5bb6d283 100644 --- a/op-mode-definitions/telnet.xml +++ b/op-mode-definitions/telnet.xml.in diff --git a/op-mode-definitions/terminal.xml b/op-mode-definitions/terminal.xml.in index 9c4e629cb..9c4e629cb 100644 --- a/op-mode-definitions/terminal.xml +++ b/op-mode-definitions/terminal.xml.in diff --git a/op-mode-definitions/traceroute.xml b/op-mode-definitions/traceroute.xml.in index 1b619ed43..1b619ed43 100644 --- a/op-mode-definitions/traceroute.xml +++ b/op-mode-definitions/traceroute.xml.in diff --git a/op-mode-definitions/traffic-dump.xml b/op-mode-definitions/traffic-dump.xml.in index 6d86f7423..76e3ddce5 100644 --- a/op-mode-definitions/traffic-dump.xml +++ b/op-mode-definitions/traffic-dump.xml.in @@ -16,6 +16,34 @@ </completionHelp> </properties> <children> + <node name="verbose"> + <command>sudo tcpdump -vvv -ne -i $4</command> + <properties> + <help>Provide more detailed packets for each monitored traffic</help> + </properties> + <children> + <tagNode name="filter"> + <command>sudo tcpdump -vvv -ne -i $4 "${@:6}"</command> + <properties> + <help>Monitor traffic matching filter conditions</help> + </properties> + </tagNode> + <tagNode name="save"> + <command>sudo tcpdump -vvv -ne -i $4 -w $6</command> + <properties> + <help>Save traffic dump from an interface to a file</help> + </properties> + <children> + <tagNode name="filter"> + <command>sudo tcpdump -vvv -ne -i $4 -w $6 "${@:8}"</command> + <properties> + <help>Save a dump of traffic matching filter conditions to a file</help> + </properties> + </tagNode> + </children> + </tagNode> + </children> + </node> <tagNode name="filter"> <command>sudo tcpdump -n -i $4 "${@:6}"</command> <properties> diff --git a/op-mode-definitions/vrrp.xml b/op-mode-definitions/vrrp.xml.in index 856fb440d..34484c706 100644 --- a/op-mode-definitions/vrrp.xml +++ b/op-mode-definitions/vrrp.xml.in @@ -28,7 +28,7 @@ <children> <node name="vrrp"> <properties> - <help>Restart the VRRP (Virtual Router Redundancy Protocol) process</help> + <help>Restart VRRP (Virtual Router Redundancy Protocol) process</help> </properties> <command>sudo systemctl restart keepalived.service</command> </node> diff --git a/op-mode-definitions/wake-on-lan.xml b/op-mode-definitions/wake-on-lan.xml.in index 1a9b88596..1a9b88596 100644 --- a/op-mode-definitions/wake-on-lan.xml +++ b/op-mode-definitions/wake-on-lan.xml.in diff --git a/op-mode-definitions/webproxy.xml b/op-mode-definitions/webproxy.xml.in index f8ec8fb0a..f8ec8fb0a 100644 --- a/op-mode-definitions/webproxy.xml +++ b/op-mode-definitions/webproxy.xml.in diff --git a/op-mode-definitions/wireguard.xml b/op-mode-definitions/wireguard.xml.in index a7bfa36a3..4aee4b1ac 100644 --- a/op-mode-definitions/wireguard.xml +++ b/op-mode-definitions/wireguard.xml.in @@ -1,28 +1,28 @@ <?xml version="1.0"?> -<!-- wireguard key management --> +<!-- Wireguard key management --> <interfaceDefinition> <node name="generate"> <children> <node name="wireguard"> <properties> - <help>wireguard key generation utility</help> + <help>Generate Wireguard keys</help> </properties> <children> <leafNode name="default-keypair"> <properties> - <help>generates the wireguard default-keypair</help> + <help>Generate the default Wireguard keypair</help> </properties> <command>sudo ${vyos_op_scripts_dir}/wireguard.py --genkey</command> </leafNode> <leafNode name="preshared-key"> <properties> - <help>generate a wireguard preshared key</help> + <help>Generate a Wireguard preshared key</help> </properties> <command>${vyos_op_scripts_dir}/wireguard.py --genpsk</command> </leafNode> <tagNode name="named-keypairs"> <properties> - <help>Generates named wireguard keypairs</help> + <help>Generate specified Wireguard keypairs</help> </properties> <command>sudo ${vyos_op_scripts_dir}/wireguard.py --genkey --location "$4"</command> </tagNode> @@ -34,17 +34,17 @@ <children> <node name="wireguard"> <properties> - <help>Show wireguard properties</help> + <help>Show Wireguard properties</help> </properties> <children> <node name="keypairs"> <properties> - <help>Shows named wireguard keys</help> + <help>Show Wireguard keys</help> </properties> <children> <tagNode name="pubkey"> <properties> - <help>Show wireguard private named key</help> + <help>Show specified Wireguard public key</help> <completionHelp> <script>${vyos_op_scripts_dir}/wireguard.py --listkdir</script> </completionHelp> @@ -53,7 +53,7 @@ </tagNode> <tagNode name="privkey"> <properties> - <help>Show wireguard public named key</help> + <help>Show specified Wireguard private key</help> <completionHelp> <script>${vyos_op_scripts_dir}/wireguard.py --listkdir</script> </completionHelp> @@ -68,7 +68,7 @@ <children> <tagNode name="wireguard"> <properties> - <help>show wireguard interface information</help> + <help>Show Wireguard interface information</help> <completionHelp> <script>${vyos_completion_dir}/list_interfaces.py --type wireguard</script> </completionHelp> @@ -77,19 +77,19 @@ <children> <leafNode name="allowed-ips"> <properties> - <help>show all allowed-ips for the specified interface</help> + <help>Show all IP addresses allowed for the specified interface</help> </properties> <command>sudo wg show "$4" allowed-ips</command> </leafNode> <leafNode name="endpoints"> <properties> - <help>show all endpoints for the specified interface</help> + <help>Show all endpoints for the specified interface</help> </properties> <command>sudo wg show "$4" endpoints</command> </leafNode> <leafNode name="peers"> <properties> - <help>show all peer IDs for the specified interface</help> + <help>Show all peer IDs for the specified interface</help> </properties> <command>sudo wg show "$4" peers</command> </leafNode> @@ -98,13 +98,13 @@ </tagNode> <node name="wireguard"> <properties> - <help>Show wireguard interface information</help> + <help>Show Wireguard interface information</help> </properties> <command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wireguard --action=show-brief</command> <children> <leafNode name="detail"> <properties> - <help>Show detailed wireguard interface information</help> + <help>Show detailed Wireguard interface information</help> </properties> <command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wireguard --action=show</command> </leafNode> @@ -118,12 +118,12 @@ <children> <node name="wireguard"> <properties> - <help>Delete wireguard properties</help> + <help>Delete Wireguard properties</help> </properties> <children> <tagNode name="keypair"> <properties> - <help>Delete a wireguard keypair</help> + <help>Delete a Wireguard keypair</help> <completionHelp> <script>${vyos_op_scripts_dir}/wireguard.py --listkdir</script> </completionHelp> @@ -135,4 +135,3 @@ </children> </node> </interfaceDefinition> - diff --git a/op-mode-definitions/wireless.xml b/op-mode-definitions/wireless.xml.in index a3a9d1f55..a3a9d1f55 100644 --- a/op-mode-definitions/wireless.xml +++ b/op-mode-definitions/wireless.xml.in diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index e5e758a8b..5acb1fdfe 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -126,14 +126,14 @@ def leaf_node_changed(conf, path): return None -def node_changed(conf, path): +def node_changed(conf, path, key_mangling=None): """ Check if a leaf node was altered. If it has been altered - values has been changed, or it was added/removed, we will return the old value. If nothing has been changed, None is returned """ from vyos.configdiff import get_config_diff, Diff - D = get_config_diff(conf, key_mangling=('-', '_')) + D = get_config_diff(conf, key_mangling) D.set_level(conf.get_level()) # get_child_nodes() will return dict_keys(), mangle this into a list with PEP448 keys = D.get_child_nodes_diff(path, expand_nodes=Diff.DELETE)['delete'].keys() @@ -272,9 +272,9 @@ def has_vlan_subinterface_configured(conf, intf): old_level = conf.get_level() conf.set_level([]) - intfpath = 'interfaces ' + Section.get_config_path(intf) - if ( conf.exists(f'{intfpath} vif') or - conf.exists(f'{intfpath} vif-s')): + intfpath = ['interfaces', Section.section(intf), intf] + if ( conf.exists(intfpath + ['vif']) or + conf.exists(intfpath + ['vif-s'])): ret = True conf.set_level(old_level) diff --git a/python/vyos/configquery.py b/python/vyos/configquery.py new file mode 100644 index 000000000..ed7346f1f --- /dev/null +++ b/python/vyos/configquery.py @@ -0,0 +1,90 @@ +# Copyright 2021 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +''' +A small library that allows querying existence or value(s) of config +settings from op mode, and execution of arbitrary op mode commands. +''' + +from subprocess import STDOUT +from vyos.util import popen + + +class ConfigQueryError(Exception): + pass + +class GenericConfigQuery: + def __init__(self): + pass + + def exists(self, path: list): + raise NotImplementedError + + def value(self, path: list): + raise NotImplementedError + + def values(self, path: list): + raise NotImplementedError + +class GenericOpRun: + def __init__(self): + pass + + def run(self, path: list, **kwargs): + raise NotImplementedError + +class CliShellApiConfigQuery(GenericConfigQuery): + def __init__(self): + super().__init__() + + def exists(self, path: list): + cmd = ' '.join(path) + (_, err) = popen(f'cli-shell-api existsActive {cmd}') + if err: + return False + return True + + def value(self, path: list): + cmd = ' '.join(path) + (out, err) = popen(f'cli-shell-api returnActiveValue {cmd}') + if err: + raise ConfigQueryError('No value for given path') + return out + + def values(self, path: list): + cmd = ' '.join(path) + (out, err) = popen(f'cli-shell-api returnActiveValues {cmd}') + if err: + raise ConfigQueryError('No values for given path') + return out + +class VbashOpRun(GenericOpRun): + def __init__(self): + super().__init__() + + def run(self, path: list, **kwargs): + cmd = ' '.join(path) + (out, err) = popen(f'. /opt/vyatta/share/vyatta-op/functions/interpreter/vyatta-op-run; _vyatta_op_run {cmd}', stderr=STDOUT, **kwargs) + if err: + raise ConfigQueryError(out) + return out + +def query_context(config_query_class=CliShellApiConfigQuery, + op_run_class=VbashOpRun): + query = config_query_class() + run = op_run_class() + return query, run + + diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py index 82b9355a3..670e6c7fc 100644 --- a/python/vyos/configsession.py +++ b/python/vyos/configsession.py @@ -129,9 +129,9 @@ class ConfigSession(object): def __run_command(self, cmd_list): p = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=self.__session_env) + (stdout_data, stderr_data) = p.communicate() + output = stdout_data.decode() result = p.wait() - output = p.stdout.read().decode() - p.communicate() if result != 0: raise ConfigSessionError(output) return output diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index b4447306e..718b7445d 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -80,7 +80,7 @@ def verify_vrf(config): recurring validation of VRF configuration. """ from netifaces import interfaces - if 'vrf' in config: + if 'vrf' in config and config['vrf'] != 'default': if config['vrf'] not in interfaces(): raise ConfigError('VRF "{vrf}" does not exist'.format(**config)) @@ -89,6 +89,50 @@ def verify_vrf(config): 'Interface "{ifname}" cannot be both a member of VRF "{vrf}" ' 'and bridge "{is_bridge_member}"!'.format(**config)) +def verify_tunnel(config): + """ + This helper is used to verify the common part of the tunnel + """ + from vyos.template import is_ipv4 + from vyos.template import is_ipv6 + + if 'encapsulation' not in config: + raise ConfigError('Must configure the tunnel encapsulation for '\ + '{ifname}!'.format(**config)) + + if 'source_address' not in config and 'dhcp_interface' not in config: + raise ConfigError('source-address is mandatory for tunnel') + + if 'remote' not in config and config['encapsulation'] != 'gre': + raise ConfigError('remote ip address is mandatory for tunnel') + + if {'source_address', 'dhcp_interface'} <= set(config): + raise ConfigError('Can not use both source-address and dhcp-interface') + + if config['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre', 'ip6gretap', 'ip6erspan']: + error_ipv6 = 'Encapsulation mode requires IPv6' + if 'source_address' in config and not is_ipv6(config['source_address']): + raise ConfigError(f'{error_ipv6} source-address') + + if 'remote' in config and not is_ipv6(config['remote']): + raise ConfigError(f'{error_ipv6} remote') + else: + error_ipv4 = 'Encapsulation mode requires IPv4' + if 'source_address' in config and not is_ipv4(config['source_address']): + raise ConfigError(f'{error_ipv4} source-address') + + if 'remote' in config and not is_ipv4(config['remote']): + raise ConfigError(f'{error_ipv4} remote address') + + if config['encapsulation'] in ['sit', 'gretap', 'ip6gretap']: + if 'source_interface' in config: + encapsulation = config['encapsulation'] + raise ConfigError(f'Option source-interface can not be used with ' \ + f'encapsulation "{encapsulation}"!') + elif config['encapsulation'] == 'gre': + if 'source_address' in config and is_ipv6(config['source_address']): + raise ConfigError('Can not use local IPv6 address is for mGRE tunnels') + def verify_eapol(config): """ Common helper function used by interface implementations to perform @@ -136,15 +180,14 @@ def verify_bridge_delete(config): 'Interface "{ifname}" cannot be deleted as it is a ' 'member of bridge "{is_bridge_member}"!'.format(**config)) -def verify_interface_exists(config): +def verify_interface_exists(ifname): """ Common helper function used by interface implementations to perform recurring validation if an interface actually exists. """ from netifaces import interfaces - if not config['ifname'] in interfaces(): - raise ConfigError('Interface "{ifname}" does not exist!' - .format(**config)) + if ifname not in interfaces(): + raise ConfigError(f'Interface "{ifname}" does not exist!') def verify_source_interface(config): """ @@ -210,6 +253,13 @@ def verify_vlan_config(config): Common helper function used by interface implementations to perform recurring validation of interface VLANs """ + + # VLAN and Q-in-Q IDs are not allowed to overlap + if 'vif' in config and 'vif_s' in config: + duplicate = list(set(config['vif']) & set(config['vif_s'])) + if duplicate: + raise ConfigError(f'Duplicate VLAN id "{duplicate[0]}" used for vif and vif-s interfaces!') + # 802.1q VLANs for vlan in config.get('vif', {}): vlan = config['vif'][vlan] @@ -218,17 +268,17 @@ def verify_vlan_config(config): verify_vrf(vlan) # 802.1ad (Q-in-Q) VLANs - for vlan in config.get('vif_s', {}): - vlan = config['vif_s'][vlan] - verify_dhcpv6(vlan) - verify_address(vlan) - verify_vrf(vlan) - - for vlan in config.get('vif_s', {}).get('vif_c', {}): - vlan = config['vif_c'][vlan] - verify_dhcpv6(vlan) - verify_address(vlan) - verify_vrf(vlan) + for s_vlan in config.get('vif_s', {}): + s_vlan = config['vif_s'][s_vlan] + verify_dhcpv6(s_vlan) + verify_address(s_vlan) + verify_vrf(s_vlan) + + for c_vlan in s_vlan.get('vif_c', {}): + c_vlan = s_vlan['vif_c'][c_vlan] + verify_dhcpv6(c_vlan) + verify_address(c_vlan) + verify_vrf(c_vlan) def verify_accel_ppp_base_service(config): """ @@ -308,3 +358,26 @@ def verify_diffie_hellman_length(file, min_keysize): return False +def verify_route_maps(config): + """ + Common helper function used by routing protocol implementations to perform + recurring validation if the specified route-map for either zebra to kernel + installation exists (this is the top-level route_map key) or when a route + is redistributed with a route-map that it exists! + """ + if 'route_map' in config: + route_map = config['route_map'] + # Check if the specified route-map exists, if not error out + if dict_search(f'policy.route_map.{route_map}', config) == None: + raise ConfigError(f'Specified route-map "{route_map}" does not exist!') + + if 'redistribute' in config: + for protocol, protocol_config in config['redistribute'].items(): + if 'route_map' in protocol_config: + # A hyphen in a route-map name will be converted to _, take care + # about this effect during validation + route_map = protocol_config['route_map'].replace('-','_') + # Check if the specified route-map exists, if not error out + if dict_search(f'policy.route_map.{route_map}', config) == None: + raise ConfigError(f'Redistribution route-map "{route_map}" ' \ + f'for "{protocol}" does not exist!') diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py new file mode 100644 index 000000000..136feae8d --- /dev/null +++ b/python/vyos/ethtool.py @@ -0,0 +1,101 @@ +# Copyright 2021 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + +from vyos.util import popen + +class Ethtool: + """ + Class is used to retrive and cache information about an ethernet adapter + """ + + # dictionary containing driver featurs, it will be populated on demand and + # the content will look like: + # { + # 'tls-hw-tx-offload': {'fixed': True, 'on': False}, + # 'tx-checksum-fcoe-crc': {'fixed': True, 'on': False}, + # 'tx-checksum-ip-generic': {'fixed': False, 'on': True}, + # 'tx-checksum-ipv4': {'fixed': True, 'on': False}, + # 'tx-checksum-ipv6': {'fixed': True, 'on': False}, + # 'tx-checksum-sctp': {'fixed': True, 'on': False}, + # 'tx-checksumming': {'fixed': False, 'on': True}, + # 'tx-esp-segmentation': {'fixed': True, 'on': False}, + # } + features = { } + ring_buffers = { } + + def __init__(self, ifname): + # Now populate features dictionaty + out, err = popen(f'ethtool -k {ifname}') + # skip the first line, it only says: "Features for eth0": + for line in out.splitlines()[1:]: + if ":" in line: + key, value = [s.strip() for s in line.strip().split(":", 1)] + fixed = "fixed" in value + if fixed: + value = value.split()[0].strip() + self.features[key.strip()] = { + "on": value == "on", + "fixed": fixed + } + + out, err = popen(f'ethtool -g {ifname}') + # We are only interested in line 2-5 which contains the device maximum + # ringbuffers + for line in out.splitlines()[2:6]: + if ':' in line: + key, value = [s.strip() for s in line.strip().split(":", 1)] + key = key.lower().replace(' ', '_') + self.ring_buffers[key] = int(value) + + + def is_fixed_lro(self): + # in case of a missing configuration, rather return "fixed". In Ethtool + # terminology "fixed" means the setting can not be changed by the user. + return self.features.get('large-receive-offload', True).get('fixed', True) + + def is_fixed_gro(self): + # in case of a missing configuration, rather return "fixed". In Ethtool + # terminology "fixed" means the setting can not be changed by the user. + return self.features.get('generic-receive-offload', True).get('fixed', True) + + def is_fixed_gso(self): + # in case of a missing configuration, rather return "fixed". In Ethtool + # terminology "fixed" means the setting can not be changed by the user. + return self.features.get('generic-segmentation-offload', True).get('fixed', True) + + def is_fixed_sg(self): + # in case of a missing configuration, rather return "fixed". In Ethtool + # terminology "fixed" means the setting can not be changed by the user. + return self.features.get('scatter-gather', True).get('fixed', True) + + def is_fixed_tso(self): + # in case of a missing configuration, rather return "fixed". In Ethtool + # terminology "fixed" means the setting can not be changed by the user. + return self.features.get('tcp-segmentation-offload', True).get('fixed', True) + + def is_fixed_ufo(self): + # in case of a missing configuration, rather return "fixed". In Ethtool + # terminology "fixed" means the setting can not be changed by the user. + return self.features.get('udp-fragmentation-offload', True).get('fixed', True) + + def get_rx_buffer(self): + # Configuration of RX ring-buffers is not supported on every device, + # thus when it's impossible return None + return self.ring_buffers.get('rx', None) + + def get_tx_buffer(self): + # Configuration of TX ring-buffers is not supported on every device, + # thus when it's impossible return None + return self.ring_buffers.get('tx', None) diff --git a/python/vyos/frr.py b/python/vyos/frr.py index 3bab64301..668489636 100644 --- a/python/vyos/frr.py +++ b/python/vyos/frr.py @@ -68,15 +68,26 @@ Apply the new configuration: import tempfile import re from vyos import util +from vyos.util import chown import logging +from logging.handlers import SysLogHandler +import os LOG = logging.getLogger(__name__) +DEBUG = os.path.exists('/tmp/vyos.frr.debug') +if DEBUG: + LOG.setLevel(logging.DEBUG) + ch = SysLogHandler(address='/dev/log') + ch2 = logging.StreamHandler() + LOG.addHandler(ch) + LOG.addHandler(ch2) _frr_daemons = ['zebra', 'bgpd', 'fabricd', 'isisd', 'ospf6d', 'ospfd', 'pbrd', 'pimd', 'ripd', 'ripngd', 'sharpd', 'staticd', 'vrrpd', 'ldpd'] path_vtysh = '/usr/bin/vtysh' path_frr_reload = '/usr/lib/frr/frr-reload.py' +path_config = '/run/frr' class FrrError(Exception): @@ -175,21 +186,53 @@ def reload_configuration(config, daemon=None): f.write(config) f.flush() + LOG.debug(f'reload_configuration: Reloading config using temporary file: {f.name}') cmd = f'{path_frr_reload} --reload' if daemon: cmd += f' --daemon {daemon}' + + if DEBUG: + cmd += f' --debug --stdout' + cmd += f' {f.name}' + LOG.debug(f'reload_configuration: Executing command against frr-reload: "{cmd}"') output, code = util.popen(cmd, stderr=util.STDOUT) f.close() + for i, e in enumerate(output.split('\n')): + LOG.debug(f'frr-reload output: {i:3} {e}') if code == 1: - raise CommitError(f'Configuration FRR failed while commiting code: {repr(output)}') + raise CommitError(f'Configuration FRR failed while commiting code, please enabling debugging to examine logs') elif code: raise OSError(code, output) return output +def save_configuration(daemon=None): + """Save FRR configuration to /run/frr/{daemon}.conf + It save configuration on each commit. + """ + if daemon and daemon not in _frr_daemons: + raise ValueError(f'The specified daemon type is not supported {repr(daemon)}') + + cmd = f"{path_vtysh} -d {daemon} -c 'show run no-header'" + output, code = util.popen(cmd, stderr=util.STDOUT) + if code: + raise OSError(code, output) + + daemon_conf = f'{path_config}/{daemon}.conf' + + with open(daemon_conf, "w") as f: + f.write(output) + # Set permissions (frr:frr) for /run/frr/{daemon}.conf + if os.path.exists(daemon_conf): + chown(daemon_conf, 'frr', 'frr') + config = output + + return config + + def execute(command): """ Run commands inside vtysh command: str containing commands to execute inside a vtysh session @@ -382,6 +425,11 @@ class FRRConfig: raise ValueError( 'The config element needs to be a string or list type object') + if config: + LOG.debug(f'__init__: frr library initiated with initial config') + for i, e in enumerate(self.config): + LOG.debug(f'__init__: initial {i:3} {e}') + 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 @@ -390,9 +438,16 @@ class FRRConfig: 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}') + if daemon: + LOG.debug(f'load_configuration: Configuration loaded from FRR daemon {daemon}') + else: + LOG.debug(f'load_configuration: Configuration loaded from FRR integrated config') + self.original_config = self.imported_config.split('\n') self.config = self.original_config.copy() + + for i, e in enumerate(self.imported_config.split('\n')): + LOG.debug(f'load_configuration: loaded {i:3} {e}') return def test_configuration(self): @@ -408,6 +463,8 @@ class FRRConfig: None to use the consolidated config ''' LOG.debug('commit_configuration: Commiting configuration') + for i, e in enumerate(self.config): + LOG.debug(f'commit_configuration: new_config {i:3} {e}') 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): @@ -459,7 +516,8 @@ class FRRConfig: start = _find_first_element(self.config, before_pattern) if start < 0: return False - + for i, e in enumerate(addition, start=start): + LOG.debug(f'add_before: add {i:3} {e}') self.config[start:start] = addition return True diff --git a/python/vyos/ifconfig/__init__.py b/python/vyos/ifconfig/__init__.py index 9cd8d44c1..f5dfa8e05 100644 --- a/python/vyos/ifconfig/__init__.py +++ b/python/vyos/ifconfig/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -31,14 +31,9 @@ from vyos.ifconfig.wireguard import WireGuardIf from vyos.ifconfig.vtun import VTunIf from vyos.ifconfig.vti import VTIIf from vyos.ifconfig.pppoe import PPPoEIf -from vyos.ifconfig.tunnel import GREIf -from vyos.ifconfig.tunnel import GRETapIf -from vyos.ifconfig.tunnel import IP6GREIf -from vyos.ifconfig.tunnel import IPIPIf -from vyos.ifconfig.tunnel import IPIP6If -from vyos.ifconfig.tunnel import IP6IP6If -from vyos.ifconfig.tunnel import SitIf -from vyos.ifconfig.tunnel import Sit6RDIf +from vyos.ifconfig.tunnel import TunnelIf +from vyos.ifconfig.erspan import ERSpanIf +from vyos.ifconfig.erspan import ER6SpanIf from vyos.ifconfig.wireless import WiFiIf from vyos.ifconfig.l2tpv3 import L2TPv3If from vyos.ifconfig.macsec import MACsecIf diff --git a/python/vyos/ifconfig/bond.py b/python/vyos/ifconfig/bond.py index 709222b09..bfa3b0025 100644 --- a/python/vyos/ifconfig/bond.py +++ b/python/vyos/ifconfig/bond.py @@ -1,4 +1,4 @@ -# Copyright 2019-2020 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -31,9 +31,7 @@ class BondIf(Interface): monitoring may be performed. """ - default = { - 'type': 'bond', - } + iftype = 'bond' definition = { **Interface.definition, ** { @@ -343,9 +341,6 @@ class BondIf(Interface): if 'shutdown_required' in config: self.set_admin_state('down') - # call base class first - super().update(config) - # ARP monitor targets need to be synchronized between sysfs and CLI. # Unfortunately an address can't be send twice to sysfs as this will # result in the following exception: OSError: [Errno 22] Invalid argument. @@ -404,12 +399,5 @@ class BondIf(Interface): value = config.get('primary') if value: self.set_primary(value) - # Enable/Disable of an interface must always be done at the end of the - # derived class to make use of the ref-counting set_admin_state() - # function. We will only enable the interface if 'up' was called as - # often as 'down'. This is required by some interface implementations - # as certain parameters can only be changed when the interface is - # in admin-down state. This ensures the link does not flap during - # reconfiguration. - state = 'down' if 'disable' in config else 'up' - self.set_admin_state(state) + # call base class first + super().update(config) diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py index 76520f2ba..14f64a8de 100644 --- a/python/vyos/ifconfig/bridge.py +++ b/python/vyos/ifconfig/bridge.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -22,6 +22,7 @@ from vyos.validate import assert_positive from vyos.util import cmd from vyos.util import dict_search from vyos.configdict import get_vlan_ids +from vyos.configdict import list_diff @Interface.register class BridgeIf(Interface): @@ -33,10 +34,7 @@ class BridgeIf(Interface): The Linux bridge code implements a subset of the ANSI/IEEE 802.1d standard. """ - - default = { - 'type': 'bridge', - } + iftype = 'bridge' definition = { **Interface.definition, **{ @@ -235,11 +233,6 @@ class BridgeIf(Interface): interface setup code and provide a single point of entry when workin on any interface. """ - # call base class first - super().update(config) - - ifname = config['ifname'] - # Set ageing time value = config.get('aging') self.set_ageing_time(value) @@ -274,20 +267,37 @@ class BridgeIf(Interface): for member in (tmp or []): if member in interfaces(): self.del_port(member) - vlan_filter = 0 - vlan_del = set() - vlan_add = set() + # enable/disable Vlan Filter + vlan_filter = '1' if 'enable_vlan' in config else '0' + self.set_vlan_filter(vlan_filter) + + ifname = config['ifname'] + if int(vlan_filter): + add_vlan = [] + cur_vlan_ids = get_vlan_ids(ifname) + + tmp = dict_search('vif', config) + if tmp: + for vif, vif_config in tmp.items(): + add_vlan.append(vif) + + # Remove redundant VLANs from the system + for vlan in list_diff(cur_vlan_ids, add_vlan): + cmd = f'bridge vlan del dev {ifname} vid {vlan} self' + self._cmd(cmd) + + for vlan in add_vlan: + cmd = f'bridge vlan add dev {ifname} vid {vlan} self' + self._cmd(cmd) + + # VLAN of bridge parent interface is always 1 + # VLAN 1 is the default VLAN for all unlabeled packets + cmd = f'bridge vlan add dev {ifname} vid 1 pvid untagged self' + self._cmd(cmd) tmp = dict_search('member.interface', config) if tmp: - if self.get_vlan_filter(): - bridge_vlan_ids = get_vlan_ids(ifname) - # Delete VLAN ID for the bridge - if 1 in bridge_vlan_ids: - bridge_vlan_ids.remove(1) - for vlan in bridge_vlan_ids: - vlan_del.add(str(vlan)) for interface, interface_config in tmp.items(): # if interface does yet not exist bail out early and @@ -302,9 +312,15 @@ class BridgeIf(Interface): # not have any addresses configured by CLI so just flush any # remaining ones lower.flush_addrs() + # enslave interface port to bridge self.add_port(interface) + # always set private-vlan/port isolation + tmp = dict_search('isolated', interface_config) + value = 'on' if (tmp != None) else 'off' + lower.set_port_isolation(value) + # set bridge port path cost if 'cost' in interface_config: value = interface_config.get('cost') @@ -315,70 +331,40 @@ class BridgeIf(Interface): value = interface_config.get('priority') lower.set_path_priority(value) - tmp = dict_search('native_vlan_removed', interface_config) - - for vlan_id in (tmp or []): - cmd = f'bridge vlan del dev {interface} vid {vlan_id}' - self._cmd(cmd) - cmd = f'bridge vlan add dev {interface} vid 1 pvid untagged master' - self._cmd(cmd) - vlan_del.add(vlan_id) - vlan_add.add(1) - - tmp = dict_search('allowed_vlan_removed', interface_config) - - - for vlan_id in (tmp or []): - cmd = f'bridge vlan del dev {interface} vid {vlan_id}' - self._cmd(cmd) - vlan_del.add(vlan_id) - - if 'native_vlan' in interface_config: - vlan_filter = 1 - cmd = f'bridge vlan del dev {interface} vid 1' - self._cmd(cmd) - vlan_id = interface_config['native_vlan'] - if int(vlan_id) != 1: - if 1 in vlan_add: - vlan_add.remove(1) - vlan_del.add(1) - cmd = f'bridge vlan add dev {interface} vid {vlan_id} pvid untagged master' - self._cmd(cmd) - vlan_add.add(vlan_id) - if vlan_id in vlan_del: - vlan_del.remove(vlan_id) - - if 'allowed_vlan' in interface_config: - vlan_filter = 1 - if 'native_vlan' not in interface_config: - cmd = f'bridge vlan del dev {interface} vid 1' + if int(vlan_filter): + add_vlan = [] + native_vlan_id = None + allowed_vlan_ids= [] + cur_vlan_ids = get_vlan_ids(interface) + + if 'native_vlan' in interface_config: + vlan_id = interface_config['native_vlan'] + add_vlan.append(vlan_id) + native_vlan_id = vlan_id + + if 'allowed_vlan' in interface_config: + for vlan in interface_config['allowed_vlan']: + vlan_range = vlan.split('-') + if len(vlan_range) == 2: + for vlan_add in range(int(vlan_range[0]),int(vlan_range[1]) + 1): + add_vlan.append(str(vlan_add)) + allowed_vlan_ids.append(str(vlan_add)) + else: + add_vlan.append(vlan) + allowed_vlan_ids.append(vlan) + + # Remove redundant VLANs from the system + for vlan in list_diff(cur_vlan_ids, add_vlan): + cmd = f'bridge vlan del dev {interface} vid {vlan} master' self._cmd(cmd) - vlan_del.add(1) - for vlan in interface_config['allowed_vlan']: + + for vlan in allowed_vlan_ids: cmd = f'bridge vlan add dev {interface} vid {vlan} master' self._cmd(cmd) - vlan_add.add(vlan) - if vlan in vlan_del: - vlan_del.remove(vlan) - - for vlan in vlan_del: - cmd = f'bridge vlan del dev {ifname} vid {vlan} self' - self._cmd(cmd) - - for vlan in vlan_add: - cmd = f'bridge vlan add dev {ifname} vid {vlan} self' - self._cmd(cmd) - - # enable/disable Vlan Filter - self.set_vlan_filter(vlan_filter) - + # Setting native VLAN to system + if native_vlan_id: + cmd = f'bridge vlan add dev {interface} vid {native_vlan_id} pvid untagged master' + self._cmd(cmd) - # Enable/Disable of an interface must always be done at the end of the - # derived class to make use of the ref-counting set_admin_state() - # function. We will only enable the interface if 'up' was called as - # often as 'down'. This is required by some interface implementations - # as certain parameters can only be changed when the interface is - # in admin-down state. This ensures the link does not flap during - # reconfiguration. - state = 'down' if 'disable' in config else 'up' - self.set_admin_state(state) + # call base class first + super().update(config) diff --git a/python/vyos/ifconfig/control.py b/python/vyos/ifconfig/control.py index 43136f361..d41dfef47 100644 --- a/python/vyos/ifconfig/control.py +++ b/python/vyos/ifconfig/control.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/python/vyos/ifconfig/dummy.py b/python/vyos/ifconfig/dummy.py index 19ef9d304..d45769931 100644 --- a/python/vyos/ifconfig/dummy.py +++ b/python/vyos/ifconfig/dummy.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -23,9 +23,7 @@ class DummyIf(Interface): packets through without actually transmitting them. """ - default = { - 'type': 'dummy', - } + iftype = 'dummy' definition = { **Interface.definition, **{ @@ -33,22 +31,3 @@ class DummyIf(Interface): 'prefixes': ['dum', ], }, } - - def update(self, config): - """ General helper function which works on a dictionary retrived by - get_config_dict(). It's main intention is to consolidate the scattered - interface setup code and provide a single point of entry when workin - on any interface. """ - - # call base class first - super().update(config) - - # Enable/Disable of an interface must always be done at the end of the - # derived class to make use of the ref-counting set_admin_state() - # function. We will only enable the interface if 'up' was called as - # often as 'down'. This is required by some interface implementations - # as certain parameters can only be changed when the interface is - # in admin-down state. This ensures the link does not flap during - # reconfiguration. - state = 'down' if 'disable' in config else 'up' - self.set_admin_state(state) diff --git a/python/vyos/ifconfig/erspan.py b/python/vyos/ifconfig/erspan.py new file mode 100755 index 000000000..03b2acdbf --- /dev/null +++ b/python/vyos/ifconfig/erspan.py @@ -0,0 +1,170 @@ +# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + +# https://developers.redhat.com/blog/2019/05/17/an-introduction-to-linux-virtual-interfaces-tunnels/#erspan +# http://vger.kernel.org/lpc_net2018_talks/erspan-linux-presentation.pdf + +from copy import deepcopy + +from netaddr import EUI +from netaddr import mac_unix_expanded +from random import getrandbits + +from vyos.util import dict_search +from vyos.ifconfig.interface import Interface +from vyos.validate import assert_list + +@Interface.register +class _ERSpan(Interface): + """ + _ERSpan: private base class for ERSPAN tunnels + """ + iftype = 'erspan' + definition = { + **Interface.definition, + **{ + 'section': 'erspan', + 'prefixes': ['ersp',], + }, + } + + def __init__(self,ifname,**config): + self.config = deepcopy(config) if config else {} + super().__init__(ifname, **self.config) + + def change_options(self): + pass + + def _create(self): + pass + +class ERSpanIf(_ERSpan): + """ + ERSpanIf: private base class for ERSPAN Over GRE and IPv4 tunnels + """ + + def _create(self): + ifname = self.config['ifname'] + source_address = self.config['source_address'] + remote = self.config['remote'] + key = self.config['parameters']['ip']['key'] + version = self.config['parameters']['version'] + command = f'ip link add dev {ifname} type erspan local {source_address} remote {remote} seq key {key} erspan_ver {version}' + + if int(version) == 1: + idx=dict_search('parameters.erspan.idx',self.config) + if idx: + command += f' erspan {idx}' + elif int(version) == 2: + direction=dict_search('parameters.erspan.direction',self.config) + if direction: + command += f' erspan_dir {direction}' + hwid=dict_search('parameters.erspan.hwid',self.config) + if hwid: + command += f' erspan_hwid {hwid}' + + ttl = dict_search('parameters.ip.ttl',self.config) + if ttl: + command += f' ttl {ttl}' + tos = dict_search('parameters.ip.tos',self.config) + if tos: + command += f' tos {tos}' + + self._cmd(command) + + def change_options(self): + ifname = self.config['ifname'] + source_address = self.config['source_address'] + remote = self.config['remote'] + key = self.config['parameters']['ip']['key'] + version = self.config['parameters']['version'] + command = f'ip link set dev {ifname} type erspan local {source_address} remote {remote} seq key {key} erspan_ver {version}' + + if int(version) == 1: + idx=dict_search('parameters.erspan.idx',self.config) + if idx: + command += f' erspan {idx}' + elif int(version) == 2: + direction=dict_search('parameters.erspan.direction',self.config) + if direction: + command += f' erspan_dir {direction}' + hwid=dict_search('parameters.erspan.hwid',self.config) + if hwid: + command += f' erspan_hwid {hwid}' + + ttl = dict_search('parameters.ip.ttl',self.config) + if ttl: + command += f' ttl {ttl}' + tos = dict_search('parameters.ip.tos',self.config) + if tos: + command += f' tos {tos}' + + self._cmd(command) + +class ER6SpanIf(_ERSpan): + """ + ER6SpanIf: private base class for ERSPAN Over GRE and IPv6 tunnels + """ + + def _create(self): + ifname = self.config['ifname'] + source_address = self.config['source_address'] + remote = self.config['remote'] + key = self.config['parameters']['ip']['key'] + version = self.config['parameters']['version'] + command = f'ip link add dev {ifname} type ip6erspan local {source_address} remote {remote} seq key {key} erspan_ver {version}' + + if int(version) == 1: + idx=dict_search('parameters.erspan.idx',self.config) + if idx: + command += f' erspan {idx}' + elif int(version) == 2: + direction=dict_search('parameters.erspan.direction',self.config) + if direction: + command += f' erspan_dir {direction}' + hwid=dict_search('parameters.erspan.hwid',self.config) + if hwid: + command += f' erspan_hwid {hwid}' + + ttl = dict_search('parameters.ip.ttl',self.config) + if ttl: + command += f' ttl {ttl}' + tos = dict_search('parameters.ip.tos',self.config) + if tos: + command += f' tos {tos}' + + self._cmd(command) + + def change_options(self): + ifname = self.config['ifname'] + source_address = self.config['source_address'] + remote = self.config['remote'] + key = self.config['parameters']['ip']['key'] + version = self.config['parameters']['version'] + command = f'ip link set dev {ifname} type ip6erspan local {source_address} remote {remote} seq key {key} erspan_ver {version}' + + if int(version) == 1: + idx=dict_search('parameters.erspan.idx',self.config) + if idx: + command += f' erspan {idx}' + elif int(version) == 2: + direction=dict_search('parameters.erspan.direction',self.config) + if direction: + command += f' erspan_dir {direction}' + hwid=dict_search('parameters.erspan.hwid',self.config) + if hwid: + command += f' erspan_hwid {hwid}' + + self._cmd(command) diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 547b54b84..b89ca5a5c 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -26,10 +26,7 @@ class EthernetIf(Interface): """ Abstraction of a Linux Ethernet Interface """ - - default = { - 'type': 'ethernet', - } + iftype = 'ethernet' definition = { **Interface.definition, **{ @@ -321,9 +318,6 @@ class EthernetIf(Interface): interface setup code and provide a single point of entry when workin on any interface. """ - # call base class first - super().update(config) - # disable ethernet flow control (pause frames) value = 'off' if 'disable_flow_control' in config else 'on' self.set_flow_control(value) @@ -357,12 +351,5 @@ class EthernetIf(Interface): for b_type in config['ring_buffer']: self.set_ring_buffer(b_type, config['ring_buffer'][b_type]) - # Enable/Disable of an interface must always be done at the end of the - # derived class to make use of the ref-counting set_admin_state() - # function. We will only enable the interface if 'up' was called as - # often as 'down'. This is required by some interface implementations - # as certain parameters can only be changed when the interface is - # in admin-down state. This ensures the link does not flap during - # reconfiguration. - state = 'down' if 'disable' in config else 'up' - self.set_admin_state(state) + # call base class first + super().update(config) diff --git a/python/vyos/ifconfig/geneve.py b/python/vyos/ifconfig/geneve.py index 5c4597be8..7cb3968df 100644 --- a/python/vyos/ifconfig/geneve.py +++ b/python/vyos/ifconfig/geneve.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -13,7 +13,8 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see <http://www.gnu.org/licenses/>. -from vyos.ifconfig.interface import Interface +from vyos.ifconfig import Interface +from vyos.util import dict_search @Interface.register class GeneveIf(Interface): @@ -26,14 +27,7 @@ class GeneveIf(Interface): https://developers.redhat.com/blog/2019/05/17/an-introduction-to-linux-virtual-interfaces-tunnels/#geneve https://lwn.net/Articles/644938/ """ - - default = { - 'type': 'geneve', - 'vni': 0, - 'remote': '', - } - options = Interface.options + \ - ['vni', 'remote'] + iftype = 'geneve' definition = { **Interface.definition, **{ @@ -44,27 +38,27 @@ class GeneveIf(Interface): } def _create(self): - cmd = 'ip link add name {ifname} type geneve id {vni} remote {remote}'.format(**self.config) - self._cmd(cmd) + # This table represents a mapping from VyOS internal config dict to + # arguments used by iproute2. For more information please refer to: + # - https://man7.org/linux/man-pages/man8/ip-link.8.html + mapping = { + 'parameters.ip.dont_fragment': 'df set', + 'parameters.ip.tos' : 'tos', + 'parameters.ip.ttl' : 'ttl', + 'parameters.ipv6.flowlabel' : 'flowlabel', + } + + cmd = 'ip link add name {ifname} type {type} id {vni} remote {remote}' + for vyos_key, iproute2_key in mapping.items(): + # dict_search will return an empty dict "{}" for valueless nodes like + # "parameters.nolearning" - thus we need to test the nodes existence + # by using isinstance() + tmp = dict_search(vyos_key, self.config) + if isinstance(tmp, dict): + cmd += f' {iproute2_key}' + elif tmp != None: + cmd += f' {iproute2_key} {tmp}' + self._cmd(cmd.format(**self.config)) # interface is always A/D down. It needs to be enabled explicitly self.set_admin_state('down') - - def update(self, config): - """ General helper function which works on a dictionary retrived by - get_config_dict(). It's main intention is to consolidate the scattered - interface setup code and provide a single point of entry when workin - on any interface. """ - - # call base class first - super().update(config) - - # Enable/Disable of an interface must always be done at the end of the - # derived class to make use of the ref-counting set_admin_state() - # function. We will only enable the interface if 'up' was called as - # often as 'down'. This is required by some interface implementations - # as certain parameters can only be changed when the interface is - # in admin-down state. This ensures the link does not flap during - # reconfiguration. - state = 'down' if 'disable' in config else 'up' - self.set_admin_state(state) diff --git a/python/vyos/ifconfig/input.py b/python/vyos/ifconfig/input.py index a6e566d87..db7d2b6b4 100644 --- a/python/vyos/ifconfig/input.py +++ b/python/vyos/ifconfig/input.py @@ -17,9 +17,6 @@ from vyos.ifconfig.interface import Interface @Interface.register class InputIf(Interface): - default = { - 'type': '', - } definition = { **Interface.definition, **{ diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 4c05ac613..ff05cab0e 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -1,4 +1,4 @@ -# Copyright 2019-2020 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -60,7 +60,6 @@ class Interface(Control): options = ['debug', 'create'] required = [] default = { - 'type': '', 'debug': True, 'create': True, } @@ -79,6 +78,14 @@ class Interface(Control): 'shellcmd': 'ip -json link show dev {ifname}', 'format': lambda j: 'up' if 'UP' in jmespath.search('[*].flags | [0]', json.loads(j)) else 'down', }, + 'alias': { + 'shellcmd': 'ip -json -detail link list dev {ifname}', + 'format': lambda j: jmespath.search('[*].ifalias | [0]', json.loads(j)) or '', + }, + 'mac': { + 'shellcmd': 'ip -json -detail link list dev {ifname}', + 'format': lambda j: jmespath.search('[*].address | [0]', json.loads(j)), + }, 'min_mtu': { 'shellcmd': 'ip -json -detail link list dev {ifname}', 'format': lambda j: jmespath.search('[*].min_mtu | [0]', json.loads(j)), @@ -87,6 +94,14 @@ class Interface(Control): 'shellcmd': 'ip -json -detail link list dev {ifname}', 'format': lambda j: jmespath.search('[*].max_mtu | [0]', json.loads(j)), }, + 'mtu': { + 'shellcmd': 'ip -json -detail link list dev {ifname}', + 'format': lambda j: jmespath.search('[*].mtu | [0]', json.loads(j)), + }, + 'oper_state': { + 'shellcmd': 'ip -json -detail link list dev {ifname}', + 'format': lambda j: jmespath.search('[*].operstate | [0]', json.loads(j)), + }, } _command_set = { @@ -94,40 +109,29 @@ class Interface(Control): 'validate': lambda v: assert_list(v, ['up', 'down']), 'shellcmd': 'ip link set dev {ifname} {value}', }, + 'alias': { + 'convert': lambda name: name if name else '', + 'shellcmd': 'ip link set dev {ifname} alias "{value}"', + }, + 'bridge_port_isolation': { + 'validate': lambda v: assert_list(v, ['on', 'off']), + 'shellcmd': 'bridge link set dev {ifname} isolated {value}', + }, 'mac': { 'validate': assert_mac, 'shellcmd': 'ip link set dev {ifname} address {value}', }, + 'mtu': { + 'validate': assert_mtu, + 'shellcmd': 'ip link set dev {ifname} mtu {value}', + }, 'vrf': { 'convert': lambda v: f'master {v}' if v else 'nomaster', 'shellcmd': 'ip link set dev {ifname} {value}', }, } - _sysfs_get = { - 'alias': { - 'location': '/sys/class/net/{ifname}/ifalias', - }, - 'mac': { - 'location': '/sys/class/net/{ifname}/address', - }, - 'mtu': { - 'location': '/sys/class/net/{ifname}/mtu', - }, - 'oper_state':{ - 'location': '/sys/class/net/{ifname}/operstate', - }, - } - _sysfs_set = { - 'alias': { - 'convert': lambda name: name if name else '\0', - 'location': '/sys/class/net/{ifname}/ifalias', - }, - 'mtu': { - 'validate': assert_mtu, - 'location': '/sys/class/net/{ifname}/mtu', - }, 'arp_cache_tmo': { 'convert': lambda tmo: (int(tmo) * 1000), 'location': '/proc/sys/net/ipv4/neigh/{ifname}/base_reachable_time_ms', @@ -231,26 +235,21 @@ class Interface(Control): >>> from vyos.ifconfig import Interface >>> i = Interface('eth0') """ + self.config = deepcopy(kargs) + self.config['ifname'] = self.ifname = ifname - self.config = deepcopy(self.default) - for k in self.options: - if k in kargs: - self.config[k] = kargs[k] - - # make sure the ifname is the first argument and not from the dict - self.config['ifname'] = ifname self._admin_state_down_cnt = 0 # we must have updated config before initialising the Interface super().__init__(**kargs) - self.ifname = ifname if not self.exists(ifname): - # Any instance of Interface, such as Interface('eth0') - # can be used safely to access the generic function in this class - # as 'type' is unset, the class can not be created - if not self.config['type']: + # Any instance of Interface, such as Interface('eth0') can be used + # safely to access the generic function in this class as 'type' is + # unset, the class can not be created + if not self.iftype: raise Exception(f'interface "{ifname}" not found') + self.config['type'] = self.iftype # Should an Instance of a child class (EthernetIf, DummyIf, ..) # be required, then create should be set to False to not accidentally create it. @@ -694,6 +693,20 @@ class Interface(Control): """ self.set_interface('path_priority', priority) + def set_port_isolation(self, on_or_off): + """ + Controls whether a given port will be isolated, which means it will be + able to communicate with non-isolated ports only. By default this flag + is off. + + Use enable=1 to enable or enable=0 to disable + + Example: + >>> from vyos.ifconfig import Interface + >>> Interface('eth1').set_port_isolation('on') + """ + self.set_interface('bridge_port_isolation', on_or_off) + def set_proxy_arp(self, enable): """ Set per interface proxy ARP configuration @@ -899,49 +912,42 @@ class Interface(Control): if 'priority' in bridge_config: self.set_path_cost(bridge_config['priority']) - vlan_filter = 0 - vlan_add = set() - - del_ifname_vlan_ids = get_vlan_ids(ifname) bridge_vlan_filter = Section.klass(bridge)(bridge, create=True).get_vlan_filter() - if bridge_vlan_filter: - if 1 in del_ifname_vlan_ids: - del_ifname_vlan_ids.remove(1) - vlan_filter = 1 - - for vlan in del_ifname_vlan_ids: - cmd = f'bridge vlan del dev {ifname} vid {vlan}' - self._cmd(cmd) - - if 'native_vlan' in bridge_config: - vlan_filter = 1 - cmd = f'bridge vlan del dev {self.ifname} vid 1' - self._cmd(cmd) - vlan_id = bridge_config['native_vlan'] - cmd = f'bridge vlan add dev {self.ifname} vid {vlan_id} pvid untagged master' - self._cmd(cmd) - vlan_add.add(vlan_id) - - if 'allowed_vlan' in bridge_config: - vlan_filter = 1 - if 'native_vlan' not in bridge_config: - cmd = f'bridge vlan del dev {self.ifname} vid 1' + if int(bridge_vlan_filter): + cur_vlan_ids = get_vlan_ids(ifname) + add_vlan = [] + native_vlan_id = None + allowed_vlan_ids= [] + + if 'native_vlan' in bridge_config: + vlan_id = bridge_config['native_vlan'] + add_vlan.append(vlan_id) + native_vlan_id = vlan_id + + if 'allowed_vlan' in bridge_config: + for vlan in bridge_config['allowed_vlan']: + vlan_range = vlan.split('-') + if len(vlan_range) == 2: + for vlan_add in range(int(vlan_range[0]),int(vlan_range[1]) + 1): + add_vlan.append(str(vlan_add)) + allowed_vlan_ids.append(str(vlan_add)) + else: + add_vlan.append(vlan) + allowed_vlan_ids.append(vlan) + + # Remove redundant VLANs from the system + for vlan in list_diff(cur_vlan_ids, add_vlan): + cmd = f'bridge vlan del dev {ifname} vid {vlan} master' self._cmd(cmd) - for vlan in bridge_config['allowed_vlan']: - cmd = f'bridge vlan add dev {self.ifname} vid {vlan} master' - self._cmd(cmd) - vlan_add.add(vlan) - if vlan_filter: - # Setting VLAN ID for the bridge - for vlan in vlan_add: - cmd = f'bridge vlan add dev {bridge} vid {vlan} self' + for vlan in allowed_vlan_ids: + cmd = f'bridge vlan add dev {ifname} vid {vlan} master' + self._cmd(cmd) + # Setting native VLAN to system + if native_vlan_id: + cmd = f'bridge vlan add dev {ifname} vid {native_vlan_id} pvid untagged master' self._cmd(cmd) - - # enable/disable Vlan Filter - # When the VLAN aware option is not detected, the setting of `bridge` should not be overwritten - Section.klass(bridge)(bridge, create=True).set_vlan_filter(vlan_filter) def set_dhcp(self, enable): """ @@ -957,6 +963,9 @@ class Interface(Control): pid_file = f'{config_base}_{ifname}.pid' lease_file = f'{config_base}_{ifname}.leases' + # Stop client with old config files to get the right IF_METRIC. + self._cmd(f'systemctl stop dhclient@{ifname}.service') + if enable and 'disable' not in self._config: if dict_search('dhcp_options.host_name', self._config) == None: # read configured system hostname. @@ -975,10 +984,8 @@ class Interface(Control): # 'up' check is mandatory b/c even if the interface is A/D, as soon as # the DHCP client is started the interface will be placed in u/u state. # This is not what we intended to do when disabling an interface. - return self._cmd(f'systemctl restart dhclient@{ifname}.service') + return self._cmd(f'systemctl start dhclient@{ifname}.service') else: - self._cmd(f'systemctl stop dhclient@{ifname}.service') - # cleanup old config files for file in [config_file, options_file, pid_file, lease_file]: if os.path.isfile(file): @@ -1021,9 +1028,11 @@ class Interface(Control): source_if = next(iter(self._config['is_mirror_intf'])) config = self._config['is_mirror_intf'][source_if].get('mirror', None) + # Please do not clear the 'set $? = 0 '. It's meant to force a return of 0 # Remove existing mirroring rules - delete_tc_cmd = f'tc qdisc del dev {source_if} handle ffff: ingress; ' - delete_tc_cmd += f'tc qdisc del dev {source_if} handle 1: root prio' + delete_tc_cmd = f'tc qdisc del dev {source_if} handle ffff: ingress 2> /dev/null;' + delete_tc_cmd += f'tc qdisc del dev {source_if} handle 1: root prio 2> /dev/null;' + delete_tc_cmd += 'set $?=0' self._popen(delete_tc_cmd) # Bail out early if nothing needs to be configured @@ -1060,6 +1069,10 @@ class Interface(Control): if not isinstance(state, bool): raise ValueError("Value out of range") + # https://phabricator.vyos.net/T3448 - there is (yet) no RPI support for XDP + if not os.path.exists('/usr/sbin/xdp_loader'): + return + ifname = self.config['ifname'] cmd = f'xdp_loader -d {ifname} -U --auto-mode' if state: @@ -1078,6 +1091,10 @@ class Interface(Control): interface setup code and provide a single point of entry when workin on any interface. """ + if self.debug: + import pprint + pprint.pprint(config) + # Cache the configuration - it will be reused inside e.g. DHCP handler # XXX: maybe pass the option via __init__ in the future and rename this # method to apply()? @@ -1108,9 +1125,10 @@ class Interface(Control): self.del_addr('dhcp') # always ensure DHCPv6 client is stopped (when not configured as client - # for IPv6 address or prefix delegation + # for IPv6 address or prefix delegation) dhcpv6pd = dict_search('dhcpv6_options.pd', config) - if 'dhcpv6' not in new_addr or dhcpv6pd == None: + dhcpv6pd = dhcpv6pd != None and len(dhcpv6pd) != 0 + if 'dhcpv6' not in new_addr and not dhcpv6pd: self.del_addr('dhcpv6') # determine IP addresses which are assigned to the interface and build a @@ -1130,7 +1148,7 @@ class Interface(Control): self.add_addr(addr) # start DHCPv6 client when only PD was configured - if dhcpv6pd != None: + if dhcpv6pd: self.set_dhcpv6(True) # There are some items in the configuration which can only be applied @@ -1246,6 +1264,16 @@ class Interface(Control): # configure port mirror self.set_mirror() + # Enable/Disable of an interface must always be done at the end of the + # derived class to make use of the ref-counting set_admin_state() + # function. We will only enable the interface if 'up' was called as + # often as 'down'. This is required by some interface implementations + # as certain parameters can only be changed when the interface is + # in admin-down state. This ensures the link does not flap during + # reconfiguration. + state = 'down' if 'disable' in config else 'up' + self.set_admin_state(state) + # remove no longer required 802.1ad (Q-in-Q VLANs) ifname = config['ifname'] for vif_s_id in config.get('vif_s_remove', {}): @@ -1299,38 +1327,7 @@ class Interface(Control): class VLANIf(Interface): """ Specific class which abstracts 802.1q and 802.1ad (Q-in-Q) VLAN interfaces """ - default = { - 'type': 'vlan', - 'source_interface': '', - 'vlan_id': '', - 'protocol': '', - 'ingress_qos': '', - 'egress_qos': '', - } - - options = Interface.options + \ - ['source_interface', 'vlan_id', 'protocol', 'ingress_qos', 'egress_qos'] - - def remove(self): - """ - Remove interface from operating system. Removing the interface - deconfigures all assigned IP addresses and clear possible DHCP(v6) - client processes. - - Example: - >>> from vyos.ifconfig import Interface - >>> VLANIf('eth0.10').remove - """ - # Do we have sub interfaces (VLANs)? As interfaces need to be deleted - # "in order" starting from Q-in-Q we delete them first. - for upper in glob(f'/sys/class/net/{self.ifname}/upper*'): - # an upper interface could be named: upper_bond0.1000.1100, thus - # we need top drop the upper_ prefix - vif_c = os.path.basename(upper) - vif_c = vif_c.replace('upper_', '') - VLANIf(vif_c).remove() - - super().remove() + iftype = 'vlan' def _create(self): # bail out early if interface already exists @@ -1338,11 +1335,11 @@ class VLANIf(Interface): return cmd = 'ip link add link {source_interface} name {ifname} type vlan id {vlan_id}' - if self.config['protocol']: + if 'protocol' in self.config: cmd += ' protocol {protocol}' - if self.config['ingress_qos']: + if 'ingress_qos' in self.config: cmd += ' ingress-qos-map {ingress_qos}' - if self.config['egress_qos']: + if 'egress_qos' in self.config: cmd += ' egress-qos-map {egress_qos}' self._cmd(cmd.format(**self.config)) @@ -1374,22 +1371,3 @@ class VLANIf(Interface): def set_mirror(self): return - - def update(self, config): - """ General helper function which works on a dictionary retrived by - get_config_dict(). It's main intention is to consolidate the scattered - interface setup code and provide a single point of entry when workin - on any interface. """ - - # call base class first - super().update(config) - - # Enable/Disable of an interface must always be done at the end of the - # derived class to make use of the ref-counting set_admin_state() - # function. We will only enable the interface if 'up' was called as - # often as 'down'. This is required by some interface implementations - # as certain parameters can only be changed when the interface is - # in admin-down state. This ensures the link does not flap during - # reconfiguration. - state = 'down' if 'disable' in config else 'up' - self.set_admin_state(state) diff --git a/python/vyos/ifconfig/l2tpv3.py b/python/vyos/ifconfig/l2tpv3.py index 8ed3d5afb..7ff0fdd0e 100644 --- a/python/vyos/ifconfig/l2tpv3.py +++ b/python/vyos/ifconfig/l2tpv3.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -24,19 +24,7 @@ class L2TPv3If(Interface): either hot standby or load balancing services. Additionally, link integrity monitoring may be performed. """ - - default = { - 'type': 'l2tp', - 'peer_tunnel_id': '', - 'local_port': 0, - 'remote_port': 0, - 'encapsulation': 'udp', - 'local_address': '', - 'remote_address': '', - 'session_id': '', - 'tunnel_id': '', - 'peer_session_id': '' - } + iftype = 'l2tp' definition = { **Interface.definition, **{ @@ -45,20 +33,16 @@ class L2TPv3If(Interface): 'bridgeable': True, } } - options = Interface.options + \ - ['tunnel_id', 'peer_tunnel_id', 'local_port', 'remote_port', - 'encapsulation', 'local_address', 'remote_address', 'session_id', - 'peer_session_id'] def _create(self): # create tunnel interface cmd = 'ip l2tp add tunnel tunnel_id {tunnel_id}' cmd += ' peer_tunnel_id {peer_tunnel_id}' - cmd += ' udp_sport {local_port}' - cmd += ' udp_dport {remote_port}' + cmd += ' udp_sport {source_port}' + cmd += ' udp_dport {destination_port}' cmd += ' encap {encapsulation}' - cmd += ' local {local_address}' - cmd += ' remote {remote_address}' + cmd += ' local {source_address}' + cmd += ' remote {remote}' self._cmd(cmd.format(**self.config)) # setup session @@ -82,36 +66,15 @@ class L2TPv3If(Interface): >>> i.remove() """ - if self.exists(self.config['ifname']): + if self.exists(self.ifname): # interface is always A/D down. It needs to be enabled explicitly self.set_admin_state('down') - if self.config['tunnel_id'] and self.config['session_id']: + if {'tunnel_id', 'session_id'} <= set(self.config): cmd = 'ip l2tp del session tunnel_id {tunnel_id}' cmd += ' session_id {session_id}' self._cmd(cmd.format(**self.config)) - if self.config['tunnel_id']: + if 'tunnel_id' in self.config: cmd = 'ip l2tp del tunnel tunnel_id {tunnel_id}' self._cmd(cmd.format(**self.config)) - - - def update(self, config): - """ General helper function which works on a dictionary retrived by - get_config_dict(). It's main intention is to consolidate the scattered - interface setup code and provide a single point of entry when workin - on any interface. """ - - # call base class first - super().update(config) - - # Enable/Disable of an interface must always be done at the end of the - # derived class to make use of the ref-counting set_admin_state() - # function. We will only enable the interface if 'up' was called as - # often as 'down'. This is required by some interface implementations - # as certain parameters can only be changed when the interface is - # in admin-down state. This ensures the link does not flap during - # reconfiguration. - state = 'down' if 'disable' in config else 'up' - self.set_admin_state(state) - diff --git a/python/vyos/ifconfig/loopback.py b/python/vyos/ifconfig/loopback.py index 0e632d826..192c12f5c 100644 --- a/python/vyos/ifconfig/loopback.py +++ b/python/vyos/ifconfig/loopback.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -22,9 +22,8 @@ class LoopbackIf(Interface): uses to communicate with itself. """ _persistent_addresses = ['127.0.0.1/8', '::1/128'] - default = { - 'type': 'loopback', - } + iftype = 'loopback' + definition = { **Interface.definition, **{ @@ -33,9 +32,6 @@ class LoopbackIf(Interface): 'bridgeable': True, } } - - name = 'loopback' - def remove(self): """ Loopback interface can not be deleted from operating system. We can @@ -70,13 +66,3 @@ class LoopbackIf(Interface): # call base class super().update(config) - - # Enable/Disable of an interface must always be done at the end of the - # derived class to make use of the ref-counting set_admin_state() - # function. We will only enable the interface if 'up' was called as - # often as 'down'. This is required by some interface implementations - # as certain parameters can only be changed when the interface is - # in admin-down state. This ensures the link does not flap during - # reconfiguration. - state = 'down' if 'disable' in config else 'up' - self.set_admin_state(state) diff --git a/python/vyos/ifconfig/macsec.py b/python/vyos/ifconfig/macsec.py index 456686ea6..1a78d18d8 100644 --- a/python/vyos/ifconfig/macsec.py +++ b/python/vyos/ifconfig/macsec.py @@ -1,4 +1,4 @@ -# Copyright 2020 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2020-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -27,12 +27,7 @@ class MACsecIf(Interface): other security solutions such as IPsec (layer 3) or TLS (layer 4), as all those solutions are used for their own specific use cases. """ - - default = { - 'type': 'macsec', - 'security_cipher': '', - 'source_interface': '' - } + iftype = 'macsec' definition = { **Interface.definition, **{ @@ -40,8 +35,6 @@ class MACsecIf(Interface): 'prefixes': ['macsec', ], }, } - options = Interface.options + \ - ['security_cipher', 'source_interface'] def _create(self): """ @@ -49,28 +42,9 @@ class MACsecIf(Interface): down by default. """ # create tunnel interface - cmd = 'ip link add link {source_interface} {ifname} type {type}' - cmd += ' cipher {security_cipher}' - self._cmd(cmd.format(**self.config)) + cmd = 'ip link add link {source_interface} {ifname} type {type}'.format(**self.config) + cmd += f' cipher {self.config["security"]["cipher"]}' + self._cmd(cmd) # interface is always A/D down. It needs to be enabled explicitly self.set_admin_state('down') - - def update(self, config): - """ General helper function which works on a dictionary retrived by - get_config_dict(). It's main intention is to consolidate the scattered - interface setup code and provide a single point of entry when workin - on any interface. """ - - # call base class first - super().update(config) - - # Enable/Disable of an interface must always be done at the end of the - # derived class to make use of the ref-counting set_admin_state() - # function. We will only enable the interface if 'up' was called as - # often as 'down'. This is required by some interface implementations - # as certain parameters can only be changed when the interface is - # in admin-down state. This ensures the link does not flap during - # reconfiguration. - state = 'down' if 'disable' in config else 'up' - self.set_admin_state(state) diff --git a/python/vyos/ifconfig/macvlan.py b/python/vyos/ifconfig/macvlan.py index 2447fec77..776014bc3 100644 --- a/python/vyos/ifconfig/macvlan.py +++ b/python/vyos/ifconfig/macvlan.py @@ -1,4 +1,4 @@ -# Copyright 2019-2020 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -20,13 +20,7 @@ class MACVLANIf(Interface): """ Abstraction of a Linux MACvlan interface """ - - default = { - 'type': 'macvlan', - 'address': '', - 'source_interface': '', - 'mode': '', - } + iftype = 'macvlan' definition = { **Interface.definition, **{ @@ -34,39 +28,13 @@ class MACVLANIf(Interface): 'prefixes': ['peth', ], }, } - options = Interface.options + \ - ['source_interface', 'mode'] def _create(self): # please do not change the order when assembling the command - cmd = 'ip link add {ifname}' - if self.config['source_interface']: - cmd += ' link {source_interface}' - cmd += ' type macvlan' - if self.config['mode']: - cmd += ' mode {mode}' + cmd = 'ip link add {ifname} link {source_interface} type {type} mode {mode}' self._cmd(cmd.format(**self.config)) def set_mode(self, mode): ifname = self.config['ifname'] cmd = f'ip link set dev {ifname} type macvlan mode {mode}' return self._cmd(cmd) - - def update(self, config): - """ General helper function which works on a dictionary retrived by - get_config_dict(). It's main intention is to consolidate the scattered - interface setup code and provide a single point of entry when workin - on any interface. """ - - # call base class first - super().update(config) - - # Enable/Disable of an interface must always be done at the end of the - # derived class to make use of the ref-counting set_admin_state() - # function. We will only enable the interface if 'up' was called as - # often as 'down'. This is required by some interface implementations - # as certain parameters can only be changed when the interface is - # in admin-down state. This ensures the link does not flap during - # reconfiguration. - state = 'down' if 'disable' in config else 'up' - self.set_admin_state(state) diff --git a/python/vyos/ifconfig/pppoe.py b/python/vyos/ifconfig/pppoe.py index 787245696..65575cf99 100644 --- a/python/vyos/ifconfig/pppoe.py +++ b/python/vyos/ifconfig/pppoe.py @@ -13,10 +13,8 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see <http://www.gnu.org/licenses/>. - from vyos.ifconfig.interface import Interface - @Interface.register class PPPoEIf(Interface): default = { diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py index 00dc36420..e5e1300b2 100644 --- a/python/vyos/ifconfig/tunnel.py +++ b/python/vyos/ifconfig/tunnel.py @@ -1,4 +1,4 @@ -# Copyright 2019-2020 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -16,13 +16,12 @@ # https://developers.redhat.com/blog/2019/05/17/an-introduction-to-linux-virtual-interfaces-tunnels/ # https://community.hetzner.com/tutorials/linux-setup-gre-tunnel -from copy import deepcopy - from netaddr import EUI from netaddr import mac_unix_expanded from random import getrandbits from vyos.ifconfig.interface import Interface +from vyos.util import dict_search from vyos.validate import assert_list def enable_to_on(value): @@ -32,11 +31,10 @@ def enable_to_on(value): return 'off' raise ValueError(f'expect enable or disable but got "{value}"') - @Interface.register -class _Tunnel(Interface): +class TunnelIf(Interface): """ - _Tunnel: private base class for tunnels + Tunnel: private base class for tunnels https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/tunnel.c https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/ip6tunnel.c """ @@ -48,45 +46,111 @@ class _Tunnel(Interface): }, } + # This table represents a mapping from VyOS internal config dict to + # arguments used by iproute2. For more information please refer to: + # - https://man7.org/linux/man-pages/man8/ip-link.8.html + # - https://man7.org/linux/man-pages/man8/ip-tunnel.8.html + mapping = { + 'source_address' : 'local', + 'source_interface' : 'dev', + 'remote' : 'remote', + 'parameters.ip.key' : 'key', + 'parameters.ip.tos' : 'tos', + 'parameters.ip.ttl' : 'ttl', + } + mapping_ipv4 = { + 'parameters.ip.key' : 'key', + 'parameters.ip.no_pmtu_discovery' : 'nopmtudisc', + 'parameters.ip.tos' : 'tos', + 'parameters.ip.ttl' : 'ttl', + } + mapping_ipv6 = { + 'parameters.ipv6.encaplimit' : 'encaplimit', + 'parameters.ipv6.flowlabel' : 'flowlabel', + 'parameters.ipv6.hoplimit' : 'hoplimit', + 'parameters.ipv6.tclass' : 'tclass', + } + # TODO: This is surely used for more than tunnels # TODO: could be refactored elsewhere - _command_set = {**Interface._command_set, **{ - 'multicast': { - 'validate': lambda v: assert_list(v, ['enable', 'disable']), - 'convert': enable_to_on, - 'shellcmd': 'ip link set dev {ifname} multicast {value}', - }, - 'allmulticast': { - 'validate': lambda v: assert_list(v, ['enable', 'disable']), - 'convert': enable_to_on, - 'shellcmd': 'ip link set dev {ifname} allmulticast {value}', - }, - }} + _command_set = { + **Interface._command_set, + **{ + 'multicast': { + 'validate': lambda v: assert_list(v, ['enable', 'disable']), + 'convert': enable_to_on, + 'shellcmd': 'ip link set dev {ifname} multicast {value}', + }, + 'allmulticast': { + 'validate': lambda v: assert_list(v, ['enable', 'disable']), + 'convert': enable_to_on, + 'shellcmd': 'ip link set dev {ifname} allmulticast {value}', + }, + } + } - def __init__(self, ifname, **config): - self.config = deepcopy(config) if config else {} - super().__init__(ifname, **config) + def __init__(self, ifname, **kargs): + # T3357: we do not have the 'encapsulation' in kargs when calling this + # class from op-mode like "show interfaces tunnel" + if 'encapsulation' in kargs: + self.iftype = kargs['encapsulation'] + # The gretap interface has the possibility to act as L2 bridge + if self.iftype in ['gretap', 'ip6gretap']: + # no multicast, ttl or tos for gretap + self.definition = { + **TunnelIf.definition, + **{ + 'bridgeable': True, + }, + } + + super().__init__(ifname, **kargs) def _create(self): - create = 'ip tunnel add {ifname} mode {type}' + if self.config['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre']: + mapping = { **self.mapping, **self.mapping_ipv6 } + else: + mapping = { **self.mapping, **self.mapping_ipv4 } + + cmd = 'ip tunnel add {ifname} mode {encapsulation}' + if self.iftype in ['gretap', 'ip6gretap']: + cmd = 'ip link add name {ifname} type {encapsulation}' + for vyos_key, iproute2_key in mapping.items(): + # dict_search will return an empty dict "{}" for valueless nodes like + # "parameters.nolearning" - thus we need to test the nodes existence + # by using isinstance() + tmp = dict_search(vyos_key, self.config) + if isinstance(tmp, dict): + cmd += f' {iproute2_key}' + elif tmp != None: + cmd += f' {iproute2_key} {tmp}' + + self._cmd(cmd.format(**self.config)) - # add " option-name option-name-value ..." for all options set - options = " ".join(["{} {}".format(k, self.config[k]) - for k in self.options if k in self.config and self.config[k]]) - self._cmd('{} {}'.format(create.format(**self.config), options)) self.set_admin_state('down') - def change_options(self): - change = 'ip tunnel cha {ifname} mode {type}' - - # add " option-name option-name-value ..." for all options set - options = " ".join(["{} {}".format(k, self.config[k]) - for k in self.options if k in self.config and self.config[k]]) - self._cmd('{} {}'.format(change.format(**self.config), options)) - - @classmethod - def get_config(cls): - return dict(zip(cls.options, ['']*len(cls.options))) + def _change_options(self): + # gretap interfaces do not support changing any parameter + if self.iftype in ['gretap', 'ip6gretap']: + return + + if self.config['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre']: + mapping = { **self.mapping, **self.mapping_ipv6 } + else: + mapping = { **self.mapping, **self.mapping_ipv4 } + + cmd = 'ip tunnel change {ifname} mode {encapsulation}' + for vyos_key, iproute2_key in mapping.items(): + # dict_search will return an empty dict "{}" for valueless nodes like + # "parameters.nolearning" - thus we need to test the nodes existence + # by using isinstance() + tmp = dict_search(vyos_key, self.config) + if isinstance(tmp, dict): + cmd += f' {iproute2_key}' + elif tmp != None: + cmd += f' {iproute2_key} {tmp}' + + self._cmd(cmd.format(**self.config)) def get_mac(self): """ @@ -117,130 +181,8 @@ class _Tunnel(Interface): get_config_dict(). It's main intention is to consolidate the scattered interface setup code and provide a single point of entry when workin on any interface. """ + # Adjust iproute2 tunnel parameters if necessary + self._change_options() # call base class first super().update(config) - - # Enable/Disable of an interface must always be done at the end of the - # derived class to make use of the ref-counting set_admin_state() - # function. We will only enable the interface if 'up' was called as - # often as 'down'. This is required by some interface implementations - # as certain parameters can only be changed when the interface is - # in admin-down state. This ensures the link does not flap during - # reconfiguration. - state = 'down' if 'disable' in config else 'up' - self.set_admin_state(state) - -class GREIf(_Tunnel): - """ - GRE: Generic Routing Encapsulation - - For more information please refer to: - RFC1701, RFC1702, RFC2784 - https://tools.ietf.org/html/rfc2784 - https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_gre.c - """ - - default = {'type': 'gre'} - options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key'] - -# GreTap also called GRE Bridge -class GRETapIf(_Tunnel): - """ - GRETapIF: GreIF using TAP instead of TUN - - https://en.wikipedia.org/wiki/TUN/TAP - """ - - # no multicast, ttl or tos for gretap - - definition = { - **_Tunnel.definition, - **{ - 'bridgeable': True, - }, - } - - default = {'type': 'gretap'} - options = ['local', 'remote', 'ttl',] - -class IP6GREIf(_Tunnel): - """ - IP6Gre: IPv6 Support for Generic Routing Encapsulation (GRE) - - For more information please refer to: - https://tools.ietf.org/html/rfc7676 - https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_gre6.c - """ - - default = {'type': 'ip6gre'} - options = ['local', 'remote', 'dev', 'encaplimit', - 'hoplimit', 'tclass', 'flowlabel'] - -class IPIPIf(_Tunnel): - """ - IPIP: IP Encapsulation within IP - - For more information please refer to: - https://tools.ietf.org/html/rfc2003 - """ - - # IPIP does not allow to pass multicast, unlike GRE - # but the interface itself can be set with multicast - - default = {'type': 'ipip'} - options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key'] - -class IPIP6If(_Tunnel): - """ - IPIP6: IPv4 over IPv6 tunnel - - For more information please refer to: - https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_ip6tnl.c - """ - - default = {'type': 'ipip6'} - options = ['local', 'remote', 'dev', 'encaplimit', - 'hoplimit', 'tclass', 'flowlabel'] - -class IP6IP6If(IPIP6If): - """ - IP6IP6: IPv6 over IPv6 tunnel - - For more information please refer to: - https://tools.ietf.org/html/rfc2473 - """ - default = {'type': 'ip6ip6'} - - -class SitIf(_Tunnel): - """ - Sit: Simple Internet Transition - - For more information please refer to: - https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_iptnl.c - """ - - default = {'type': 'sit'} - options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key'] - -class Sit6RDIf(SitIf): - """ - Sit6RDIf: Simple Internet Transition with 6RD - - https://en.wikipedia.org/wiki/IPv6_rapid_deployment - """ - # TODO: check if key can really be used with 6RD - options = ['remote', 'ttl', 'tos', 'key', '6rd-prefix', '6rd-relay-prefix'] - - def _create(self): - # do not call _Tunnel.create, building fully here - - create = 'ip tunnel add {ifname} mode {type} remote {remote}' - self._cmd(create.format(**self.config)) - self.set_interface('state','down') - - set6rd = 'ip tunnel 6rd dev {ifname} 6rd-prefix {6rd-prefix}' - if '6rd-relay-prefix' in self.config: - set6rd += ' 6rd-relay-prefix {6rd-relay-prefix}' - self._cmd(set6rd.format(**self.config)) diff --git a/python/vyos/ifconfig/vti.py b/python/vyos/ifconfig/vti.py index d0745898c..e2090c889 100644 --- a/python/vyos/ifconfig/vti.py +++ b/python/vyos/ifconfig/vti.py @@ -1,4 +1,4 @@ -# Copyright 2020 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,9 +17,7 @@ from vyos.ifconfig.interface import Interface @Interface.register class VTIIf(Interface): - default = { - 'type': 'vti', - } + iftype = 'vti' definition = { **Interface.definition, **{ diff --git a/python/vyos/ifconfig/vtun.py b/python/vyos/ifconfig/vtun.py index 99a592b3e..6fb414e56 100644 --- a/python/vyos/ifconfig/vtun.py +++ b/python/vyos/ifconfig/vtun.py @@ -1,4 +1,4 @@ -# Copyright 2020 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2020-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,10 +17,7 @@ from vyos.ifconfig.interface import Interface @Interface.register class VTunIf(Interface): - default = { - 'type': 'vtun', - 'device_type': 'tun', - } + iftype = 'vtun' definition = { **Interface.definition, **{ @@ -29,7 +26,6 @@ class VTunIf(Interface): 'bridgeable': True, }, } - options = Interface.options + ['device_type'] def _create(self): """ Depending on OpenVPN operation mode the interface is created @@ -51,22 +47,3 @@ class VTunIf(Interface): def del_addr(self, addr): # IP addresses are managed by OpenVPN daemon pass - - def update(self, config): - """ General helper function which works on a dictionary retrived by - get_config_dict(). It's main intention is to consolidate the scattered - interface setup code and provide a single point of entry when workin - on any interface. """ - - # call base class first - super().update(config) - - # Enable/Disable of an interface must always be done at the end of the - # derived class to make use of the ref-counting set_admin_state() - # function. We will only enable the interface if 'up' was called as - # often as 'down'. This is required by some interface implementations - # as certain parameters can only be changed when the interface is - # in admin-down state. This ensures the link does not flap during - # reconfiguration. - state = 'down' if 'disable' in config else 'up' - self.set_admin_state(state) diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py index ad1f605ed..d73fb47b8 100644 --- a/python/vyos/ifconfig/vxlan.py +++ b/python/vyos/ifconfig/vxlan.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -14,7 +14,8 @@ # License along with this library. If not, see <http://www.gnu.org/licenses/>. from vyos import ConfigError -from vyos.ifconfig.interface import Interface +from vyos.ifconfig import Interface +from vyos.util import dict_search @Interface.register class VXLANIf(Interface): @@ -38,16 +39,7 @@ class VXLANIf(Interface): https://www.kernel.org/doc/Documentation/networking/vxlan.txt """ - default = { - 'type': 'vxlan', - 'group': '', - 'port': 8472, # The Linux implementation of VXLAN pre-dates - # the IANA's selection of a standard destination port - 'remote': '', - 'source_address': '', - 'source_interface': '', - 'vni': 0 - } + iftype = 'vxlan' definition = { **Interface.definition, **{ @@ -56,60 +48,34 @@ class VXLANIf(Interface): 'bridgeable': True, } } - options = Interface.options + \ - ['group', 'remote', 'source_interface', 'port', 'vni', 'source_address'] - - mapping = { - 'ifname': 'add', - 'vni': 'id', - 'port': 'dstport', - 'source_address': 'local', - 'source_interface': 'dev', - } def _create(self): - cmdline = ['ifname', 'type', 'vni', 'port'] - - if self.config['source_address']: - cmdline.append('source_address') - - if self.config['remote']: - cmdline.append('remote') - - if self.config['group'] or self.config['source_interface']: - if self.config['group'] and self.config['source_interface']: - cmdline.append('group') - cmdline.append('source_interface') - else: - ifname = self.config['ifname'] - raise ConfigError( - f'VXLAN "{ifname}" is missing mandatory underlay multicast' - 'group or source interface for a multicast network.') - - cmd = 'ip link' - for key in cmdline: - value = self.config.get(key, '') - if not value: - continue - cmd += ' {} {}'.format(self.mapping.get(key, key), value) - - self._cmd(cmd) - - def update(self, config): - """ General helper function which works on a dictionary retrived by - get_config_dict(). It's main intention is to consolidate the scattered - interface setup code and provide a single point of entry when workin - on any interface. """ - - # call base class first - super().update(config) + # This table represents a mapping from VyOS internal config dict to + # arguments used by iproute2. For more information please refer to: + # - https://man7.org/linux/man-pages/man8/ip-link.8.html + mapping = { + 'source_address' : 'local', + 'source_interface' : 'dev', + 'remote' : 'remote', + 'group' : 'group', + 'parameters.ip.dont_fragment': 'df set', + 'parameters.ip.tos' : 'tos', + 'parameters.ip.ttl' : 'ttl', + 'parameters.ipv6.flowlabel' : 'flowlabel', + 'parameters.nolearning' : 'nolearning', + } - # Enable/Disable of an interface must always be done at the end of the - # derived class to make use of the ref-counting set_admin_state() - # function. We will only enable the interface if 'up' was called as - # often as 'down'. This is required by some interface implementations - # as certain parameters can only be changed when the interface is - # in admin-down state. This ensures the link does not flap during - # reconfiguration. - state = 'down' if 'disable' in config else 'up' - self.set_admin_state(state) + cmd = 'ip link add {ifname} type {type} id {vni} dstport {port}' + for vyos_key, iproute2_key in mapping.items(): + # dict_search will return an empty dict "{}" for valueless nodes like + # "parameters.nolearning" - thus we need to test the nodes existence + # by using isinstance() + tmp = dict_search(vyos_key, self.config) + if isinstance(tmp, dict): + cmd += f' {iproute2_key}' + elif tmp != None: + cmd += f' {iproute2_key} {tmp}' + + self._cmd(cmd.format(**self.config)) + # interface is always A/D down. It needs to be enabled explicitly + self.set_admin_state('down') diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py index 9ee798ee8..e5b9c4408 100644 --- a/python/vyos/ifconfig/wireguard.py +++ b/python/vyos/ifconfig/wireguard.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -148,18 +148,7 @@ class WireGuardOperational(Operational): @Interface.register class WireGuardIf(Interface): OperationalClass = WireGuardOperational - - default = { - 'type': 'wireguard', - 'port': 0, - 'private_key': None, - 'pubkey': None, - 'psk': '', - 'allowed_ips': [], - 'fwmark': 0x00, - 'endpoint': None, - 'keepalive': 0 - } + iftype = 'wireguard' definition = { **Interface.definition, **{ @@ -168,9 +157,6 @@ class WireGuardIf(Interface): 'bridgeable': False, } } - options = Interface.options + \ - ['port', 'private_key', 'pubkey', 'psk', - 'allowed_ips', 'fwmark', 'endpoint', 'keepalive'] def get_mac(self): """ @@ -261,14 +247,3 @@ class WireGuardIf(Interface): # call base class super().update(config) - - # Enable/Disable of an interface must always be done at the end of the - # derived class to make use of the ref-counting set_admin_state() - # function. We will only enable the interface if 'up' was called as - # often as 'down'. This is required by some interface implementations - # as certain parameters can only be changed when the interface is - # in admin-down state. This ensures the link does not flap during - # reconfiguration. - state = 'down' if 'disable' in config else 'up' - self.set_admin_state(state) - diff --git a/python/vyos/ifconfig/wireless.py b/python/vyos/ifconfig/wireless.py index 37703d242..748b6e02d 100644 --- a/python/vyos/ifconfig/wireless.py +++ b/python/vyos/ifconfig/wireless.py @@ -1,4 +1,4 @@ -# Copyright 2020 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2020-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -20,11 +20,7 @@ class WiFiIf(Interface): """ Handle WIFI/WLAN interfaces. """ - - default = { - 'type': 'wifi', - 'phy': 'phy0' - } + iftype = 'wifi' definition = { **Interface.definition, **{ @@ -33,14 +29,10 @@ class WiFiIf(Interface): 'bridgeable': True, } } - options = Interface.options + \ - ['phy', 'op_mode'] - def _create(self): # all interfaces will be added in monitor mode - cmd = 'iw phy {phy} interface add {ifname} type monitor' \ - .format(**self.config) - self._cmd(cmd) + cmd = 'iw phy {physical_device} interface add {ifname} type monitor' + self._cmd(cmd.format(**self.config)) # wireless interface is administratively down by default self.set_admin_state('down') @@ -71,24 +63,3 @@ class WiFiIf(Interface): # re-add ourselves to any bridge we might have fallen out of if bridge_member: self.add_to_bridge(bridge_member) - - # Enable/Disable of an interface must always be done at the end of the - # derived class to make use of the ref-counting set_admin_state() - # function. We will only enable the interface if 'up' was called as - # often as 'down'. This is required by some interface implementations - # as certain parameters can only be changed when the interface is - # in admin-down state. This ensures the link does not flap during - # reconfiguration. - state = 'down' if 'disable' in config else 'up' - self.set_admin_state(state) - - -@Interface.register -class WiFiModemIf(WiFiIf): - definition = { - **WiFiIf.definition, - **{ - 'section': 'wirelessmodem', - 'prefixes': ['wlm', ], - } - } diff --git a/python/vyos/remote.py b/python/vyos/remote.py index 3f46d979b..18e772cc8 100644 --- a/python/vyos/remote.py +++ b/python/vyos/remote.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -13,131 +13,110 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see <http://www.gnu.org/licenses/>. -import sys import os -import re -import fileinput +import sys +import tempfile +from ftplib import FTP +import urllib.parse +import urllib.request from vyos.util import cmd -from vyos.util import DEVNULL - - -def check_and_add_host_key(host_name): +from paramiko import SSHClient + +def upload_ftp(local_path, hostname, remote_path,\ + username='anonymous', password='', port=21): + with open(local_path, 'rb') as file: + with FTP() as conn: + conn.connect(hostname, port) + conn.login(username, password) + conn.storbinary(f'STOR {remote_path}', file) + +def download_ftp(local_path, hostname, remote_path,\ + username='anonymous', password='', port=21): + with open(local_path, 'wb') as file: + with FTP() as conn: + conn.connect(hostname, port) + conn.login(username, password) + conn.retrbinary(f'RETR {remote_path}', file.write) + +def upload_sftp(local_path, hostname, remote_path,\ + username=None, password=None, port=22): + with SSHClient() as ssh: + ssh.load_system_host_keys() + ssh.connect(hostname, port, username, password) + with ssh.open_sftp() as sftp: + sftp.put(local_path, remote_path) + +def download_sftp(local_path, hostname, remote_path,\ + username=None, password=None, port=22): + with SSHClient() as ssh: + ssh.load_system_host_keys() + ssh.connect(hostname, port, username, password) + with ssh.open_sftp() as sftp: + sftp.get(remote_path, local_path) + +def upload_tftp(local_path, hostname, remote_path, port=69): + with open(local_path, 'rb') as file: + cmd(f'curl -s -T - tftp://{hostname}:{port}/{remote_path}', stderr=None, input=file.read()).encode() + +def download_tftp(local_path, hostname, remote_path, port=69): + with open(local_path, 'wb') as file: + file.write(cmd(f'curl -s tftp://{hostname}:{port}/{remote_path}', stderr=None).encode()) + +def download_http(urlstring, local_path): + with open(local_path, 'wb') as file: + with urllib.request.urlopen(urlstring) as response: + file.write(response.read()) + +def download(local_path, urlstring): """ - Filter host keys and prompt for adding key to known_hosts file, if - needed. + Dispatch the appropriate download function for the given URL and save to local path. """ - known_hosts = '{}/.ssh/known_hosts'.format(os.getenv('HOME')) - if not os.path.exists(known_hosts): - mode = 0o600 - os.mknod(known_hosts, 0o600) - - keyscan_cmd = 'ssh-keyscan -t rsa {}'.format(host_name) - - try: - host_key = cmd(keyscan_cmd, stderr=DEVNULL) - except OSError: - sys.exit("Can not get RSA host key") - - # libssh2 (jessie; stretch) does not recognize ec host keys, and curl - # will fail with error 51 if present in known_hosts file; limit to rsa. - usable_keys = False - offending_keys = [] - for line in fileinput.input(known_hosts, inplace=True): - if host_name in line and 'ssh-rsa' in line: - if line.split()[-1] != host_key.split()[-1]: - offending_keys.append(line) - continue - else: - usable_keys = True - if host_name in line and not 'ssh-rsa' in line: - continue - - sys.stdout.write(line) - - if usable_keys: - return - - if offending_keys: - print("Host key has changed!") - print("If you trust the host key fingerprint below, continue.") - - fingerprint_cmd = 'ssh-keygen -lf /dev/stdin' - try: - fingerprint = cmd(fingerprint_cmd, stderr=DEVNULL, input=host_key) - except OSError: - sys.exit("Can not get RSA host key fingerprint.") - - print("RSA host key fingerprint is {}".format(fingerprint.split()[1])) - response = input("Do you trust this host? [y]/n ") - - if not response or response == 'y': - with open(known_hosts, 'a+') as f: - print("Adding {} to the list of known" - " hosts.".format(host_name)) - f.write(host_key) + url = urllib.parse.urlparse(urlstring) + if url.scheme == 'http' or url.scheme == 'https': + download_http(urlstring, local_path) + elif url.scheme == 'ftp': + username = url.username if url.username else 'anonymous' + download_ftp(local_path, url.hostname, url.path, username, url.password) + elif url.scheme == 'sftp' or url.scheme == 'scp': + download_sftp(local_path, url.hostname, url.path, url.username, password) + elif url.scheme == 'tftp': + download_tftp(local_path, url.hostname, url.path) else: - sys.exit("Host not trusted") + ValueError(f'Unsupported URL scheme: {url.scheme}') -def get_remote_config(remote_file): - """ Invoke curl to download remote (config) file. +def upload(local_path, urlstring): + """ + Dispatch the appropriate upload function for the given URL and upload from local path. + """ + url = urllib.parse.urlparse(urlstring) + if url.scheme == 'ftp': + username = url.username if url.username else 'anonymous' + upload_ftp(local_path, url.hostname, url.path, username, url.password) + elif url.scheme == 'sftp' or url.scheme == 'scp': + upload_sftp(local_path, url.hostname, url.path, url.username, password) + elif url.scheme == 'tftp': + upload_tftp(local_path, url.hostname, url.path) + else: + ValueError(f'Unsupported URL scheme: {url.scheme}') +def get_remote_config(urlstring): + """ + Download remote (config) file and return the contents. Args: remote file URI: scp://<user>[:<passwd>]@<host>/<file> sftp://<user>[:<passwd>]@<host>/<file> http://<host>/<file> https://<host>/<file> - ftp://<user>[:<passwd>]@<host>/<file> + ftp://[<user>[:<passwd>]@]<host>/<file> tftp://<host>/<file> """ - request = dict.fromkeys(['protocol', 'user', 'host', 'file']) - protocols = ['scp', 'sftp', 'http', 'https', 'ftp', 'tftp'] - or_protocols = '|'.join(protocols) - - request_match = re.match(r'(' + or_protocols + r')://(.*?)(/.*)', - remote_file) - if request_match: - (request['protocol'], request['host'], - request['file']) = request_match.groups() - else: - print("Malformed URI") - sys.exit(1) - - user_match = re.search(r'(.*)@(.*)', request['host']) - if user_match: - request['user'] = user_match.groups()[0] - request['host'] = user_match.groups()[1] - - remote_file = '{0}://{1}{2}'.format(request['protocol'], request['host'], request['file']) - - if request['protocol'] in ('scp', 'sftp'): - check_and_add_host_key(request['host']) - - redirect_opt = '' - - if request['protocol'] in ('http', 'https'): - redirect_opt = '-L' - # Try header first, and look for 'OK' or 'Moved' codes: - curl_cmd = 'curl {0} -q -I {1}'.format(redirect_opt, remote_file) - try: - curl_output = cmd(curl_cmd) - except OSError: - sys.exit(1) - - return_vals = re.findall(r'^HTTP\/\d+\.?\d\s+(\d+)\s+(.*)$', - curl_output, re.MULTILINE) - for val in return_vals: - if int(val[0]) not in [200, 301, 302]: - print('HTTP error: {0} {1}'.format(*val)) - sys.exit(1) - - if request['user']: - curl_cmd = 'curl -# -u {0} {1}'.format(request['user'], remote_file) - else: - curl_cmd = 'curl {0} -# {1}'.format(redirect_opt, remote_file) - + url = urllib.parse.urlparse(urlstring) + temp = tempfile.NamedTemporaryFile(delete=False).name try: - return cmd(curl_cmd, stderr=None) - except OSError: - return None + download(temp, urlstring) + with open(temp, 'r') as file: + return file.read() + finally: + os.remove(temp) diff --git a/python/vyos/template.py b/python/vyos/template.py index bf087c223..85e4d12b3 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -131,6 +131,13 @@ def address_from_cidr(prefix): from ipaddress import ip_network return str(ip_network(prefix).network_address) +@register_filter('bracketize_ipv6') +def bracketize_ipv6(address): + """ Place a passed IPv6 address into [] brackets, do nothing for IPv4 """ + if is_ipv6(address): + return f'[{address}]' + return address + @register_filter('netmask_from_cidr') def netmask_from_cidr(prefix): """ Take CIDR prefix and convert the prefix length to a "subnet mask". @@ -149,7 +156,9 @@ def netmask_from_ipv4(address): Example: - 172.18.201.10 -> 255.255.255.128 """ - from netifaces import interfaces, ifaddresses, AF_INET + from netifaces import interfaces + from netifaces import ifaddresses + from netifaces import AF_INET for interface in interfaces(): tmp = ifaddresses(interface) if AF_INET in tmp: @@ -160,6 +169,30 @@ def netmask_from_ipv4(address): raise ValueError +@register_filter('is_ip_network') +def is_ip_network(addr): + """ Take IP(v4/v6) address and validate if the passed argument is a network + or a host address. + + Example: + - 192.0.2.0 -> False + - 192.0.2.10/24 -> False + - 192.0.2.0/24 -> True + - 2001:db8:: -> False + - 2001:db8::100 -> False + - 2001:db8::/48 -> True + - 2001:db8:1000::/64 -> True + """ + try: + from ipaddress import ip_network + # input variables must contain a / to indicate its CIDR notation + if len(addr.split('/')) != 2: + raise ValueError() + ip_network(addr) + return True + except: + return False + @register_filter('network_from_ipv4') def network_from_ipv4(address): """ Take IP address and search all attached interface IP addresses for the @@ -248,6 +281,20 @@ def dec_ip(address, decrement): from ipaddress import ip_interface return str(ip_interface(address).ip - int(decrement)) +@register_filter('compare_netmask') +def compare_netmask(netmask1, netmask2): + """ + Compare two IP netmask if they have the exact same size. + + compare_netmask('10.0.0.0/8', '20.0.0.0/8') -> True + compare_netmask('10.0.0.0/8', '20.0.0.0/16') -> False + """ + from ipaddress import ip_network + try: + return ip_network(netmask1).netmask == ip_network(netmask2).netmask + except: + return False + @register_filter('isc_static_route') def isc_static_route(subnet, router): # https://ercpe.de/blog/pushing-static-routes-with-isc-dhcp-server @@ -275,3 +322,22 @@ def is_file(filename): if os.path.exists(filename): return os.path.isfile(filename) return False + +@register_filter('get_dhcp_router') +def get_dhcp_router(interface): + """ Static routes can point to a router received by a DHCP reply. This + helper is used to get the current default router from the DHCP reply. + + Returns False of no router is found, returns the IP address as string if + a router is found. + """ + interface = interface.replace('.', '_') + lease_file = f'/var/lib/dhcp/dhclient_{interface}.leases' + if not os.path.exists(lease_file): + return None + + from vyos.util import read_file + for line in read_file(lease_file).splitlines(): + if 'option routers' in line: + (_, _, address) = line.split() + return address.rstrip(';') diff --git a/python/vyos/util.py b/python/vyos/util.py index 494c8155e..e2f4b8fc4 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -311,7 +311,7 @@ def chmod_755(path): def makedir(path, user=None, group=None): if os.path.exists(path): return - os.mkdir(path) + os.makedirs(path, mode=0o755) chown(path, user, group) @@ -554,16 +554,19 @@ def ask_yes_no(question, default=False) -> bool: from sys import stdout default_msg = "[Y/n]" if default else "[y/N]" while True: - stdout.write("%s %s " % (question, default_msg)) - c = input().lower() - if c == '': - return default - elif c in ("y", "ye", "yes"): - return True - elif c in ("n", "no"): - return False - else: - stdout.write("Please respond with yes/y or no/n\n") + try: + stdout.write("%s %s " % (question, default_msg)) + c = input().lower() + if c == '': + return default + elif c in ("y", "ye", "yes"): + return True + elif c in ("n", "no"): + return False + else: + stdout.write("Please respond with yes/y or no/n\n") + except EOFError: + stdout.write("\nPlease respond with yes/y or no/n\n") def is_admin() -> bool: @@ -627,18 +630,44 @@ def find_device_file(device): return None -def dict_search(path, dict): - """ Traverse Python dictionary (dict) delimited by dot (.). +def dict_search(path, my_dict): + """ Traverse Python dictionary (my_dict) delimited by dot (.). Return value of key if found, None otherwise. - This is faster implementation then jmespath.search('foo.bar', dict)""" + This is faster implementation then jmespath.search('foo.bar', my_dict)""" + if not isinstance(my_dict, dict) or not path: + return None + parts = path.split('.') inside = parts[:-1] if not inside: - if path not in dict: + if path not in my_dict: return None - return dict[path] - c = dict + return my_dict[path] + c = my_dict for p in parts[:-1]: c = c.get(p, {}) return c.get(parts[-1], None) + +def get_interface_config(interface): + """ Returns the used encapsulation protocol for given interface. + If interface does not exist, None is returned. + """ + if not os.path.exists(f'/sys/class/net/{interface}'): + return None + from json import loads + tmp = loads(cmd(f'ip -d -j link show {interface}'))[0] + return tmp + +def get_all_vrfs(): + """ Return a dictionary of all system wide known VRF instances """ + from json import loads + tmp = loads(cmd('ip -j vrf list')) + # Result is of type [{"name":"red","table":1000},{"name":"blue","table":2000}] + # so we will re-arrange it to a more nicer representation: + # {'red': {'table': 1000}, 'blue': {'table': 2000}} + data = {} + for entry in tmp: + name = entry.pop('name') + data[name] = entry + return data diff --git a/scripts/override-default b/scripts/override-default new file mode 100755 index 000000000..c8a0ff1da --- /dev/null +++ b/scripts/override-default @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# +# override-default: preprocessor for XML interface definitions to interpret +# redundant entries (relative to path) with tag 'defaultValue' as an override +# directive. Must be called before build-command-templates, as the schema +# disallows redundancy. +# +# Copyright (C) 2021 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/>. +# +# + +# Use lxml xpath capability to find multiple elements with tag defaultValue +# relative to path; replace and remove to override the value. + +import sys +import glob +import logging +from lxml import etree + +debug = False + +logger = logging.getLogger(__name__) +logs_handler = logging.StreamHandler() +logger.addHandler(logs_handler) + +if debug: + logger.setLevel(logging.DEBUG) +else: + logger.setLevel(logging.INFO) + +def override_element(l: list): + """ + Allow multiple override elements; use the final one (in document order). + """ + if len(l) < 2: + logger.debug("passing list of single element to override_element") + return + + # assemble list of leafNodes of overriding defaultValues, for later removal + parents = [] + for el in l[1:]: + parents.append(el.getparent()) + + # replace element with final override + l[0].getparent().replace(l[0], l[-1]) + + # remove all but overridden element + for el in parents: + el.getparent().remove(el) + +def collect_and_override(dir_name): + """ + Collect elements with defaultValue tag into dictionary indexed by tuple + of (name: str, ancestor path: str). + """ + for fname in glob.glob(f'{dir_name}/*.xml'): + tree = etree.parse(fname) + root = tree.getroot() + defv = {} + + xpath_str = f'//defaultValue' + xp = tree.xpath(xpath_str) + + for element in xp: + ap = element.xpath('ancestor::*[@name]') + ap_name = [el.get("name") for el in ap] + ap_path_str = ' '.join(ap_name[:-1]) + defv.setdefault((ap_name[-1], ap_path_str), []).append(element) + + for k, v in defv.items(): + if len(v) > 1: + logger.info(f"overridding default in {k[0]}, path '{k[1]}'") + override_element(v) + + revised_str = etree.tostring(root, encoding='unicode', pretty_print=True) + + with open(f'{fname}', 'w') as f: + f.write(revised_str) + +def main(): + if len(sys.argv) < 2: + logger.critical('Must specify XML directory!') + sys.exit(1) + + dir_name = sys.argv[1] + + collect_and_override(dir_name) + +if __name__ == '__main__': + main() diff --git a/smoketest/configs/azure-bgp-gateway b/smoketest/configs/azure-bgp-gateway new file mode 100644 index 000000000..b3f5e9edc --- /dev/null +++ b/smoketest/configs/azure-bgp-gateway @@ -0,0 +1,435 @@ +firewall {
+ all-ping enable
+ broadcast-ping disable
+ config-trap disable
+ ipv6-receive-redirects disable
+ ipv6-src-route disable
+ ip-src-route disable
+ log-martians disable
+ options {
+ interface vti31 {
+ adjust-mss 1350
+ }
+ interface vti32 {
+ adjust-mss 1350
+ }
+ interface vti41 {
+ adjust-mss 1350
+ }
+ interface vti42 {
+ adjust-mss 1350
+ }
+ interface vti51 {
+ adjust-mss 1350
+ }
+ interface vti52 {
+ adjust-mss 1350
+ }
+ }
+ receive-redirects disable
+ send-redirects enable
+ source-validation disable
+ syn-cookies enable
+ twa-hazards-protection disable
+}
+high-availability {
+ vrrp {
+ group DMZ-VLAN-3962 {
+ interface eth1
+ preempt-delay 180
+ priority 200
+ virtual-address 192.168.34.36/27
+ vrid 62
+ }
+ }
+}
+interfaces {
+ ethernet eth0 {
+ address 192.0.2.189/27
+ duplex auto
+ smp-affinity auto
+ speed auto
+ }
+ ethernet eth1 {
+ address 192.168.34.37/27
+ duplex auto
+ smp-affinity auto
+ speed auto
+ }
+ loopback lo {
+ }
+ vti vti31 {
+ }
+ vti vti32 {
+ }
+ vti vti41 {
+ }
+ vti vti42 {
+ }
+ vti vti51 {
+ }
+ vti vti52 {
+ }
+}
+policy {
+ prefix-list AZURE-BGP-IPv4-in {
+ description "Prefixes received from Azure"
+ rule 100 {
+ action permit
+ le 32
+ prefix 100.64.0.0/10
+ }
+ }
+ prefix-list ONPREM-BGP-IPv4-out {
+ description "Prefixes allowed to be announced into Azure"
+ rule 100 {
+ action permit
+ prefix 10.0.0.0/8
+ }
+ rule 200 {
+ action permit
+ prefix 172.16.0.0/12
+ }
+ rule 300 {
+ action permit
+ prefix 192.168.0.0/16
+ }
+ }
+}
+protocols {
+ bgp 65522 {
+ address-family {
+ ipv4-unicast {
+ network 10.0.0.0/8 {
+ }
+ network 172.16.0.0/12 {
+ }
+ network 192.168.0.0/16 {
+ }
+ }
+ }
+ neighbor 100.66.8.36 {
+ peer-group AZURE
+ remote-as 64517
+ }
+ neighbor 100.66.8.37 {
+ peer-group AZURE
+ remote-as 64517
+ }
+ neighbor 100.66.24.36 {
+ peer-group AZURE
+ remote-as 64513
+ }
+ neighbor 100.66.24.37 {
+ peer-group AZURE
+ remote-as 64513
+ }
+ neighbor 100.66.40.36 {
+ peer-group AZURE
+ remote-as 64515
+ }
+ neighbor 100.66.40.37 {
+ peer-group AZURE
+ remote-as 64515
+ }
+ neighbor 192.168.34.38 {
+ address-family {
+ ipv4-unicast {
+ nexthop-self
+ soft-reconfiguration {
+ inbound
+ }
+ }
+ }
+ capability {
+ dynamic
+ }
+ password VyOSR0xx123
+ remote-as 65522
+ update-source eth1
+ }
+ peer-group AZURE {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 50
+ prefix-list {
+ export ONPREM-BGP-IPv4-out
+ import AZURE-BGP-IPv4-in
+ }
+ }
+ }
+ ebgp-multihop 2
+ update-source eth1
+ }
+ timers {
+ holdtime 30
+ keepalive 5
+ }
+ }
+ static {
+ interface-route 100.66.8.36/32 {
+ next-hop-interface vti31 {
+ }
+ next-hop-interface vti32 {
+ }
+ }
+ interface-route 100.66.8.37/32 {
+ next-hop-interface vti31 {
+ }
+ next-hop-interface vti32 {
+ }
+ }
+ interface-route 100.66.24.36/32 {
+ next-hop-interface vti41 {
+ }
+ next-hop-interface vti42 {
+ }
+ }
+ interface-route 100.66.24.37/32 {
+ next-hop-interface vti41 {
+ }
+ next-hop-interface vti42 {
+ }
+ }
+ interface-route 100.66.40.36/32 {
+ next-hop-interface vti51 {
+ }
+ next-hop-interface vti52 {
+ }
+ }
+ interface-route 100.66.40.37/32 {
+ next-hop-interface vti51 {
+ }
+ next-hop-interface vti52 {
+ }
+ }
+ route 0.0.0.0/0 {
+ next-hop 192.168.34.33 {
+ }
+ }
+ route 51.105.0.0/16 {
+ next-hop 192.0.2.161 {
+ }
+ }
+ route 52.143.0.0/16 {
+ next-hop 192.0.2.161 {
+ }
+ }
+ route 195.137.175.0/24 {
+ next-hop 192.0.2.161 {
+ }
+ }
+ route 212.23.159.0/26 {
+ next-hop 192.0.2.161 {
+ }
+ }
+ }
+}
+service {
+ ssh {
+ disable-host-validation
+ port 22
+ }
+}
+system {
+ config-management {
+ commit-revisions 100
+ }
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ domain-name vyos.net
+ flow-accounting {
+ interface eth1
+ interface vti31
+ interface vti32
+ interface vti41
+ interface vti42
+ interface vti51
+ interface vti52
+ netflow {
+ server 10.0.1.1 {
+ port 2055
+ }
+ source-ip 192.168.34.37
+ version 10
+ }
+ syslog-facility daemon
+ }
+ host-name azure-gw-01
+ login {
+ radius-server 192.0.2.253 {
+ port 1812
+ secret secret1234
+ timeout 2
+ }
+ radius-server 192.0.2.254 {
+ port 1812
+ secret secret1234
+ timeout 2
+ }
+ radius-source-address 192.168.34.37
+ user vyos {
+ authentication {
+ encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0
+ plaintext-password ""
+ }
+ }
+ }
+ name-server 192.0.2.254
+ ntp {
+ server 192.0.2.254 {
+ }
+ }
+ syslog {
+ global {
+ archive {
+ file 10
+ size 20480
+ }
+ facility all {
+ level info
+ }
+ facility protocols {
+ level debug
+ }
+ }
+ host 10.0.9.188 {
+ facility all {
+ level info
+ protocol udp
+ }
+ }
+ }
+ time-zone Europe/Berlin
+}
+vpn {
+ ipsec {
+ esp-group ESP-AZURE {
+ compression disable
+ lifetime 27000
+ mode tunnel
+ pfs disable
+ proposal 1 {
+ encryption aes256
+ hash sha1
+ }
+ }
+ ike-group IKE-AZURE {
+ close-action none
+ dead-peer-detection {
+ action restart
+ interval 2
+ timeout 15
+ }
+ ikev2-reauth no
+ key-exchange ikev2
+ lifetime 27000
+ proposal 1 {
+ dh-group 2
+ encryption aes256
+ hash sha1
+ }
+ }
+ ipsec-interfaces {
+ interface eth0
+ }
+ logging {
+ log-level 2
+ log-modes ike
+ }
+ site-to-site {
+ peer 51.105.0.2 {
+ authentication {
+ mode pre-shared-secret
+ pre-shared-secret averysecretpsktowardsazure
+ }
+ connection-type respond
+ ike-group IKE-AZURE
+ ikev2-reauth inherit
+ local-address 192.0.2.189
+ vti {
+ bind vti51
+ esp-group ESP-AZURE
+ }
+ }
+ peer 51.105.0.3 {
+ authentication {
+ mode pre-shared-secret
+ pre-shared-secret averysecretpsktowardsazure
+ }
+ connection-type respond
+ ike-group IKE-AZURE
+ ikev2-reauth inherit
+ local-address 192.0.2.189
+ vti {
+ bind vti52
+ esp-group ESP-AZURE
+ }
+ }
+ peer 51.105.0.246 {
+ authentication {
+ mode pre-shared-secret
+ pre-shared-secret averysecretpsktowardsazure
+ }
+ connection-type respond
+ ike-group IKE-AZURE
+ ikev2-reauth inherit
+ local-address 192.0.2.189
+ vti {
+ bind vti32
+ esp-group ESP-AZURE
+ }
+ }
+ peer 51.105.0.247 {
+ authentication {
+ mode pre-shared-secret
+ pre-shared-secret averysecretpsktowardsazure
+ }
+ connection-type respond
+ ike-group IKE-AZURE
+ ikev2-reauth inherit
+ local-address 192.0.2.189
+ vti {
+ bind vti31
+ esp-group ESP-AZURE
+ }
+ }
+ peer 51.105.0.18 {
+ authentication {
+ mode pre-shared-secret
+ pre-shared-secret averysecretpsktowardsazure
+ }
+ connection-type respond
+ ike-group IKE-AZURE
+ ikev2-reauth inherit
+ local-address 192.0.2.189
+ vti {
+ bind vti42
+ esp-group ESP-AZURE
+ }
+ }
+ peer 51.105.0.19 {
+ authentication {
+ mode pre-shared-secret
+ pre-shared-secret averysecretpsktowardsazure
+ }
+ connection-type respond
+ ike-group IKE-AZURE
+ ikev2-reauth inherit
+ local-address 192.0.2.189
+ vti {
+ bind vti41
+ esp-group ESP-AZURE
+ }
+ }
+ }
+ }
+}
+
+
+/* Warning: Do not remove the following line. */
+/* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pptp@1:qos@1:quagga@6:snmp@1:ssh@1:system@9:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */
+/* Release version: 1.2.5 */
diff --git a/smoketest/configs/bgp-bfd-communities b/smoketest/configs/bgp-bfd-communities new file mode 100644 index 000000000..3b3056a51 --- /dev/null +++ b/smoketest/configs/bgp-bfd-communities @@ -0,0 +1,533 @@ +interfaces { + ethernet eth0 { + address 192.0.2.100/25 + address 2001:db8::ffff/64 + } + loopback lo { + } +} +policy { + large-community-list ANYCAST_ALL { + rule 10 { + action permit + description "Allow all anycast from anywhere" + regex "4242420696:100:.*" + } + } + large-community-list ANYCAST_INT { + rule 10 { + action permit + description "Allow all anycast from int" + regex 4242420696:100:1 + } + } + prefix-list BGP-BACKBONE-IN { + description "Inbound backbone routes from other sites" + rule 10 { + action deny + description "Block default route" + prefix 0.0.0.0/0 + } + rule 20 { + action deny + description "Block int primary" + ge 21 + prefix 192.168.0.0/20 + } + rule 30 { + action deny + description "Block loopbacks" + ge 25 + prefix 192.168.253.0/24 + } + rule 40 { + action deny + description "Block backbone peering" + ge 25 + prefix 192.168.254.0/24 + } + rule 999 { + action permit + description "Allow everything else" + ge 1 + prefix 0.0.0.0/0 + } + } + prefix-list BGP-BACKBONE-OUT { + description "Outbound backbone routes to other sites" + rule 10 { + action permit + description "Int primary" + ge 23 + prefix 192.168.0.0/20 + } + } + prefix-list GLOBAL { + description "Globally redistributed routes" + rule 10 { + action permit + prefix 192.168.100.1/32 + } + rule 20 { + action permit + prefix 192.168.7.128/25 + } + } + prefix-list6 BGP-BACKBONE-IN-V6 { + description "Inbound backbone routes from other sites" + rule 10 { + action deny + description "Block default route" + prefix ::/0 + } + rule 20 { + action deny + description "Block int primary" + ge 53 + prefix fd52:d62e:8011::/52 + } + rule 30 { + action deny + description "Block peering and stuff" + ge 53 + prefix fd52:d62e:8011:f000::/52 + } + rule 999 { + action permit + description "Allow everything else" + ge 1 + prefix ::/0 + } + } + prefix-list6 BGP-BACKBONE-OUT-V6 { + description "Outbound backbone routes to other sites" + rule 10 { + action permit + ge 64 + prefix fd52:d62e:8011::/52 + } + } + prefix-list6 GLOBAL-V6 { + description "Globally redistributed routes" + rule 10 { + action permit + ge 64 + prefix fd52:d62e:8011:2::/63 + } + } + route-map BGP-REDISTRIBUTE { + rule 10 { + action permit + description "Prepend AS and allow VPN and modem" + match { + ip { + address { + prefix-list GLOBAL + } + } + } + set { + as-path-prepend 4242420666 + } + } + rule 20 { + action permit + description "Allow VPN" + match { + ipv6 { + address { + prefix-list GLOBAL-V6 + } + } + } + } + } + route-map BGP-BACKBONE-IN { + rule 10 { + action permit + match { + ip { + address { + prefix-list BGP-BACKBONE-IN + } + } + } + } + rule 20 { + action permit + match { + ipv6 { + address { + prefix-list BGP-BACKBONE-IN-V6 + } + } + } + } + rule 30 { + action permit + match { + large-community { + large-community-list ANYCAST_ALL + } + } + } + } + route-map BGP-BACKBONE-OUT { + rule 10 { + action permit + match { + ip { + address { + prefix-list BGP-BACKBONE-OUT + } + } + } + } + rule 20 { + action permit + match { + ipv6 { + address { + prefix-list BGP-BACKBONE-OUT-V6 + } + } + } + } + rule 30 { + action permit + match { + large-community { + large-community-list ANYCAST_INT + } + } + set { + as-path-prepend 4242420666 + } + } + } +} +protocols { + bfd { + peer 192.168.253.1 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address 192.168.253.3 + } + } + peer 192.168.253.2 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address 192.168.253.3 + } + } + peer 192.168.253.6 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address 192.168.253.3 + } + } + peer 192.168.253.7 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address 192.168.253.3 + } + } + peer 192.168.253.12 { + interval { + receive 100 + transmit 100 + } + multihop + source { + address 192.168.253.3 + } + } + peer fd52:d62e:8011:fffe:192:168:253:1 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address fd52:d62e:8011:fffe:192:168:253:3 + } + } + peer fd52:d62e:8011:fffe:192:168:253:2 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address fd52:d62e:8011:fffe:192:168:253:3 + } + } + peer fd52:d62e:8011:fffe:192:168:253:6 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address fd52:d62e:8011:fffe:192:168:253:3 + } + } + peer fd52:d62e:8011:fffe:192:168:253:7 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address fd52:d62e:8011:fffe:192:168:253:3 + } + } + peer fd52:d62e:8011:fffe:192:168:253:12 { + interval { + receive 100 + transmit 100 + } + multihop + source { + address fd52:d62e:8011:fffe:192:168:253:3 + } + } + } + bgp 4242420666 { + address-family { + ipv4-unicast { + redistribute { + connected { + route-map BGP-REDISTRIBUTE + } + static { + route-map BGP-REDISTRIBUTE + } + } + } + ipv6-unicast { + redistribute { + connected { + route-map BGP-REDISTRIBUTE + } + } + } + } + neighbor 192.168.253.1 { + peer-group INT + } + neighbor 192.168.253.2 { + peer-group INT + } + neighbor 192.168.253.6 { + peer-group DAL13 + } + neighbor 192.168.253.7 { + peer-group DAL13 + } + neighbor 192.168.253.12 { + address-family { + ipv4-unicast { + route-map { + export BGP-BACKBONE-OUT + import BGP-BACKBONE-IN + } + soft-reconfiguration { + inbound + } + } + } + bfd { + } + ebgp-multihop 2 + remote-as 4242420669 + update-source dum0 + } + neighbor fd52:d62e:8011:fffe:192:168:253:1 { + address-family { + ipv6-unicast { + peer-group INTv6 + } + } + } + neighbor fd52:d62e:8011:fffe:192:168:253:2 { + address-family { + ipv6-unicast { + peer-group INTv6 + } + } + } + neighbor fd52:d62e:8011:fffe:192:168:253:6 { + address-family { + ipv6-unicast { + peer-group DAL13v6 + } + } + } + neighbor fd52:d62e:8011:fffe:192:168:253:7 { + address-family { + ipv6-unicast { + peer-group DAL13v6 + } + } + } + neighbor fd52:d62e:8011:fffe:192:168:253:12 { + address-family { + ipv6-unicast { + route-map { + export BGP-BACKBONE-OUT + import BGP-BACKBONE-IN + } + soft-reconfiguration { + inbound + } + } + } + bfd { + } + ebgp-multihop 2 + remote-as 4242420669 + update-source dum0 + } + parameters { + confederation { + identifier 4242420696 + peers 4242420668 + peers 4242420669 + } + default { + no-ipv4-unicast + } + distance { + global { + external 220 + internal 220 + local 220 + } + } + graceful-restart { + } + } + peer-group DAL13 { + address-family { + ipv4-unicast { + route-map { + export BGP-BACKBONE-OUT + import BGP-BACKBONE-IN + } + soft-reconfiguration { + inbound + } + } + } + bfd + ebgp-multihop 2 + remote-as 4242420668 + update-source dum0 + } + peer-group DAL13v6 { + address-family { + ipv6-unicast { + route-map { + export BGP-BACKBONE-OUT + import BGP-BACKBONE-IN + } + soft-reconfiguration { + inbound + } + } + } + bfd + ebgp-multihop 2 + remote-as 4242420668 + update-source dum0 + } + peer-group INT { + address-family { + ipv4-unicast { + default-originate { + } + soft-reconfiguration { + inbound + } + } + } + bfd + remote-as 4242420666 + update-source dum0 + } + peer-group INTv6 { + address-family { + ipv6-unicast { + default-originate { + } + soft-reconfiguration { + inbound + } + } + } + bfd + remote-as 4242420666 + update-source dum0 + } + } +} +system { + config-management { + commit-revisions 200 + } + console { + device ttyS0 { + speed 115200 + } + } + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/ + plaintext-password "" + } + level admin + } + } + ntp { + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } + time-zone Europe/Berlin +} + +/* Warning: Do not remove the following line. */ +/* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pptp@1:qos@1:quagga@6:snmp@1:ssh@1:system@10:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */ +/* Release version: 1.2.6-S1 */ diff --git a/smoketest/configs/bgp-big-as-cloud b/smoketest/configs/bgp-big-as-cloud new file mode 100644 index 000000000..694243d1e --- /dev/null +++ b/smoketest/configs/bgp-big-as-cloud @@ -0,0 +1,1956 @@ +firewall { + all-ping enable + broadcast-ping disable + config-trap disable + group { + address-group bgp-peers-4 { + address 192.0.68.3 + address 192.0.68.2 + address 192.0.176.193 + address 192.0.52.0-192.0.52.255 + address 192.0.53.0-192.0.53.255 + address 192.0.16.209 + address 192.0.192.0-192.0.192.255 + address 192.0.193.0-192.0.193.255 + address 192.0.194.0-192.0.194.255 + address 192.0.195.0-192.0.195.255 + address 192.0.196.0-192.0.196.255 + address 192.0.197.0-192.0.197.255 + address 192.0.198.0-192.0.198.255 + address 192.0.199.0-192.0.199.255 + } + address-group vrrp-peers-4 { + address 192.0.68.3 + address 192.0.160.3 + address 192.0.98.3 + address 192.0.71.131 + address 192.0.84.67 + address 192.0.71.195 + address 192.0.71.115 + address 192.0.70.195 + address 192.0.70.179 + address 192.0.70.163 + address 192.0.70.147 + address 192.0.70.131 + address 192.0.70.19 + address 192.0.70.3 + address 192.0.71.99 + address 192.0.68.67 + address 192.0.71.67 + address 192.0.71.3 + address 192.0.68.35 + address 192.0.68.131 + address 192.0.69.2 + address 192.0.70.35 + address 192.0.70.67 + } + ipv6-address-group bgp-peers-6 { + address 2001:db8:c::3 + address 2001:db8:1000::2e9 + address 2001:db8:24::fb + address 2001:db8:24::fc + address 2001:db8:24::fd + address 2001:db8:24::2e + address 2001:db8:24::3d + address 2001:db8:24::4a + address 2001:db8:24::5e + address 2001:db8:24::7 + address 2001:db8:24::11 + address 2001:db8:24::18 + address 2001:db8:24::20 + address 2001:db8:24::22 + address 2001:db8:24::31 + address 2001:db8:24::58 + address 2001:db8:24::64 + address 2001:db8:24::a5 + address 2001:db8:24::aa + address 2001:db8:24::ab + address 2001:db8:24::b0 + address 2001:db8:24::b3 + address 2001:db8:24::bd + address 2001:db8:24::c + address 2001:db8:24::d2 + address 2001:db8:24::d3 + address 2001:db8:838::1 + address 2001:db8::1a27:5051:c09d + address 2001:db8::1a27:5051:c19d + address 2001:db8::20ad:0:1 + address 2001:db8::2306:0:1 + address 2001:db8::2ca:0:1 + address 2001:db8::2ca:0:2 + address 2001:db8::2ca:0:3 + address 2001:db8::2ca:0:4 + } + ipv6-address-group vrrp-peers-6 { + address fe80::fe89:15cf + } + ipv6-network-group AS64512-6 { + network 2001::/29 + } + network-group AS64512-4 { + network 192.0.68.0/22 + network 192.0.98.0/24 + network 192.0.160.0/24 + network 192.0.84.0/22 + } + } + ipv6-name management-to-local-6 { + default-action reject + enable-default-log + } + ipv6-name management-to-peers-6 { + default-action reject + enable-default-log + } + ipv6-name management-to-servers-6 { + default-action reject + enable-default-log + } + ipv6-name peers-to-local-6 { + default-action reject + enable-default-log + rule 500 { + action accept + protocol icmpv6 + } + rule 501 { + action accept + protocol vrrp + source { + group { + address-group vrrp-peers-6 + } + } + } + rule 502 { + action accept + destination { + port bgp + } + protocol tcp + source { + group { + address-group bgp-peers-6 + } + } + } + rule 503 { + action accept + protocol tcp + source { + group { + address-group bgp-peers-6 + } + port bgp + } + } + } + ipv6-name peers-to-management-6 { + default-action reject + enable-default-log + } + ipv6-name peers-to-servers-6 { + default-action reject + enable-default-log + rule 9990 { + action reject + source { + group { + network-group AS64512-6 + } + } + } + rule 9999 { + action accept + destination { + group { + network-group AS64512-6 + } + } + } + } + ipv6-name servers-to-local-6 { + default-action reject + enable-default-log + rule 500 { + action accept + protocol icmpv6 + } + rule 501 { + action accept + protocol vrrp + source { + group { + address-group vrrp-peers-6 + } + } + } + rule 511 { + action accept + protocol tcp_udp + source { + port 53 + } + } + } + ipv6-name servers-to-management-6 { + default-action reject + enable-default-log + } + ipv6-name servers-to-peers-6 { + default-action reject + enable-default-log + rule 51 { + action accept + source { + group { + network-group AS64512-6 + } + } + } + } + ipv6-receive-redirects disable + ipv6-src-route disable + ip-src-route disable + log-martians enable + name management-to-local-4 { + default-action reject + enable-default-log + rule 500 { + action accept + protocol icmp + } + rule 501 { + action accept + destination { + port 22 + } + protocol tcp + } + rule 502 { + action accept + destination { + port snmp + } + protocol udp + } + } + name management-to-peers-4 { + default-action reject + enable-default-log + } + name management-to-servers-4 { + default-action reject + enable-default-log + } + name peers-to-local-4 { + default-action reject + enable-default-log + rule 500 { + action accept + protocol icmp + } + rule 501 { + action accept + protocol vrrp + source { + group { + address-group vrrp-peers-4 + } + } + } + rule 502 { + action accept + destination { + port bgp + } + protocol tcp + source { + group { + address-group bgp-peers-4 + } + } + } + rule 503 { + action accept + protocol tcp + source { + group { + address-group bgp-peers-4 + } + port bgp + } + } + } + name peers-to-management-4 { + default-action reject + enable-default-log + } + name peers-to-servers-4 { + default-action reject + enable-default-log + rule 9990 { + action reject + source { + group { + network-group AS64512-4 + } + } + } + rule 9999 { + action accept + destination { + group { + network-group AS64512-4 + } + } + } + } + name servers-to-local-4 { + default-action reject + enable-default-log + rule 500 { + action accept + protocol icmp + } + rule 501 { + action accept + protocol vrrp + source { + group { + address-group vrrp-peers-4 + } + } + } + rule 511 { + action accept + protocol tcp_udp + source { + port 53 + } + } + } + name servers-to-management-4 { + default-action reject + enable-default-log + } + name servers-to-peers-4 { + default-action reject + enable-default-log + rule 51 { + action accept + source { + group { + network-group AS64512-4 + } + } + } + } + receive-redirects disable + send-redirects enable + source-validation disable + syn-cookies enable + twa-hazards-protection disable +} +high-availability { + vrrp { + group 11-4 { + interface eth0.11 + priority 200 + virtual-address 192.0.68.1/27 + vrid 4 + } + group 11-6 { + interface eth0.11 + priority 200 + virtual-address 2001:db8:c::1/64 + vrid 6 + } + group 102-4 { + interface eth0.102 + priority 200 + virtual-address 192.0.98.1/24 + vrid 4 + } + group 102-6 { + interface eth0.102 + priority 200 + virtual-address 2001:db8:0:102::1/64 + vrid 6 + } + group 105-4 { + interface eth0.105 + priority 200 + virtual-address 192.0.160.1/24 + vrid 4 + } + group 105-6 { + interface eth0.105 + priority 200 + virtual-address 2001:db8:0:105::1/64 + vrid 6 + } + group 1001-4 { + interface eth0.1001 + priority 200 + virtual-address 192.0.68.33/27 + vrid 4 + } + group 1001-6 { + interface eth0.1001 + priority 200 + virtual-address 2001:db8:0:1001::1/64 + vrid 6 + } + group 1002-4 { + interface eth0.1002 + priority 200 + virtual-address 192.0.68.65/26 + vrid 4 + } + group 1002-6 { + interface eth0.1002 + priority 200 + virtual-address 2001:db8:0:1002::1/64 + vrid 6 + } + group 1003-4 { + interface eth0.1003 + priority 200 + virtual-address 192.0.68.129/25 + vrid 4 + } + group 1003-6 { + interface eth0.1003 + priority 200 + virtual-address 2001:db8:0:1003::1/64 + vrid 6 + } + group 1004-4 { + interface eth0.1004 + priority 200 + virtual-address 192.0.69.1/24 + vrid 4 + } + group 1004-6 { + interface eth0.1004 + priority 200 + virtual-address 2001:db8:0:1004::1/64 + vrid 6 + } + group 1005-4 { + interface eth0.1005 + priority 200 + virtual-address 192.0.70.1/28 + vrid 4 + } + group 1005-6 { + interface eth0.1005 + priority 200 + virtual-address 2001:db8:0:1005::1/64 + vrid 6 + } + group 1006-4 { + interface eth0.1006 + priority 200 + virtual-address 192.0.70.17/28 + vrid 4 + } + group 1006-6 { + interface eth0.1006 + priority 200 + virtual-address 2001:db8:0:1006::1/64 + vrid 6 + } + group 1007-4 { + interface eth0.1007 + priority 200 + virtual-address 192.0.70.33/27 + vrid 4 + } + group 1007-6 { + interface eth0.1007 + priority 200 + virtual-address 2001:db8:0:1007::1/64 + vrid 6 + } + group 1008-4 { + interface eth0.1008 + priority 200 + virtual-address 192.0.70.65/26 + vrid 4 + } + group 1008-6 { + interface eth0.1008 + priority 200 + virtual-address 2001:db8:0:1008::1/64 + vrid 6 + } + group 1009-4 { + interface eth0.1009 + priority 200 + virtual-address 192.0.70.129/28 + vrid 4 + } + group 1009-6 { + interface eth0.1009 + priority 200 + virtual-address 2001:db8:0:1009::1/64 + vrid 6 + } + group 1010-4 { + interface eth0.1010 + priority 200 + virtual-address 192.0.70.145/28 + vrid 4 + } + group 1010-6 { + interface eth0.1010 + priority 200 + virtual-address 2001:db8:0:1010::1/64 + vrid 6 + } + group 1011-4 { + interface eth0.1011 + priority 200 + virtual-address 192.0.70.161/28 + vrid 4 + } + group 1011-6 { + interface eth0.1011 + priority 200 + virtual-address 2001:db8:0:1011::1/64 + vrid 6 + } + group 1012-4 { + interface eth0.1012 + priority 200 + virtual-address 192.0.70.177/28 + vrid 4 + } + group 1012-6 { + interface eth0.1012 + priority 200 + virtual-address 2001:db8:0:1012::1/64 + vrid 6 + } + group 1013-4 { + interface eth0.1013 + priority 200 + virtual-address 192.0.70.193/27 + vrid 4 + } + group 1013-6 { + interface eth0.1013 + priority 200 + virtual-address 2001:db8:0:1013::1/64 + vrid 6 + } + group 1014-4 { + interface eth0.1014 + priority 200 + virtual-address 192.0.84.65/26 + vrid 4 + } + group 1014-6 { + interface eth0.1014 + priority 200 + virtual-address 2001:db8:0:1014::1/64 + vrid 6 + } + group 1015-4 { + interface eth0.1015 + priority 200 + virtual-address 192.0.71.1/26 + vrid 4 + } + group 1015-6 { + interface eth0.1015 + priority 200 + virtual-address 2001:db8:0:1015::1/64 + vrid 6 + } + group 1016-4 { + interface eth0.1016 + priority 200 + virtual-address 192.0.71.65/27 + vrid 4 + } + group 1016-6 { + interface eth0.1016 + priority 200 + virtual-address 2001:db8:0:1016::1/64 + vrid 6 + } + group 1017-4 { + interface eth0.1017 + priority 200 + virtual-address 192.0.71.97/28 + vrid 4 + } + group 1017-6 { + interface eth0.1017 + priority 200 + virtual-address 2001:db8:0:1017::1/64 + vrid 6 + } + group 1018-4 { + interface eth0.1018 + priority 200 + virtual-address 192.0.71.113/28 + vrid 4 + } + group 1018-6 { + interface eth0.1018 + priority 200 + virtual-address 2001:db8:0:1018::1/64 + vrid 6 + } + group 1019-4 { + interface eth0.1019 + priority 200 + virtual-address 192.0.71.129/26 + vrid 4 + } + group 1019-6 { + interface eth0.1019 + priority 200 + virtual-address 2001:db8:0:1019::1/64 + vrid 6 + } + group 1020-4 { + interface eth0.1020 + priority 200 + virtual-address 192.0.71.193/26 + vrid 4 + } + group 1020-6 { + interface eth0.1020 + priority 200 + virtual-address 2001:db8:0:1020::1/64 + vrid 6 + } + } +} +interfaces { + ethernet eth0 { + address 192.0.0.11/16 + duplex auto + smp-affinity auto + speed auto + vif 11 { + address 192.0.68.2/27 + address 2001:db8:c::2/64 + } + vif 102 { + address 192.0.98.2/24 + address 2001:db8:0:102::2/64 + } + vif 105 { + address 192.0.160.2/24 + address 2001:db8:0:105::2/64 + } + vif 838 { + address 192.0.16.210/30 + address 2001:db8:838::2/64 + } + vif 886 { + address 192.0.193.224/21 + address 2001:db8::3:669:0:1/64 + } + vif 1001 { + address 192.0.68.34/27 + address 2001:db8:0:1001::2/64 + } + vif 1002 { + address 192.0.68.66/26 + address 2001:db8:0:1002::2/64 + } + vif 1003 { + address 192.0.68.130/25 + address 2001:db8:0:1003::2/64 + } + vif 1004 { + address 192.0.69.2/24 + address 2001:db8:0:1004::2/64 + } + vif 1005 { + address 192.0.70.2/28 + address 2001:db8:0:1005::2/64 + } + vif 1006 { + address 192.0.70.18/28 + address 2001:db8:0:1006::2/64 + } + vif 1007 { + address 192.0.70.34/27 + address 2001:db8:0:1007::2/64 + } + vif 1008 { + address 192.0.70.66/26 + address 2001:db8:0:1008::2/64 + } + vif 1009 { + address 192.0.70.130/28 + address 2001:db8:0:1009::2/64 + } + vif 1010 { + address 192.0.70.146/28 + address 2001:db8:0:1010::2/64 + } + vif 1011 { + address 192.0.70.162/28 + address 2001:db8:0:1011::2/64 + } + vif 1012 { + address 192.0.70.178/28 + address 2001:db8:0:1012::2/64 + } + vif 1013 { + address 192.0.70.194/27 + address 2001:db8:0:1013::3/64 + } + vif 1014 { + address 192.0.84.66/26 + address 2001:db8:0:1014::2/64 + } + vif 1015 { + address 192.0.71.2/26 + address 2001:db8:0:1015::2/64 + } + vif 1016 { + address 192.0.71.66/27 + address 2001:db8:0:1016::2/64 + } + vif 1017 { + address 192.0.71.98/28 + address 2001:db8:0:1017::2/64 + } + vif 1018 { + address 192.0.71.114/28 + address 2001:db8:0:1018::2/64 + } + vif 1019 { + address 192.0.71.130/26 + address 2001:db8:0:1019::2/64 + } + vif 1020 { + address 192.0.71.194/26 + address 2001:db8:0:1020::2/64 + } + vif 4088 { + address 2001:db8:24::c7/64 + address 192.0.52.199/23 + } + vif 4089 { + address 192.0.176.194/30 + address 2001:db8:1000::2ea/126 + } + } + loopback lo { + } +} +policy { + as-path-list AS64513-AS64514 { + rule 10 { + action permit + regex "^64513 64514$" + } + } + as-path-list AS64512 { + rule 10 { + action permit + regex ^$ + } + } + prefix-list defaultV4 { + rule 10 { + action permit + prefix 0.0.0.0/0 + } + } + prefix-list hostrouteV4 { + rule 10 { + action permit + ge 32 + prefix 192.0.160.0/24 + } + rule 20 { + action permit + ge 32 + prefix 192.0.98.0/24 + } + rule 30 { + action permit + ge 32 + prefix 192.0.68.0/22 + } + rule 40 { + action permit + ge 32 + prefix 192.0.84.0/22 + } + } + prefix-list vyosV4 { + rule 10 { + action permit + prefix 192.0.160.0/24 + } + rule 20 { + action permit + prefix 192.0.98.0/24 + } + rule 30 { + action permit + prefix 192.0.68.0/22 + } + rule 40 { + action permit + prefix 192.0.84.0/22 + } + } + prefix-list privateV4 { + rule 10 { + action permit + le 32 + prefix 192.0.0.0/8 + } + rule 20 { + action permit + le 32 + prefix 192.0.0.0/12 + } + rule 30 { + action permit + le 32 + prefix 192.0.0.0/16 + } + } + prefix-list6 all6 { + rule 10 { + action permit + ge 4 + prefix 2000::/3 + } + } + prefix-list6 hostrouteV6 { + rule 20 { + action permit + ge 128 + prefix 2001:db8::/29 + } + } + prefix-list6 vyosV6 { + rule 20 { + action permit + prefix 2001:db8::/29 + } + } + prefix-list6 privateV6 { + rule 10 { + action permit + prefix fc00::/7 + } + } + route-map ExportRouteMap { + rule 5 { + action permit + match { + as-path AS64512 + ip { + address { + prefix-list hostrouteV4 + } + } + } + set { + community 65000:666 + } + } + rule 10 { + action permit + match { + as-path AS64512 + ip { + address { + prefix-list vyosV4 + } + } + } + } + rule 15 { + action permit + match { + as-path AS64512 + ipv6 { + address { + prefix-list hostrouteV6 + } + } + } + set { + community 65000:666 + } + } + rule 20 { + action permit + match { + as-path AS64512 + ipv6 { + address { + prefix-list vyosV6 + } + } + } + } + rule 100 { + action deny + } + } + route-map ExportRouteMapAS64515 { + rule 10 { + action permit + match { + ipv6 { + address { + prefix-list all6 + } + } + } + } + rule 20 { + action deny + match { + ip { + address { + prefix-list defaultV4 + } + } + } + } + rule 100 { + action deny + } + } + route-map ExportRouteMapAS64516 { + rule 5 { + action permit + match { + as-path AS64512 + ip { + address { + prefix-list hostrouteV4 + } + } + } + set { + community 65000:666 + } + } + rule 10 { + action permit + match { + as-path AS64512 + ip { + address { + prefix-list vyosV4 + } + } + } + } + rule 15 { + action permit + match { + as-path AS64512 + ipv6 { + address { + prefix-list hostrouteV6 + } + } + } + set { + community 65000:666 + } + } + rule 20 { + action permit + match { + as-path AS64512 + ipv6 { + address { + prefix-list vyosV6 + } + } + } + } + rule 100 { + action deny + } + } + route-map ExportRouteMapAS64517 { + rule 5 { + action permit + match { + as-path AS64512 + ip { + address { + prefix-list hostrouteV4 + } + } + } + set { + community 64517:666 + } + } + rule 10 { + action permit + match { + as-path AS64512 + ip { + address { + prefix-list vyosV4 + } + } + } + } + rule 15 { + action permit + match { + as-path AS64512 + ipv6 { + address { + prefix-list hostrouteV6 + } + } + } + set { + community 64517:666 + } + } + rule 20 { + action permit + match { + as-path AS64512 + ipv6 { + address { + prefix-list vyosV6 + } + } + } + } + rule 100 { + action deny + } + } + route-map ExportRouteMapAS64513 { + rule 5 { + action permit + match { + as-path AS64512 + ip { + address { + prefix-list hostrouteV4 + } + } + } + set { + community 64513:666 + } + } + rule 10 { + action permit + match { + as-path AS64512 + ip { + address { + prefix-list vyosV4 + } + } + } + } + rule 15 { + action permit + match { + as-path AS64512 + ipv6 { + address { + prefix-list hostrouteV6 + } + } + } + set { + community 64513:666 + } + } + rule 20 { + action permit + match { + as-path AS64512 + ipv6 { + address { + prefix-list vyosV6 + } + } + } + } + rule 100 { + action deny + } + } + route-map ImportRouteMap { + rule 10 { + action deny + match { + ip { + address { + prefix-list privateV4 + } + } + } + } + rule 15 { + action deny + match { + ipv6 { + address { + prefix-list privateV6 + } + } + } + } + rule 20 { + action deny + match { + ip { + address { + prefix-list vyosV4 + } + } + } + } + rule 30 { + action deny + match { + ipv6 { + address { + prefix-list vyosV6 + } + } + } + } + rule 40 { + action deny + match { + as-path AS64512 + } + } + rule 50 { + action permit + match { + as-path AS64513-AS64514 + } + set { + weight 10001 + } + } + rule 65535 { + action permit + } + } +} +protocols { + bgp 64500 { + address-family { + ipv4-unicast { + network 192.0.98.0/24 { + } + network 192.0.160.0/24 { + } + network 192.0.68.0/22 { + } + network 192.0.84.0/22 { + } + redistribute { + static { + route-map ExportRouteMap + } + } + } + ipv6-unicast { + network 2001:db8::/29 { + } + redistribute { + static { + route-map ExportRouteMap + } + } + } + } + maximum-paths { + ebgp 8 + ibgp 16 + } + neighbor 192.0.16.209 { + address-family { + ipv4-unicast { + route-map { + export ExportRouteMapAS64516 + import ImportRouteMap + } + } + } + remote-as 64501 + } + neighbor 192.0.192.6 { + address-family { + ipv4-unicast { + maximum-prefix 100 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64502 + } + neighbor 192.0.192.157 { + address-family { + ipv4-unicast { + maximum-prefix 350000 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64503 + } + neighbor 192.0.192.228 { + address-family { + ipv4-unicast { + maximum-prefix 10000 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64504 + } + neighbor 192.0.193.157 { + address-family { + ipv4-unicast { + maximum-prefix 350000 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64505 + } + neighbor 192.0.193.202 { + address-family { + ipv4-unicast { + maximum-prefix 10000 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64506 + } + neighbor 192.0.193.223 { + address-family { + ipv4-unicast { + maximum-prefix 10000 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64507 + } + neighbor 192.0.194.161 { + address-family { + ipv4-unicast { + maximum-prefix 10000 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64508 + } + neighbor 192.0.194.171 { + address-family { + ipv4-unicast { + maximum-prefix 10000 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64509 + } + neighbor 192.0.176.193 { + address-family { + ipv4-unicast { + route-map { + export ExportRouteMapAS64516 + import ImportRouteMap + } + } + } + remote-as 64510 + } + neighbor 192.0.52.12 { + address-family { + ipv4-unicast { + maximum-prefix 300 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64511 + } + neighbor 192.0.52.17 { + address-family { + ipv4-unicast { + maximum-prefix 75 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + password vyosvyos + remote-as 64512 + } + neighbor 192.0.52.24 { + address-family { + ipv4-unicast { + maximum-prefix 300 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64513 + } + neighbor 192.0.52.32 { + address-family { + ipv4-unicast { + maximum-prefix 50 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + password vyosfoooo + remote-as 64514 + } + neighbor 192.0.52.34 { + address-family { + ipv4-unicast { + maximum-prefix 10 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64515 + } + neighbor 192.0.52.46 { + address-family { + ipv4-unicast { + maximum-prefix 10 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64516 + } + neighbor 192.0.52.49 { + address-family { + ipv4-unicast { + maximum-prefix 75 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + password secret + remote-as 64517 + } + neighbor 192.0.52.74 { + address-family { + ipv4-unicast { + maximum-prefix 15000 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + password secretvyos + remote-as 64518 + } + neighbor 192.0.52.94 { + address-family { + ipv4-unicast { + maximum-prefix 250 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64519 + } + neighbor 192.0.52.100 { + address-family { + ipv4-unicast { + maximum-prefix 50 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64520 + } + neighbor 192.0.52.119 { + address-family { + ipv4-unicast { + maximum-prefix 30 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64521 + } + neighbor 192.0.52.165 { + address-family { + ipv4-unicast { + maximum-prefix 50 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64522 + } + neighbor 192.0.52.170 { + address-family { + ipv4-unicast { + maximum-prefix 150000 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64523 + } + neighbor 192.0.52.171 { + address-family { + ipv4-unicast { + maximum-prefix 10000 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64524 + } + neighbor 192.0.52.179 { + address-family { + ipv4-unicast { + maximum-prefix 20 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64525 + } + neighbor 192.0.52.189 { + address-family { + ipv4-unicast { + maximum-prefix 1000 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64526 + } + neighbor 192.0.52.210 { + address-family { + ipv4-unicast { + maximum-prefix 15 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64527 + } + neighbor 192.0.52.211 { + address-family { + ipv4-unicast { + maximum-prefix 15 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64528 + } + neighbor 192.0.52.251 { + address-family { + ipv4-unicast { + route-map { + export ExportRouteMap + import ImportRouteMap + } + weight 1010 + } + } + remote-as 64529 + } + neighbor 192.0.52.252 { + address-family { + ipv4-unicast { + route-map { + export ExportRouteMap + } + weight 1010 + } + } + remote-as 64530 + } + neighbor 192.0.52.253 { + address-family { + ipv4-unicast { + route-map { + export ExportRouteMapAS64515 + import ImportRouteMap + } + } + } + passive + remote-as 64531 + } + neighbor 192.0.68.3 { + address-family { + ipv4-unicast { + nexthop-self + soft-reconfiguration { + inbound + } + } + } + remote-as 64532 + update-source 192.0.68.2 + } + neighbor 2001:db8:838::1 { + address-family { + ipv6-unicast { + route-map { + export ExportRouteMapAS64516 + import ImportRouteMap + } + } + } + remote-as 64533 + } + neighbor 2001:db8:c::3 { + address-family { + ipv6-unicast { + nexthop-self + soft-reconfiguration { + inbound + } + } + } + remote-as 64534 + update-source 2001:db8:c::2 + } + neighbor 2001:db8:24::2e { + address-family { + ipv6-unicast { + maximum-prefix 5 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + password vyossecret + remote-as 64535 + } + neighbor 2001:db8:24::4a { + address-family { + ipv6-unicast { + maximum-prefix 1000 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64536 + } + neighbor 2001:db8:24::5e { + address-family { + ipv6-unicast { + maximum-prefix 200 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64537 + } + neighbor 2001:db8:24::11 { + address-family { + ipv6-unicast { + maximum-prefix 20 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64538 + } + neighbor 2001:db8:24::18 { + address-family { + ipv6-unicast { + maximum-prefix 300 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64539 + } + neighbor 2001:db8:24::20 { + address-family { + ipv6-unicast { + maximum-prefix 10 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64540 + } + neighbor 2001:db8:24::22 { + address-family { + ipv6-unicast { + maximum-prefix 5 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64541 + } + neighbor 2001:db8:24::31 { + address-family { + ipv6-unicast { + maximum-prefix 20 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64542 + } + neighbor 2001:db8:24::58 { + address-family { + ipv6-unicast { + maximum-prefix 15 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64543 + } + neighbor 2001:db8:24::64 { + address-family { + ipv6-unicast { + maximum-prefix 10 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + password geheim + remote-as 64544 + } + neighbor 2001:db8:24::a5 { + address-family { + ipv6-unicast { + maximum-prefix 10 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64545 + } + neighbor 2001:db8:24::aa { + address-family { + ipv6-unicast { + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64546 + } + neighbor 2001:db8:24::ab { + address-family { + ipv6-unicast { + maximum-prefix 1800 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + remote-as 64547 + } + neighbor 2001:db8:24::b0 { + address-family { + ipv6-unicast { + maximum-prefix 5 + route-map { + export ExportRouteMap + import ImportRouteMap + } + } + } + password secret123 + remote-as 64548 + } + parameters { + default { + no-ipv4-unicast + } + log-neighbor-changes + router-id 192.0.68.2 + } + } + static { + route 192.0.98.0/24 { + blackhole { + } + } + route 192.0.160.0/24 { + blackhole { + } + } + route 192.0.68.0/22 { + blackhole { + } + } + route 192.0.84.0/22 { + blackhole { + } + } + route6 2001:db8::/29 { + blackhole { + } + } + } +} +system { + config-management { + commit-revisions 100 + } + console { + device ttyS0 { + speed 115200 + } + } + flow-accounting { + disable-imt + interface eth0.4088 + interface eth0.4089 + netflow { + engine-id 1 + server 192.0.2.55 { + port 2055 + } + version 9 + } + syslog-facility daemon + } + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/ + plaintext-password "" + } + } + } + name-server 2001:db8::1 + name-server 2001:db8::2 + name-server 192.0.2.1 + name-server 192.0.2.2 + ntp { + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + syslog { + global { + facility all { + level all + } + preserve-fqdn + } + } + time-zone Europe/Zurich +} +zone-policy { + zone local { + default-action drop + from management { + firewall { + ipv6-name management-to-local-6 + name management-to-local-4 + } + } + from peers { + firewall { + ipv6-name peers-to-local-6 + name peers-to-local-4 + } + } + from servers { + firewall { + ipv6-name servers-to-local-6 + name servers-to-local-4 + } + } + local-zone + } + zone management { + default-action reject + from peers { + firewall { + ipv6-name peers-to-management-6 + name peers-to-management-4 + } + } + from servers { + firewall { + ipv6-name servers-to-management-6 + name servers-to-management-4 + } + } + interface eth0 + } + zone peers { + default-action reject + from management { + firewall { + ipv6-name management-to-peers-6 + name management-to-peers-4 + } + } + from servers { + firewall { + ipv6-name servers-to-peers-6 + name servers-to-peers-4 + } + } + interface eth0.4088 + interface eth0.4089 + interface eth0.11 + interface eth0.838 + interface eth0.886 + } + zone servers { + default-action reject + from management { + firewall { + ipv6-name management-to-servers-6 + name management-to-servers-4 + } + } + from peers { + firewall { + ipv6-name peers-to-servers-6 + name peers-to-servers-4 + } + } + interface eth0.1001 + interface eth0.105 + interface eth0.102 + interface eth0.1019 + interface eth0.1014 + interface eth0.1020 + interface eth0.1018 + interface eth0.1013 + interface eth0.1012 + interface eth0.1011 + interface eth0.1010 + interface eth0.1009 + interface eth0.1006 + interface eth0.1005 + interface eth0.1017 + interface eth0.1016 + interface eth0.1002 + interface eth0.1015 + interface eth0.1003 + interface eth0.1004 + interface eth0.1007 + interface eth0.1008 + } +} + + +/* Warning: Do not remove the following line. */ +/* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pptp@1:qos@1:quagga@6:snmp@1:ssh@1:system@9:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */ +/* Release version: 1.2.5 */ diff --git a/smoketest/configs/bgp-evpn-leaf b/smoketest/configs/bgp-evpn-leaf new file mode 100644 index 000000000..73d658de8 --- /dev/null +++ b/smoketest/configs/bgp-evpn-leaf @@ -0,0 +1,148 @@ +interfaces { + bridge br100 { + member { + interface eth3 { + } + interface vxlan100 { + } + } + } + dummy dum0 { + address 172.29.0.1/32 + } + ethernet eth0 { + description "Out-of-Band Managament Port" + address 2001:db8::41/64 + address 192.0.2.41/27 + vrf MGMT + } + ethernet eth1 { + address 172.29.1.1/31 + mtu 1600 + } + ethernet eth2 { + address 172.29.2.1/31 + mtu 1600 + } + ethernet eth3 { + } + loopback lo { + } + vxlan vxlan100 { + parameters { + nolearning + } + port 4789 + source-address 172.29.0.1 + vni 100 + } +} +protocols { + bgp 65010 { + address-family { + ipv4-unicast { + maximum-paths { + ibgp 4 + } + redistribute { + connected { + } + } + } + l2vpn-evpn { + advertise-all-vni + } + } + neighbor 172.29.1.0 { + peer-group evpn + } + neighbor 172.29.2.0 { + peer-group evpn + } + parameters { + log-neighbor-changes + } + peer-group evpn { + address-family { + ipv4-unicast { + nexthop-self { + } + } + l2vpn-evpn { + nexthop-self { + } + } + } + remote-as 65010 + } + } + vrf MGMT { + static { + route 0.0.0.0/0 { + next-hop 192.0.2.62 { + } + } + route6 ::/0 { + next-hop 2001:db8::1 { + } + } + } + } +} +service { + lldp { + interface all { + } + } + ssh { + disable-host-validation + vrf MGMT + } +} +system { + config-management { + commit-revisions 100 + } + console { + device ttyS0 { + speed 115200 + } + } + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + ntp { + listen-address 192.0.2.41 + listen-address 2001:db8::41 + server 0.de.pool.ntp.org { + prefer + } + vrf MGMT + } + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } +} +vrf { + name MGMT { + table 1000 + } +} + + +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@2:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@20:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:nat66@1:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@8:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@20:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" +// Release version: 1.4-rolling-202103091038 diff --git a/smoketest/configs/bgp-evpn-spine b/smoketest/configs/bgp-evpn-spine new file mode 100644 index 000000000..5dafc2f77 --- /dev/null +++ b/smoketest/configs/bgp-evpn-spine @@ -0,0 +1,128 @@ +interfaces { + ethernet eth0 { + description "Out-of-Band Managament Port" + address 192.0.2.51/27 + address 2001:db8::51/64 + vrf MGMT + } + ethernet eth1 { + address 172.29.1.0/31 + mtu 1600 + } + ethernet eth2 { + address 172.29.1.2/31 + mtu 1600 + } + ethernet eth3 { + address 172.29.1.4/31 + mtu 1600 + } + loopback lo { + } +} +protocols { + bgp 65010 { + address-family { + ipv4-unicast { + maximum-paths { + ibgp 4 + } + redistribute { + connected { + } + } + } + } + listen { + range 172.29.1.0/24 { + peer-group evpn + } + } + parameters { + log-neighbor-changes + } + peer-group evpn { + address-family { + ipv4-unicast { + route-reflector-client + } + l2vpn-evpn { + route-reflector-client + } + } + capability { + dynamic + } + remote-as 65010 + } + } + vrf MGMT { + static { + route 0.0.0.0/0 { + next-hop 192.0.2.62 { + } + } + route6 ::/0 { + next-hop 2001:db8::1 { + } + } + } + } +} +service { + lldp { + interface all { + } + } + ssh { + disable-host-validation + vrf MGMT + } +} +system { + config-management { + commit-revisions 100 + } + console { + device ttyS0 { + speed 115200 + } + } + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + ntp { + listen-address 192.0.2.51 + listen-address 2001:db8::51 + server 0.de.pool.ntp.org { + prefer + } + vrf MGMT + } + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } +} +vrf { + name MGMT { + table 1000 + } +} + + +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@2:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@20:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:nat66@1:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@8:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@20:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" +// Release version: 1.4-rolling-202103091038 diff --git a/smoketest/configs/pppoe-client b/smoketest/configs/bgp-rpki index ef6a26423..e11ec9e72 100644 --- a/smoketest/configs/pppoe-client +++ b/smoketest/configs/bgp-rpki @@ -1,17 +1,71 @@ interfaces { ethernet eth0 { + address 192.0.2.100/25 + address 2001:db8::ffff/64 + } + ethernet eth1 { } loopback lo { } - pppoe pppoe0 { - authentication { - password bar - user foo +} +policy { + route-map ebgp-transit-rpki { + rule 10 { + action deny + match { + rpki invalid + } + } + rule 20 { + action permit + match { + rpki notfound + } + set { + local-preference 20 + } + } + rule 30 { + action permit + match { + rpki valid + } + set { + local-preference 100 + } + } + } +} +protocols { + bgp 64500 { + neighbor 1.2.3.4 { + address-family { + ipv4-unicast { + nexthop-self { + } + route-map { + import ebgp-transit-rpki + } + } + } + remote-as 10 + } + } + rpki { + cache routinator { + address 192.0.2.10 + port 3323 + } + } + static { + route 0.0.0.0/0 { + next-hop 192.0.2.1 { + } + } + route6 ::/0 { + next-hop 2001:db8::1 { + } } - connect-on-demand - default-route auto - mtu 1492 - source-interface eth0 } } service { diff --git a/smoketest/configs/bgp-small-as b/smoketest/configs/bgp-small-as new file mode 100644 index 000000000..6b953a3f6 --- /dev/null +++ b/smoketest/configs/bgp-small-as @@ -0,0 +1,687 @@ +firewall { + all-ping enable + broadcast-ping disable + config-trap disable + group { + address-group NET-VYOS-HTTPS-4 { + address 10.0.150.73 + } + ipv6-network-group NET-VYOS-6 { + network 2001:db8:200::/40 + } + network-group NET-VYOS-4 { + network 10.0.150.0/23 + network 192.168.189.0/24 + } + port-group MY-NAS-PORTS { + port 80 + port 5000 + port 5001 + port 6022 + port 9443 + } + } + ipv6-name WAN-TO-VLAN15-6 { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + source { + group { + network-group NET-VYOS-6 + } + } + } + rule 1010 { + action accept + destination { + address 2001:db8:200:15::a + group { + port-group MY-NAS-PORTS + } + } + protocol tcp + } + } + ipv6-receive-redirects disable + ipv6-src-route disable + ip-src-route disable + log-martians enable + name WAN-TO-VLAN15-4 { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + source { + group { + network-group NET-VYOS-4 + } + } + } + rule 1000 { + action accept + destination { + group { + address-group NET-VYOS-HTTPS-4 + } + port 80,443 + } + protocol tcp + } + rule 1010 { + action accept + destination { + address 10.0.150.74 + group { + port-group MY-NAS-PORTS + } + } + protocol tcp + } + } + receive-redirects disable + send-redirects enable + source-validation disable + syn-cookies enable + twa-hazards-protection disable +} +high-availability { + vrrp { + group VLAN5-IPv4 { + interface eth0.5 + preempt-delay 180 + priority 250 + virtual-address 10.0.150.120/28 + vrid 5 + } + group VLAN5-IPv6 { + interface eth0.5 + preempt-delay 180 + priority 250 + virtual-address 2001:db8:200:f0::ffff/64 + vrid 6 + } + group VLAN10-IPv4 { + interface eth0.10 + preempt-delay 180 + priority 250 + virtual-address 10.0.150.62/26 + vrid 10 + } + group VLAN10-IPv6 { + interface eth0.10 + preempt-delay 180 + priority 250 + virtual-address 2001:db8:200:10::ffff/64 + virtual-address 2001:db8:200::ffff/64 + vrid 11 + } + group VLAN15-IPv4 { + interface eth0.15 + preempt-delay 180 + priority 250 + virtual-address 10.0.150.78/28 + vrid 15 + } + group VLAN15-IPv6 { + interface eth0.15 + preempt-delay 180 + priority 250 + virtual-address 2001:db8:200:15::ffff/64 + vrid 16 + } + group VLAN500-IPv4 { + interface eth0.500 + preempt-delay 180 + priority 250 + virtual-address 10.0.151.238/28 + vrid 238 + } + group VLAN500-IPv6 { + interface eth0.500 + preempt-delay 180 + priority 250 + virtual-address 2001:db8:200:50::ffff/64 + vrid 239 + } + group VLAN520-IPv4 { + interface eth0.520 + preempt-delay 180 + priority 250 + virtual-address 10.0.150.190/28 + vrid 52 + } + group VLAN520-IPv6 { + interface eth0.520 + preempt-delay 180 + priority 250 + virtual-address 2001:db8:200:520::ffff/64 + vrid 53 + } + group VLAN810-IPv4 { + interface eth0.810 + preempt-delay 180 + priority 250 + virtual-address 10.0.151.30/27 + vrid 80 + } + group VLAN810-IPv6 { + interface eth0.810 + preempt-delay 180 + priority 250 + virtual-address 2001:db8:200:102::ffff/64 + vrid 81 + } + sync-group VYOS { + member VLAN5-IPv4 + member VLAN5-IPv6 + member VLAN10-IPv4 + member VLAN10-IPv6 + member VLAN500-IPv4 + member VLAN500-IPv6 + member VLAN15-IPv4 + member VLAN15-IPv6 + member VLAN810-IPv6 + member VLAN810-IPv4 + member VLAN520-IPv4 + member VLAN520-IPv6 + } + } +} +interfaces { + dummy dum0 { + address 2001:db8:200:ffff::2/128 + address 10.0.151.251/32 + } + ethernet eth0 { + vif 5 { + address 10.0.150.121/28 + address 2001:db8:200:f0::4/64 + ip { + ospf { + authentication { + md5 { + key-id 10 { + md5-key vyosospfkey + } + } + } + cost 10 + dead-interval 40 + hello-interval 10 + network broadcast + priority 200 + retransmit-interval 5 + transmit-delay 5 + } + } + } + vif 10 { + address 2001:db8:200:10::1:ffff/64 + address 2001:db8:200::1:ffff/64 + address 10.0.150.60/26 + } + vif 15 { + address 10.0.150.76/28 + address 2001:db8:200:15::1:ffff/64 + firewall { + out { + ipv6-name WAN-TO-VLAN15-6 + name WAN-TO-VLAN15-4 + } + } + } + vif 50 { + address 192.168.189.2/24 + } + vif 110 { + address 2001:db8:200:101::ffff/64 + address 10.0.151.190/27 + address 10.0.151.158/28 + } + vif 410 { + address 10.0.151.206/28 + address 2001:db8:200:104::ffff/64 + } + vif 450 { + address 2001:db8:200:103::ffff/64 + address 10.0.151.142/29 + disable + } + vif 500 { + address 10.0.151.236/28 + address 2001:db8:200:50::1:ffff/64 + } + vif 520 { + address 10.0.150.188/26 + address 2001:db8:200:520::1:ffff/64 + } + vif 800 { + address 2001:db8:200:ff::104:1/112 + address 10.0.151.212/31 + } + vif 810 { + address 10.0.151.28/27 + address 2001:db8:200:102::1:ffff/64 + } + } + ethernet eth1 { + } + loopback lo { + } +} +policy { + prefix-list as65000-origin-v4 { + rule 10 { + action permit + prefix 10.0.150.0/23 + } + rule 100 { + action permit + prefix 0.0.0.0/0 + } + } + prefix-list6 as65000-origin-v6 { + rule 10 { + action permit + prefix 2001:db8:200::/40 + } + } + route-map as65010-in { + rule 10 { + action permit + set { + local-preference 30 + } + } + } + route-map as65010-out { + rule 10 { + action permit + set { + as-path-prepend "65000 65000" + } + } + } +} +protocols { + bgp 65000 { + address-family { + ipv4-unicast { + network 10.0.150.0/23 { + } + } + ipv6-unicast { + network 2001:db8:200::/40 { + } + } + } + neighbor 10.0.151.222 { + disable-send-community { + extended + standard + } + address-family { + ipv4-unicast { + default-originate { + } + prefix-list { + export as65000-origin-v4 + } + route-map { + export as65010-out + import as65010-in + } + soft-reconfiguration { + inbound + } + } + } + capability { + dynamic + } + remote-as 65010 + } + neighbor 10.0.151.252 { + peer-group VYOSv4 + } + neighbor 10.0.151.254 { + peer-group VYOSv4 + } + neighbor 2001:db8:200:ffff::3 { + peer-group VYOSv6 + } + neighbor 2001:db8:200:ffff::a { + peer-group VYOSv6 + } + neighbor 2001:db8:200:ff::101:2 { + address-family { + ipv6-unicast { + capability { + dynamic + } + prefix-list { + export as65000-origin-v6 + } + route-map { + import as65010-in + } + soft-reconfiguration { + inbound + } + } + } + remote-as 65010 + } + parameters { + default { + no-ipv4-unicast + } + log-neighbor-changes + router-id 10.0.151.251 + } + peer-group VYOSv4 { + address-family { + ipv4-unicast { + nexthop-self { + } + } + } + capability { + dynamic + } + remote-as 65000 + update-source dum0 + } + peer-group VYOSv6 { + address-family { + ipv6-unicast { + nexthop-self { + } + } + } + capability { + dynamic + } + remote-as 65000 + update-source dum0 + } + timers { + holdtime 30 + keepalive 10 + } + } + ospf { + area 0 { + area-type { + normal + } + authentication md5 + network 10.0.151.251/32 + network 10.0.151.208/31 + network 10.0.150.112/28 + } + parameters { + abr-type cisco + router-id 10.0.151.251 + } + passive-interface default + passive-interface-exclude dum0 + passive-interface-exclude eth0.5 + redistribute { + connected { + metric-type 2 + } + static { + metric-type 2 + } + } + } + ospfv3 { + area 0.0.0.0 { + interface dum0 + interface eth0.5 + } + parameters { + router-id 10.0.151.251 + } + redistribute { + connected { + } + static { + } + } + } + static { + route 10.0.0.0/8 { + MY-NAS { + distance 254 + } + } + route 172.16.0.0/12 { + MY-NAS { + distance 254 + } + } + route 192.168.0.0/16 { + MY-NAS { + distance 254 + } + } + route 193.148.249.144/32 { + next-hop 192.168.189.1 { + } + } + route 10.0.150.0/23 { + MY-NAS { + distance 254 + } + } + route 10.0.151.32/27 { + next-hop 10.0.151.5 { + } + } + route6 2001:db8:2fe:ffff::/64 { + next-hop 2001:db8:200:102::4 { + } + } + route6 2001:db8:2ff::/48 { + next-hop 2001:db8:200:101::1 { + } + } + route6 2001:db8:200::/40 { + MY-NAS { + distance 254 + } + } + } +} +service { + dhcp-server { + shared-network-name NET-VYOS-DHCP-1 { + subnet 10.0.151.224/28 { + default-router 10.0.151.238 + dns-server 10.0.150.2 + dns-server 10.0.150.1 + domain-name vyos.net + failover { + local-address 10.0.151.236 + name NET-VYOS-DHCP-1 + peer-address 10.0.151.237 + status primary + } + lease 1800 + range 0 { + start 10.0.151.225 + stop 10.0.151.237 + } + } + } + shared-network-name NET-VYOS-HOSTING-1 { + subnet 10.0.150.128/26 { + default-router 10.0.150.190 + dns-server 10.0.150.2 + dns-server 10.0.150.1 + domain-name vyos.net + failover { + local-address 10.0.150.188 + name NET-VYOS-HOSTING-1 + peer-address 10.0.150.189 + status primary + } + lease 604800 + range 0 { + start 10.0.150.129 + stop 10.0.150.187 + } + } + } + } + lldp { + interface all { + } + management-address 10.0.151.251 + snmp { + enable + } + } + router-advert { + interface eth4.500 { + default-preference high + name-server 2001:db8:200::1 + name-server 2001:db8:200::2 + prefix 2001:db8:200:50::/64 { + valid-lifetime infinity + } + } + interface eth4.520 { + default-preference high + name-server 2001:db8:200::1 + name-server 2001:db8:200::2 + prefix 2001:db8:200:520::/64 { + valid-lifetime infinity + } + } + } + snmp { + community public { + network 10.0.150.0/26 + network 2001:db8:200:10::/64 + } + contact noc@vyos.net + listen-address 10.0.151.251 { + } + listen-address 2001:db8:200:ffff::2 { + } + location "Jenkins" + } + ssh { + disable-host-validation + listen-address 10.0.151.251 + listen-address 2001:db8:200:ffff::2 + listen-address 192.168.189.2 + loglevel fatal + port 22 + } +} +system { + config-management { + commit-revisions 200 + } + console { + device ttyS0 { + speed 115200 + } + } + domain-name vyos.net + host-name vyos + login { + banner { + pre-login "VyOS - Network\n" + } + radius { + server 192.0.2.1 { + key SuperS3cretRADIUSkey + timeout 1 + } + server 192.0.2.2 { + key SuperS3cretRADIUSkey + timeout 1 + } + source-address 192.0.2.254 + } + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + name-server 192.0.2.1 + name-server 192.0.2.2 + name-server 2001:db8:200::1 + name-server 2001:db8:200::2 + ntp { + allow-clients { + address 10.0.150.0/23 + address 2001:db8:200::/40 + } + listen-address 10.0.151.251 + listen-address 2001:db8:200:ffff::2 + server 0.de.pool.ntp.org { + } + server 1.de.pool.ntp.org { + } + server 2.de.pool.ntp.org { + } + } + syslog { + global { + facility all { + level notice + } + facility protocols { + level debug + } + } + host 10.0.150.26 { + facility all { + level all + } + } + } + time-zone Europe/Berlin +} + + +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@18:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@6:salt@1:snmp@2:ssh@2:sstp@3:system@20:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" +// Release version: 1.3-beta-202101151942 diff --git a/smoketest/configs/bgp-small-internet-exchange b/smoketest/configs/bgp-small-internet-exchange new file mode 100644 index 000000000..d51f87c4a --- /dev/null +++ b/smoketest/configs/bgp-small-internet-exchange @@ -0,0 +1,488 @@ +interfaces { + ethernet eth0 { + address 192.0.2.100/25 + address 2001:db8:aaaa::ffff/64 + } + ethernet eth1 { + address 192.0.2.200/25 + address 2001:db8:bbbb::ffff/64 + } + loopback lo { + } +} +policy { + as-path-list bogon-asns { + rule 10 { + action permit + description "RFC 7607" + regex _0_ + } + rule 20 { + action permit + description "RFC 4893" + regex _23456_ + } + rule 30 { + action permit + description "RFC 5398/6996/7300" + regex _6449[6-9]_|_65[0-4][0-9][0-9]_|_655[0-4][0-9]_|_6555[0-1]_ + } + rule 40 { + action permit + description "IANA reserved" + regex _6555[2-9]_|_655[6-9][0-9]_|_65[6-9][0-9][0-9]_|_6[6-9][0-9][0-9][0-]_|_[7-9][0-9][0-9][0-9][0-9]_|_1[0-2][0-9][0-9][0-9][0-9]_|_130[0-9][0-9][0-9]_|_1310[0-6][0-9]_|_13107[01]_ + } + } + prefix-list bogon-v4 { + rule 10 { + action permit + le 32 + prefix 0.0.0.0/8 + } + rule 20 { + action permit + le 32 + prefix 10.0.0.0/8 + } + rule 30 { + action permit + le 32 + prefix 100.64.0.0/10 + } + rule 40 { + action permit + le 32 + prefix 127.0.0.0/8 + } + rule 50 { + action permit + le 32 + prefix 169.254.0.0/16 + } + rule 60 { + action permit + le 32 + prefix 172.16.0.0/12 + } + rule 70 { + action permit + le 32 + prefix 192.0.2.0/24 + } + rule 80 { + action permit + le 32 + prefix 192.88.99.0/24 + } + rule 90 { + action permit + le 32 + prefix 192.168.0.0/16 + } + rule 100 { + action permit + le 32 + prefix 198.18.0.0/15 + } + rule 110 { + action permit + le 32 + prefix 198.51.100.0/24 + } + rule 120 { + action permit + le 32 + prefix 203.0.113.0/24 + } + rule 130 { + action permit + le 32 + prefix 224.0.0.0/4 + } + rule 140 { + action permit + le 32 + prefix 240.0.0.0/4 + } + } + prefix-list IX-out-v4 { + rule 10 { + action permit + prefix 10.0.0.0/23 + } + rule 20 { + action permit + prefix 10.0.128.0/23 + } + } + prefix-list prefix-filter-v4 { + rule 10 { + action permit + ge 25 + prefix 0.0.0.0/0 + } + } + prefix-list6 bogon-v6 { + rule 10 { + action permit + description "RFC 4291 IPv4-compatible, loopback, et al" + le 128 + prefix ::/8 + } + rule 20 { + action permit + description "RFC 6666 Discard-Only" + le 128 + prefix 0100::/64 + } + rule 30 { + action permit + description "RFC 5180 BMWG" + le 128 + prefix 2001:2::/48 + } + rule 40 { + action permit + description "RFC 4843 ORCHID" + le 128 + prefix 2001:10::/28 + } + rule 50 { + action permit + description "RFC 3849 documentation" + le 128 + prefix 2001:db8::/32 + } + rule 60 { + action permit + description "RFC 7526 6to4 anycast relay" + le 128 + prefix 2002::/16 + } + rule 70 { + action permit + description "RFC 3701 old 6bone" + le 128 + prefix 3ffe::/16 + } + rule 80 { + action permit + description "RFC 4193 unique local unicast" + le 128 + prefix fc00::/7 + } + rule 90 { + action permit + description "RFC 4291 link local unicast" + le 128 + prefix fe80::/10 + } + rule 100 { + action permit + description "RFC 3879 old site local unicast" + le 128 + prefix fec0::/10 + } + rule 110 { + action permit + description "RFC 4291 multicast" + le 128 + prefix ff00::/8 + } + } + prefix-list6 prefix-filter-v6 { + rule 10 { + action permit + ge 49 + prefix ::/0 + } + } + prefix-list6 IX-out-v6 { + rule 10 { + action permit + prefix 2001:db8:100::/40 + } + rule 20 { + action permit + prefix 2001:db8:200::/40 + } + } + route-map eBGP-IN-v4 { + rule 10 { + action deny + match { + as-path bogon-asns + } + } + rule 20 { + action deny + match { + ip { + address { + prefix-list bogon-v4 + } + } + } + } + rule 30 { + action deny + match { + ip { + address { + prefix-list prefix-filter-v4 + } + } + } + } + rule 40 { + action permit + set { + local-preference 100 + metric 0 + } + } + } + route-map eBGP-IN-v6 { + rule 10 { + action deny + match { + as-path bogon-asns + } + } + rule 20 { + action deny + match { + ipv6 { + address { + prefix-list bogon-v6 + } + } + } + } + rule 30 { + action deny + match { + ipv6 { + address { + prefix-list prefix-filter-v6 + } + } + } + } + rule 40 { + action permit + set { + local-preference 100 + metric 0 + } + } + } + route-map IX-in-v4 { + rule 5 { + action permit + call eBGP-IN-v4 + on-match { + next + } + } + rule 10 { + action permit + } + } + route-map IX-out-v4 { + rule 10 { + action permit + match { + ip { + address { + prefix-list IX-out-v4 + } + } + } + } + } + route-map IX-in-v6 { + rule 5 { + action permit + call eBGP-IN-v6 + on-match { + next + } + } + rule 10 { + action permit + } + } + route-map IX-out-v6 { + rule 10 { + action permit + match { + ipv6 { + address { + prefix-list IX-out-v6 + } + } + } + } + } +} +protocols { + bgp 65000 { + address-family { + ipv4-unicast { + network 10.0.0.0/23 { + } + network 10.0.128.0/23 { + } + } + ipv6-unicast { + network 2001:db8:100::/40 { + } + network 2001:db8:200::/40 { + } + } + } + neighbor 192.0.2.1 { + description "Peering: IX-1 (Route Server)" + peer-group IXPeeringIPv4 + remote-as 65020 + } + neighbor 192.0.2.2 { + description "Peering: IX-1 (Route Server)" + peer-group IXPeeringIPv4 + remote-as 65020 + } + neighbor 192.0.2.3 { + description "Peering: IX-1 (Route Server)" + peer-group IXPeeringIPv4 + remote-as 65020 + } + neighbor 192.0.2.129 { + description "Peering: IX-2 (Route Server)" + peer-group IXPeeringIPv4 + remote-as 65030 + } + neighbor 192.0.2.130 { + description "Peering: IX-2 (Route Server)" + peer-group IXPeeringIPv4 + remote-as 65030 + } + neighbor 2001:db8:aaaa::1 { + description "Peering: IX-1 (Route Server)" + peer-group IXPeeringIPv6 + remote-as 65020 + } + neighbor 2001:db8:aaaa::2 { + description "Peering: IX-1 (Route Server)" + peer-group IXPeeringIPv6 + remote-as 65020 + } + neighbor 2001:db8:bbbb::1 { + description "Peering: IX-2 (Route Server)" + peer-group IXPeeringIPv6 + remote-as 65030 + } + neighbor 2001:db8:bbbb::2 { + description "Peering: IX-2 (Route Server)" + peer-group IXPeeringIPv6 + remote-as 65030 + } + parameters { + default { + no-ipv4-unicast + } + } + peer-group IXPeeringIPv4 { + address-family { + ipv4-unicast { + route-map { + export IX-out-v4 + } + soft-reconfiguration { + inbound + } + } + } + } + peer-group IXPeeringIPv6 { + address-family { + ipv6-unicast { + route-map { + export IX-out-v6 + } + soft-reconfiguration { + inbound + } + } + } + } + } + static { + route 10.0.0.0/23 { + blackhole { + distance 250 + } + } + route 10.0.128.0/23 { + blackhole { + distance 250 + } + } + route6 2001:db8:100::/40 { + blackhole { + distance 250 + } + } + route6 2001:db8:200::/40 { + blackhole { + distance 250 + } + } + } +} +service { + ssh { + } +} +system { + config-management { + commit-revisions 100 + } + console { + device ttyS0 { + speed 115200 + } + } + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/ + plaintext-password "" + } + } + } + ntp { + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } +} + + +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@13:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@6:salt@1:snmp@2:ssh@2:sstp@3:system@19:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webgui@1:webproxy@2:zone-policy@1" +// Release version: 1.3-rolling-202010241631 diff --git a/smoketest/configs/dialup-router-complex b/smoketest/configs/dialup-router-complex new file mode 100644 index 000000000..fef79ea56 --- /dev/null +++ b/smoketest/configs/dialup-router-complex @@ -0,0 +1,1662 @@ +firewall { + all-ping enable + broadcast-ping disable + config-trap disable + group { + address-group MEDIA-STREAMING-CLIENTS { + address 172.16.35.241 + address 172.16.35.242 + address 172.16.35.243 + } + address-group DMZ-WEBSERVER { + address 172.16.36.10 + address 172.16.36.40 + address 172.16.36.20 + } + address-group DMZ-RDP-SERVER { + address 172.16.33.40 + } + address-group DOMAIN-CONTROLLER { + address 172.16.100.10 + address 172.16.100.20 + } + address-group AUDIO-STREAM { + address 172.16.35.20 + address 172.16.35.21 + address 172.16.35.22 + address 172.16.35.23 + } + ipv6-network-group LOCAL-ADDRESSES { + network ff02::/64 + network fe80::/10 + } + network-group SSH-IN-ALLOW { + network 192.0.2.0/24 + network 10.0.0.0/8 + network 172.16.0.0/12 + network 192.168.0.0/16 + } + port-group SMART-TV-PORTS { + port 5005-5006 + port 80 + port 443 + port 3722 + } + } + ipv6-name ALLOW-ALL-6 { + default-action accept + } + ipv6-name ALLOW-BASIC-6 { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + state { + invalid enable + } + } + rule 10 { + action accept + protocol icmpv6 + } + } + ipv6-name ALLOW-ESTABLISHED-6 { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + state { + invalid enable + } + } + rule 10 { + action accept + destination { + group { + network-group LOCAL-ADDRESSES + } + } + protocol icmpv6 + source { + address fe80::/10 + } + } + rule 20 { + action accept + icmpv6 { + type echo-request + } + protocol icmpv6 + } + rule 21 { + action accept + icmpv6 { + type destination-unreachable + } + protocol icmpv6 + } + rule 22 { + action accept + icmpv6 { + type packet-too-big + } + protocol icmpv6 + } + rule 23 { + action accept + icmpv6 { + type time-exceeded + } + protocol icmpv6 + } + rule 24 { + action accept + icmpv6 { + type parameter-problem + } + protocol icmpv6 + } + } + ipv6-name WAN-LOCAL-6 { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + state { + invalid enable + } + } + rule 10 { + action accept + destination { + address ff02::/64 + } + protocol icmpv6 + source { + address fe80::/10 + } + } + rule 50 { + action accept + description DHCPv6 + destination { + address fe80::/10 + port 546 + } + protocol udp + source { + address fe80::/10 + port 547 + } + } + } + ipv6-receive-redirects disable + ipv6-src-route disable + ip-src-route disable + log-martians enable + name DMZ-GUEST { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + } + name DMZ-LAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + description "NTP and LDAP to AD DC" + destination { + group { + address-group DOMAIN-CONTROLLER + } + port 123,389,636 + } + protocol tcp_udp + } + rule 300 { + action accept + destination { + group { + address-group DMZ-RDP-SERVER + } + port 3389 + } + protocol tcp_udp + source { + address 172.16.36.20 + } + } + } + name DMZ-LOCAL { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 50 { + action accept + destination { + address 172.16.254.30 + port 53 + } + protocol tcp_udp + } + rule 123 { + action accept + destination { + port 123 + } + protocol udp + } + } + name DMZ-WAN { + default-action accept + } + name GUEST-DMZ { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + destination { + port 80,443 + } + protocol tcp + } + } + name GUEST-IOT { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + description "MEDIA-STREAMING-CLIENTS Devices to GUEST" + destination { + group { + address-group MEDIA-STREAMING-CLIENTS + } + } + protocol tcp_udp + } + rule 110 { + action accept + description "AUDIO-STREAM Devices to GUEST" + destination { + group { + address-group AUDIO-STREAM + } + } + protocol tcp_udp + } + rule 200 { + action accept + description "MCAST relay" + destination { + address 224.0.0.251 + port 5353 + } + protocol udp + } + rule 300 { + action accept + description "BCAST relay" + destination { + port 1900 + } + protocol udp + } + } + name GUEST-LAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + } + name GUEST-LOCAL { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 10 { + action accept + description DNS + destination { + address 172.31.0.254 + port 53 + } + protocol tcp_udp + } + rule 11 { + action accept + description DHCP + destination { + port 67 + } + protocol udp + } + rule 15 { + action accept + destination { + address 172.31.0.254 + } + protocol icmp + } + rule 200 { + action accept + description "MCAST relay" + destination { + address 224.0.0.251 + port 5353 + } + protocol udp + } + rule 210 { + action accept + description "AUDIO-STREAM Broadcast" + destination { + port 1900 + } + protocol udp + } + } + name GUEST-WAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 25 { + action accept + description SMTP + destination { + port 25,587 + } + protocol tcp + } + rule 53 { + action accept + destination { + port 53 + } + protocol tcp_udp + } + rule 60 { + action accept + source { + address 172.31.0.200 + } + } + rule 80 { + action accept + source { + address 172.31.0.200 + } + } + rule 100 { + action accept + protocol icmp + } + rule 110 { + action accept + description POP3 + destination { + port 110,995 + } + protocol tcp + } + rule 123 { + action accept + description "NTP Client" + destination { + port 123 + } + protocol udp + } + rule 143 { + action accept + description IMAP + destination { + port 143,993 + } + protocol tcp + } + rule 200 { + action accept + destination { + port 80,443 + } + protocol tcp + } + rule 500 { + action accept + description "L2TP IPSec" + destination { + port 500,4500 + } + protocol udp + } + rule 600 { + action accept + destination { + port 5222-5224 + } + protocol tcp + } + rule 601 { + action accept + destination { + port 3478-3497,4500,16384-16387,16393-16402 + } + protocol udp + } + rule 1000 { + action accept + source { + address 172.31.0.184 + } + } + } + name IOT-GUEST { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + description "MEDIA-STREAMING-CLIENTS Devices to IOT" + protocol tcp_udp + source { + group { + address-group MEDIA-STREAMING-CLIENTS + } + } + } + rule 110 { + action accept + description "AUDIO-STREAM Devices to IOT" + protocol tcp_udp + source { + group { + address-group AUDIO-STREAM + } + } + } + rule 200 { + action accept + description "MCAST relay" + destination { + address 224.0.0.251 + port 5353 + } + protocol udp + } + rule 300 { + action accept + description "BCAST relay" + destination { + port 1900 + } + protocol udp + } + } + name IOT-LAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + description "AppleTV to LAN" + destination { + group { + port-group SMART-TV-PORTS + } + } + protocol tcp_udp + source { + group { + address-group MEDIA-STREAMING-CLIENTS + } + } + } + rule 110 { + action accept + description "AUDIO-STREAM Devices to LAN" + protocol tcp_udp + source { + group { + address-group AUDIO-STREAM + } + } + } + } + name IOT-LOCAL { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 10 { + action accept + description DNS + destination { + address 172.16.254.30 + port 53 + } + protocol tcp_udp + } + rule 11 { + action accept + description DHCP + destination { + port 67 + } + protocol udp + } + rule 15 { + action accept + destination { + address 172.16.35.254 + } + protocol icmp + } + rule 200 { + action accept + description "MCAST relay" + destination { + address 224.0.0.251 + port 5353 + } + protocol udp + } + rule 201 { + action accept + description "MCAST relay" + destination { + address 172.16.35.254 + port 5353 + } + protocol udp + } + rule 210 { + action accept + description "AUDIO-STREAM Broadcast" + destination { + port 1900,1902,6969 + } + protocol udp + } + } + name IOT-WAN { + default-action accept + } + name LAN-DMZ { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 22 { + action accept + description "SSH into DMZ" + destination { + port 22 + } + protocol tcp + } + rule 100 { + action accept + destination { + group { + address-group DMZ-WEBSERVER + } + port 22,80,443 + } + protocol tcp + } + } + name LAN-GUEST { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + } + name LAN-IOT { + default-action accept + } + name LAN-LOCAL { + default-action accept + } + name LAN-WAN { + default-action accept + } + name LOCAL-DMZ { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + } + name LOCAL-GUEST { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 5 { + action accept + protocol icmp + } + rule 200 { + action accept + description "MCAST relay" + destination { + address 224.0.0.251 + port 5353 + } + protocol udp + } + rule 300 { + action accept + description "BCAST relay" + destination { + port 1900 + } + protocol udp + } + } + name LOCAL-IOT { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 5 { + action accept + protocol icmp + } + rule 200 { + action accept + description "MCAST relay" + destination { + address 224.0.0.251 + port 5353 + } + protocol udp + } + rule 300 { + action accept + description "BCAST relay" + destination { + port 1900,6969 + } + protocol udp + } + } + name LOCAL-LAN { + default-action accept + } + name LOCAL-WAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 10 { + action accept + protocol icmp + } + rule 50 { + action accept + description DNS + destination { + port 53 + } + protocol tcp_udp + } + rule 80 { + action accept + destination { + port 80,443 + } + protocol tcp + } + rule 123 { + action accept + description NTP + destination { + port 123 + } + protocol udp + } + } + name WAN-DMZ { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + destination { + address 172.16.36.10 + port 80,443 + } + protocol tcp + } + } + name WAN-GUEST { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 1000 { + action accept + destination { + address 172.31.0.184 + } + } + rule 8000 { + action accept + destination { + address 172.31.0.200 + port 10000 + } + protocol udp + } + } + name WAN-IOT { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + } + name WAN-LAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 1000 { + action accept + destination { + address 172.16.33.40 + port 3389 + } + protocol tcp + source { + group { + network-group SSH-IN-ALLOW + } + } + } + } + name WAN-LOCAL { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 22 { + action accept + destination { + port 22 + } + protocol tcp + source { + group { + network-group SSH-IN-ALLOW + } + } + } + } + options { + interface pppoe0 { + adjust-mss 1452 + adjust-mss6 1432 + } + } + receive-redirects disable + send-redirects enable + source-validation disable + syn-cookies enable + twa-hazards-protection disable +} +interfaces { + dummy dum0 { + address 172.16.254.30/32 + } + ethernet eth0 { + duplex auto + speed auto + vif 5 { + address 172.16.37.254/24 + } + vif 10 { + address 172.16.33.254/24 + } + vif 20 { + address 172.31.0.254/24 + } + vif 35 { + address 172.16.35.254/24 + } + vif 50 { + address 172.16.36.254/24 + } + vif 100 { + address 172.16.100.254/24 + } + vif 201 { + address 172.18.201.254/24 + } + vif 202 { + address 172.18.202.254/24 + } + vif 203 { + address 172.18.203.254/24 + } + vif 204 { + address 172.18.204.254/24 + } + } + ethernet eth1 { + vif 7 { + description FTTH-PPPoE + } + } + loopback lo { + address 172.16.254.30/32 + } + pppoe pppoe0 { + authentication { + password vyos + user vyos + } + default-route auto + description "FTTH 100/50MBit" + dhcpv6-options { + pd 0 { + interface eth0.10 { + address 1 + sla-id 10 + } + interface eth0.20 { + address 1 + sla-id 20 + } + length 56 + } + } + ipv6 { + address { + autoconf + } + } + mtu 1492 + no-peer-dns + source-interface eth1.7 + } +} +nat { + destination { + rule 100 { + description HTTP(S) + destination { + port 80,443 + } + inbound-interface pppoe0 + log + protocol tcp + translation { + address 172.16.36.10 + } + } + rule 1000 { + destination { + port 3389 + } + disable + inbound-interface pppoe0 + protocol tcp + translation { + address 172.16.33.40 + } + } + rule 8000 { + destination { + port 10000 + } + inbound-interface pppoe0 + log + protocol udp + translation { + address 172.31.0.200 + } + } + } + source { + rule 100 { + log + outbound-interface pppoe0 + source { + address 172.16.32.0/19 + } + translation { + address masquerade + } + } + rule 200 { + outbound-interface pppoe0 + source { + address 172.16.100.0/24 + } + translation { + address masquerade + } + } + rule 300 { + outbound-interface pppoe0 + source { + address 172.31.0.0/24 + } + translation { + address masquerade + } + } + rule 400 { + outbound-interface pppoe0 + source { + address 172.18.200.0/21 + } + translation { + address masquerade + } + } + } +} +protocols { + static { + interface-route6 2000::/3 { + next-hop-interface pppoe0 { + } + } + route 10.0.0.0/8 { + blackhole { + distance 254 + } + } + route 169.254.0.0/16 { + blackhole { + distance 254 + } + } + route 172.16.0.0/12 { + blackhole { + distance 254 + } + } + route 192.168.0.0/16 { + blackhole { + distance 254 + } + } + } +} +service { + dhcp-server { + shared-network-name BACKBONE { + authoritative + subnet 172.16.37.0/24 { + default-router 172.16.37.254 + dns-server 172.16.254.30 + domain-name vyos.net + domain-search vyos.net + lease 86400 + ntp-server 172.16.254.30 + range 0 { + start 172.16.37.120 + stop 172.16.37.149 + } + static-mapping AP1.wue3 { + ip-address 172.16.37.231 + mac-address 18:e8:29:6c:c3:a5 + } + } + } + shared-network-name GUEST { + authoritative + subnet 172.31.0.0/24 { + default-router 172.31.0.254 + dns-server 172.31.0.254 + domain-name vyos.net + domain-search vyos.net + lease 86400 + range 0 { + start 172.31.0.100 + stop 172.31.0.199 + } + static-mapping host01 { + ip-address 172.31.0.200 + mac-address 00:50:00:00:00:01 + } + static-mapping host02 { + ip-address 172.31.0.184 + mac-address 00:50:00:00:00:02 + } + } + } + shared-network-name IOT { + authoritative + subnet 172.16.35.0/24 { + default-router 172.16.35.254 + dns-server 172.16.254.30 + domain-name vyos.net + domain-search vyos.net + lease 86400 + ntp-server 172.16.254.30 + range 0 { + start 172.16.35.101 + stop 172.16.35.149 + } + } + } + shared-network-name LAN { + authoritative + subnet 172.16.33.0/24 { + default-router 172.16.33.254 + dns-server 172.16.254.30 + domain-name vyos.net + domain-search vyos.net + lease 86400 + ntp-server 172.16.254.30 + range 0 { + start 172.16.33.100 + stop 172.16.33.189 + } + } + } + } + dns { + forwarding { + allow-from 172.16.0.0/12 + cache-size 0 + domain 16.172.in-addr.arpa { + addnta + recursion-desired + server 172.16.100.10 + server 172.16.100.20 + server 172.16.110.30 + } + domain 18.172.in-addr.arpa { + addnta + recursion-desired + server 172.16.100.10 + server 172.16.100.20 + server 172.16.110.30 + } + domain vyos.net { + addnta + recursion-desired + server 172.16.100.20 + server 172.16.100.10 + server 172.16.110.30 + } + ignore-hosts-file + listen-address 172.16.254.30 + listen-address 172.31.0.254 + negative-ttl 60 + } + } + lldp { + legacy-protocols { + cdp + } + snmp { + enable + } + } + mdns { + repeater { + interface eth0.35 + interface eth0.10 + } + } + router-advert { + interface eth0.10 { + prefix ::/64 { + preferred-lifetime 2700 + valid-lifetime 5400 + } + } + interface eth0.20 { + prefix ::/64 { + preferred-lifetime 2700 + valid-lifetime 5400 + } + } + } + snmp { + community fooBar { + authorization ro + network 172.16.100.0/24 + } + contact "VyOS maintainers and contributors <maintainers@vyos.io>" + listen-address 172.16.254.30 { + port 161 + } + location "The Internet" + } + ssh { + disable-host-validation + port 22 + } +} +system { + config-management { + commit-revisions 200 + } + conntrack { + expect-table-size 2048 + hash-size 32768 + modules { + sip { + disable + } + } + table-size 262144 + timeout { + icmp 30 + other 600 + udp { + other 300 + stream 300 + } + } + } + console { + device ttyS0 { + speed 115200 + } + } + domain-name vyos.net + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/ + plaintext-password "" + } + } + } + name-server 172.16.254.30 + ntp { + allow-clients { + address 172.16.0.0/12 + } + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + option { + ctrl-alt-delete ignore + reboot-on-panic + startup-beep + } + syslog { + global { + facility all { + level debug + } + facility protocols { + level debug + } + } + host 172.16.100.1 { + facility all { + level warning + } + } + } + time-zone Europe/Berlin +} +traffic-policy { + shaper QoS { + bandwidth 50mbit + default { + bandwidth 100% + burst 15k + queue-limit 1000 + queue-type fq-codel + } + } +} +zone-policy { + zone DMZ { + default-action drop + from GUEST { + firewall { + name GUEST-DMZ + } + } + from LAN { + firewall { + name LAN-DMZ + } + } + from LOCAL { + firewall { + name LOCAL-DMZ + } + } + from WAN { + firewall { + name WAN-DMZ + } + } + interface eth0.50 + } + zone GUEST { + default-action drop + from DMZ { + firewall { + name DMZ-GUEST + } + } + from IOT { + firewall { + name IOT-GUEST + } + } + from LAN { + firewall { + name LAN-GUEST + } + } + from LOCAL { + firewall { + ipv6-name ALLOW-ALL-6 + name LOCAL-GUEST + } + } + from WAN { + firewall { + ipv6-name ALLOW-ESTABLISHED-6 + name WAN-GUEST + } + } + interface eth0.20 + } + zone IOT { + default-action drop + from GUEST { + firewall { + name GUEST-IOT + } + } + from LAN { + firewall { + name LAN-IOT + } + } + from LOCAL { + firewall { + name LOCAL-IOT + } + } + from WAN { + firewall { + name WAN-IOT + } + } + interface eth0.35 + } + zone LAN { + default-action drop + from DMZ { + firewall { + name DMZ-LAN + } + } + from GUEST { + firewall { + name GUEST-LAN + } + } + from IOT { + firewall { + name IOT-LAN + } + } + from LOCAL { + firewall { + ipv6-name ALLOW-ALL-6 + name LOCAL-LAN + } + } + from WAN { + firewall { + ipv6-name ALLOW-ESTABLISHED-6 + name WAN-LAN + } + } + interface eth0.5 + interface eth0.10 + interface eth0.100 + interface eth0.201 + interface eth0.202 + interface eth0.203 + interface eth0.204 + } + zone LOCAL { + default-action drop + from DMZ { + firewall { + name DMZ-LOCAL + } + } + from GUEST { + firewall { + ipv6-name ALLOW-ESTABLISHED-6 + name GUEST-LOCAL + } + } + from IOT { + firewall { + name IOT-LOCAL + } + } + from LAN { + firewall { + ipv6-name ALLOW-ALL-6 + name LAN-LOCAL + } + } + from WAN { + firewall { + ipv6-name WAN-LOCAL-6 + name WAN-LOCAL + } + } + local-zone + } + zone WAN { + default-action drop + from DMZ { + firewall { + name DMZ-WAN + } + } + from GUEST { + firewall { + ipv6-name ALLOW-ALL-6 + name GUEST-WAN + } + } + from IOT { + firewall { + name IOT-WAN + } + } + from LAN { + firewall { + ipv6-name ALLOW-ALL-6 + name LAN-WAN + } + } + from LOCAL { + firewall { + ipv6-name ALLOW-ALL-6 + name LOCAL-WAN + } + } + interface pppoe0 + } +} + + +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@18:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@6:salt@1:snmp@2:ssh@2:sstp@3:system@20:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" +// Release version: 1.3-beta-202101091250 diff --git a/smoketest/configs/dialup-router-medium-vpn b/smoketest/configs/dialup-router-medium-vpn new file mode 100644 index 000000000..dfb3d9621 --- /dev/null +++ b/smoketest/configs/dialup-router-medium-vpn @@ -0,0 +1,707 @@ +firewall { + all-ping enable + broadcast-ping disable + config-trap disable + ipv6-receive-redirects disable + ipv6-src-route disable + ip-src-route disable + log-martians enable + options { + interface vtun0 { + adjust-mss 1380 + } + interface vtun1 { + adjust-mss 1380 + } + interface vtun2 { + adjust-mss 1380 + } + interface wg0 { + adjust-mss 1380 + } + interface wg1 { + adjust-mss 1380 + } + } + receive-redirects disable + send-redirects enable + source-validation disable + syn-cookies disable + twa-hazards-protection enable +} +high-availability { + vrrp { + group LAN { + hello-source-address 192.168.0.250 + interface eth1 + peer-address 192.168.0.251 + priority 200 + virtual-address 192.168.0.1/24 + vrid 1 + } + sync-group failover-group { + member LAN + } + } +} +interfaces { + ethernet eth0 { + duplex auto + mtu 9000 + offload-options { + generic-receive on + generic-segmentation on + scatter-gather on + tcp-segmentation on + } + pppoe 0 { + default-route auto + mtu 1500 + name-server auto + password password + traffic-policy { + out shape-17mbit + } + user-id vyos + password vyos + } + smp-affinity auto + speed auto + } + ethernet eth1 { + address 192.168.0.250/24 + duplex auto + ip { + source-validation strict + } + mtu 9000 + offload-options { + generic-receive on + generic-segmentation on + scatter-gather on + tcp-segmentation on + } + policy { + route LAN-POLICY-BASED-ROUTING + } + smp-affinity auto + speed auto + traffic-policy { + out shape-94mbit + } + } + loopback lo { + } + openvpn vtun0 { + encryption aes256 + hash sha512 + ip { + source-validation strict + } + keep-alive { + failure-count 3 + interval 30 + } + mode client + openvpn-option "comp-lzo adaptive" + openvpn-option fast-io + openvpn-option persist-key + openvpn-option "reneg-sec 86400" + persistent-tunnel + remote-host 192.0.2.10 + tls { + ca-cert-file /config/auth/ovpn_test_ca.pem + cert-file /config/auth/ovpn_test_server.pem + key-file /config/auth/ovpn_test_server.key + auth-file /config/auth/ovpn_test_tls_auth.key + } + } + openvpn vtun1 { + authentication { + password vyos1 + username vyos1 + } + encryption aes256 + hash sha1 + keep-alive { + failure-count 3 + interval 30 + } + mode client + openvpn-option "comp-lzo adaptive" + openvpn-option "tun-mtu 1500" + openvpn-option "tun-mtu-extra 32" + openvpn-option "mssfix 1300" + openvpn-option persist-key + openvpn-option "mute 10" + openvpn-option route-nopull + openvpn-option fast-io + openvpn-option "reneg-sec 86400" + persistent-tunnel + protocol udp + remote-host 01.foo.com + remote-port 1194 + tls { + ca-cert-file /config/auth/ovpn_test_ca.pem + auth-file /config/auth/ovpn_test_tls_auth.key + } + } + openvpn vtun2 { + authentication { + password vyos2 + username vyos2 + } + disable + encryption aes256 + hash sha512 + keep-alive { + failure-count 3 + interval 30 + } + mode client + openvpn-option "tun-mtu 1500" + openvpn-option "tun-mtu-extra 32" + openvpn-option "mssfix 1300" + openvpn-option persist-key + openvpn-option "mute 10" + openvpn-option route-nopull + openvpn-option fast-io + openvpn-option remote-random + openvpn-option "reneg-sec 86400" + persistent-tunnel + protocol udp + remote-host 01.myvpn.com + remote-host 02.myvpn.com + remote-host 03.myvpn.com + remote-port 1194 + tls { + ca-cert-file /config/auth/ovpn_test_ca.pem + auth-file /config/auth/ovpn_test_tls_auth.key + } + } + wireguard wg0 { + address 192.168.10.1/24 + peer red { + allowed-ips 192.168.10.4/32 + persistent-keepalive 20 + preshared-key CumyXX7osvUT9AwnS+m2TEfCaL0Ptc2LfuZ78Sujuk8= + pubkey ALGWvMJCKpHF2tVH3hEIHqUe9iFfAmZATUUok/WQzks= + } + peer green { + allowed-ips 192.168.10.21/32 + persistent-keepalive 25 + preshared-key LQ9qmlTh9G4nZu4UgElxRUwg7JB/qoV799aADJOijnY= + pubkey 5iQUD3VoCDBTPXAPHOwUJ0p7xzKGHEY/wQmgvBVmaFI= + } + peer blue { + allowed-ips 192.168.10.3/32 + persistent-keepalive 20 + preshared-key ztFDOY9UyaDvn8N3X97SFMDwIfv7EEfuUIPP2yab6UI= + pubkey G4pZishpMRrLmd96Kr6V7LIuNGdcUb81gWaYZ+FWkG0= + } + peer pink { + allowed-ips 192.168.10.14/32 + allowed-ips 192.168.10.16/32 + persistent-keepalive 25 + preshared-key Qi9Odyx0/5itLPN5C5bEy3uMX+tmdl15QbakxpKlWqQ= + pubkey i4qNPmxyy9EETL4tIoZOLKJF4p7IlVmpAE15gglnAk4= + } + port 7777 + } + wireguard wg1 { + address 10.89.90.2/30 + peer sam { + allowed-ips 10.1.1.0/24 + allowed-ips 10.89.90.1/32 + endpoint 192.0.2.45:1200 + persistent-keepalive 20 + preshared-key XpFtzx2Z+nR8pBv9/sSf7I94OkZkVYTz0AeU5Q/QQUE= + pubkey v5zfKGvH6W/lfDXJ0en96lvKo1gfFxMUWxe02+Fj5BU= + } + port 7778 + } +} +nat { + destination { + rule 50 { + destination { + port 49371 + } + inbound-interface pppoe0 + protocol tcp_udp + translation { + address 192.168.0.5 + } + } + rule 51 { + destination { + port 58050-58051 + } + inbound-interface pppoe0 + protocol tcp + translation { + address 192.168.0.5 + } + } + rule 52 { + destination { + port 22067-22070 + } + inbound-interface pppoe0 + protocol tcp + translation { + address 192.168.0.5 + } + } + rule 53 { + destination { + port 34342 + } + inbound-interface pppoe0 + protocol tcp_udp + translation { + address 192.168.0.121 + } + } + rule 54 { + destination { + port 45459 + } + inbound-interface pppoe0 + protocol tcp_udp + translation { + address 192.168.0.120 + } + } + rule 55 { + destination { + port 22 + } + inbound-interface pppoe0 + protocol tcp + translation { + address 192.168.0.5 + } + } + rule 56 { + destination { + port 8920 + } + inbound-interface pppoe0 + protocol tcp + translation { + address 192.168.0.5 + } + } + rule 60 { + destination { + port 80,443 + } + inbound-interface pppoe0 + protocol tcp + translation { + address 192.168.0.5 + } + } + rule 70 { + destination { + port 5001 + } + inbound-interface pppoe0 + protocol tcp + translation { + address 192.168.0.5 + } + } + rule 80 { + destination { + port 25 + } + inbound-interface pppoe0 + protocol tcp + translation { + address 192.168.0.5 + } + } + rule 90 { + destination { + port 8123 + } + inbound-interface pppoe0 + protocol tcp + translation { + address 192.168.0.7 + } + } + rule 91 { + destination { + port 1880 + } + inbound-interface pppoe0 + protocol tcp + translation { + address 192.168.0.7 + } + } + rule 500 { + destination { + address !192.168.0.0/24 + port 53 + } + inbound-interface eth1 + protocol tcp_udp + source { + address !192.168.0.1-192.168.0.5 + } + translation { + address 192.168.0.1 + } + } + } + source { + rule 1000 { + outbound-interface pppoe0 + translation { + address masquerade + } + } + rule 2000 { + outbound-interface vtun0 + source { + address 192.168.0.0/16 + } + translation { + address masquerade + } + } + rule 3000 { + outbound-interface vtun1 + translation { + address masquerade + } + } + } +} +policy { + prefix-list user2-routes { + rule 1 { + action permit + prefix 10.1.1.0/24 + } + } + prefix-list user1-routes { + rule 1 { + action permit + prefix 192.168.0.0/24 + } + } + route LAN-POLICY-BASED-ROUTING { + rule 10 { + destination { + } + disable + set { + table 10 + } + source { + address 192.168.0.119/32 + } + } + rule 20 { + destination { + } + set { + table 100 + } + source { + address 192.168.0.240 + } + } + } + route-map rm-static-to-bgp { + rule 10 { + action permit + match { + ip { + address { + prefix-list user1-routes + } + } + } + } + rule 100 { + action deny + } + } +} +protocols { + bgp 64590 { + address-family { + ipv4-unicast { + redistribute { + connected { + route-map rm-static-to-bgp + } + } + } + } + neighbor 10.89.90.1 { + address-family { + ipv4-unicast { + nexthop-self + prefix-list { + export user1-routes + import user2-routes + } + soft-reconfiguration { + inbound + } + } + } + password ericandre2020 + remote-as 64589 + } + parameters { + log-neighbor-changes + router-id 10.89.90.2 + } + } + static { + interface-route 100.64.160.23/32 { + next-hop-interface pppoe0 { + } + } + interface-route 100.64.165.25/32 { + next-hop-interface pppoe0 { + } + } + interface-route 100.64.165.26/32 { + next-hop-interface pppoe0 { + } + } + interface-route 100.64.198.0/24 { + next-hop-interface vtun0 { + } + } + table 10 { + interface-route 0.0.0.0/0 { + next-hop-interface vtun1 { + } + } + } + table 100 { + route 0.0.0.0/0 { + next-hop 192.168.10.5 { + } + } + } + } +} +service { + conntrack-sync { + accept-protocol tcp,udp,icmp + disable-external-cache + event-listen-queue-size 8 + expect-sync all + failover-mechanism { + vrrp { + sync-group failover-group + } + } + interface eth1 { + peer 192.168.0.251 + } + sync-queue-size 8 + } + dhcp-server { + shared-network-name LAN { + authoritative + subnet 192.168.0.0/24 { + default-router 192.168.0.1 + dns-server 192.168.0.1 + domain-name vyos.net + domain-search vyos.net + failover { + local-address 192.168.0.250 + name DHCP02 + peer-address 192.168.0.251 + status primary + } + lease 86400 + range LANDynamic { + start 192.168.0.200 + stop 192.168.0.240 + } + static-mapping IPTV { + ip-address 192.168.0.104 + mac-address 00:50:01:31:b5:f6 + } + static-mapping McPrintus { + ip-address 192.168.0.60 + mac-address 00:50:01:58:ac:95 + static-mapping-parameters "option domain-name-servers 192.168.0.6,192.168.0.17;" + } + static-mapping Audio { + ip-address 192.168.0.107 + mac-address 00:50:01:dc:91:14 + } + static-mapping Mobile01 { + ip-address 192.168.0.109 + mac-address 00:50:01:bc:ac:51 + static-mapping-parameters "option domain-name-servers 192.168.0.6,192.168.0.17;" + } + static-mapping sand { + ip-address 192.168.0.110 + mac-address 00:50:01:af:c5:d2 + } + static-mapping pearTV { + ip-address 192.168.0.101 + mac-address 00:50:01:ba:62:79 + } + static-mapping camera1 { + ip-address 192.168.0.11 + mac-address 00:50:01:70:b9:4d + static-mapping-parameters "option domain-name-servers 192.168.0.6,192.168.0.17;" + } + static-mapping camera2 { + ip-address 192.168.0.12 + mac-address 00:50:01:70:b7:4f + static-mapping-parameters "option domain-name-servers 192.168.0.6,192.168.0.17;" + } + } + } + } + dns { + forwarding { + allow-from 192.168.0.0/16 + cache-size 8192 + dnssec off + listen-address 192.168.0.1 + name-server 100.64.0.1 + name-server 100.64.0.2 + } + } + snmp { + community AwesomeCommunity { + authorization ro + client 127.0.0.1 + network 192.168.0.0/24 + } + } + ssh { + access-control { + allow { + user vyos + } + } + client-keepalive-interval 60 + listen-address 192.168.0.1 + listen-address 192.168.10.1 + listen-address 192.168.0.250 + } +} +system { + config-management { + commit-revisions 100 + } + console { + device ttyS0 { + speed 115200 + } + } + host-name vyos + ip { + arp { + table-size 1024 + } + } + login { + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + name-server 192.168.0.1 + ntp { + allow-clients { + address 192.168.0.0/16 + } + listen-address 192.168.0.1 + listen-address 192.168.0.250 + server nz.pool.ntp.org { + prefer + } + } + options { + beep-if-fully-booted + ctrl-alt-del-action ignore + reboot-on-panic true + } + static-host-mapping { + host-name host104.vyos.net { + inet 192.168.0.104 + } + host-name host60.vyos.net { + inet 192.168.0.60 + } + host-name host107.vyos.net { + inet 192.168.0.107 + } + host-name host109.vyos.net { + inet 192.168.0.109 + } + } + sysctl { + custom net.core.default_qdisc { + value fq + } + custom net.ipv4.tcp_congestion_control { + value bbr + } + } + syslog { + global { + facility all { + level info + } + } + host 192.168.0.252 { + facility all { + level debug + protocol udp + } + } + } + task-scheduler { + task Update-Blacklists { + executable { + path /config/scripts/vyos-foo-update.script + } + interval 3h + } + } + time-zone Pacific/Auckland +} +traffic-policy { + shaper shape-17mbit { + bandwidth 17mbit + default { + bandwidth 100% + burst 15k + queue-type fq-codel + } + } + shaper shape-94mbit { + bandwidth 94mbit + default { + bandwidth 100% + burst 15k + queue-type fq-codel + } + } +} +/* Warning: Do not remove the following line. */ +/* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pptp@1:qos@1:quagga@6:snmp@1:ssh@1:system@9:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */ +/* Release version: 1.2.6 */ diff --git a/smoketest/configs/isis-small b/smoketest/configs/isis-small new file mode 100644 index 000000000..2c42ac9c4 --- /dev/null +++ b/smoketest/configs/isis-small @@ -0,0 +1,105 @@ +interfaces { + dummy dum0 { + address 203.0.113.1/24 + } + ethernet eth0 { + duplex auto + speed auto + } + ethernet eth1 { + address 192.0.2.1/24 + duplex auto + speed auto + } + ethernet eth2 { + duplex auto + speed auto + } + ethernet eth3 { + duplex auto + speed auto + } +} +policy { + prefix-list EXPORT-ISIS { + rule 10 { + action permit + prefix 203.0.113.0/24 + } + } + route-map EXPORT-ISIS { + rule 10 { + action permit + match { + ip { + address { + prefix-list EXPORT-ISIS + } + } + } + } + } +} +protocols { + isis FOO { + interface eth1 { + bfd + } + net 49.0001.1921.6800.1002.00 + redistribute { + ipv4 { + connected { + level-2 { + route-map EXPORT-ISIS + } + } + } + } + } +} +system { + config-management { + commit-revisions 200 + } + console { + device ttyS0 { + speed 115200 + } + } + domain-name vyos.io + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/ + plaintext-password "" + } + level admin + } + } + ntp { + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } + time-zone Europe/Berlin +} + + +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@18:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@7:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@20:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" +// Release version: 1.3.0-rc1 + diff --git a/smoketest/configs/ospf-small b/smoketest/configs/ospf-small new file mode 100644 index 000000000..d95ba4ea4 --- /dev/null +++ b/smoketest/configs/ospf-small @@ -0,0 +1,142 @@ +interfaces { + dummy dum0 { + address 172.18.254.201/32 + } + ethernet eth0 { + duplex auto + smp-affinity auto + speed auto + vif 201 { + address 172.18.201.10/24 + ip { + ospf { + authentication { + md5 { + key-id 10 { + md5-key OSPFVyOSNET + } + } + } + dead-interval 40 + hello-interval 10 + priority 1 + retransmit-interval 5 + transmit-delay 1 + } + } + ipv6 { + ospfv3 { + bfd + cost 40 + } + } + } + } + ethernet eth1 { + duplex auto + smp-affinity auto + speed auto + ipv6 { + ospfv3 { + bfd + cost 60 + mtu-ignore + network broadcast + priority 20 + } + } + } +} +protocols { + ospf { + area 0 { + network 172.18.201.0/24 + network 172.18.254.201/32 + } + log-adjacency-changes { + } + parameters { + abr-type cisco + router-id 172.18.254.201 + } + passive-interface default + passive-interface-exclude eth0.201 + } + ospfv3 { + area 0.0.0.0 { + interface eth0 + interface eth1 + interface eth2 + } + } + static { + route 0.0.0.0/0 { + next-hop 172.18.201.254 { + distance 10 + } + } + } +} +service { + lldp { + interface all { + } + } + snmp { + community public { + authorization ro + network 172.16.100.0/24 + } + contact "VyOS maintainers and contributors <maintainers@vyos.io>" + location "Jenkins" + } + ssh { + disable-host-validation + port 22 + } +} +system { + config-management { + commit-revisions 200 + } + console { + device ttyS0 { + speed 115200 + } + } + domain-name vyos.net + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/ + plaintext-password "" + } + level admin + } + } + name-server 172.16.254.30 + ntp { + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } + time-zone Europe/Berlin +} + +/* Warning: Do not remove the following line. */ +/* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pptp@1:qos@1:quagga@6:snmp@1:ssh@1:system@9:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */ +/* Release version: 1.2.6 */ diff --git a/smoketest/configs/rip-router b/smoketest/configs/rip-router new file mode 100644 index 000000000..09cb11a45 --- /dev/null +++ b/smoketest/configs/rip-router @@ -0,0 +1,267 @@ +interfaces { + dummy dum0 { + address 192.0.2.0/32 + } + ethernet eth0 { + duplex auto + ip { + rip { + authentication { + md5 1 { + password VyOSsecure + } + } + split-horizon { + poison-reverse + } + } + } + ipv6 { + ripng { + split-horizon { + poison-reverse + } + } + } + smp-affinity auto + speed auto + address 172.18.202.10/24 + } + ethernet eth1 { + duplex auto + smp-affinity auto + speed auto + vif 20 { + ip { + rip { + authentication { + plaintext-password VyOSsecure + } + split-horizon { + poison-reverse + } + } + } + ipv6 { + ripng { + split-horizon { + disable + } + } + } + } + vif-s 200 { + ip { + rip { + authentication { + md5 1 { + password VyOSsecure + } + } + split-horizon { + disable + } + } + } + ipv6 { + ripng { + split-horizon { + poison-reverse + } + } + } + vif-c 2000 { + ip { + rip { + authentication { + md5 1 { + password VyOSsecure + } + } + } + } + } + vif-c 3000 { + ip { + rip { + split-horizon { + disable + } + } + } + ipv6 { + ripng { + split-horizon { + poison-reverse + } + } + } + } + } + } +} +policy { + access-list6 198 { + rule 10 { + action permit + source { + any + } + } + } + access-list6 199 { + rule 20 { + action deny + source { + any + } + } + } + prefix-list6 bar-prefix { + rule 200 { + action deny + prefix 2001:db8::/32 + } + } + prefix-list6 foo-prefix { + rule 100 { + action permit + prefix 2001:db8::/32 + } + } + route-map FooBar123 { + rule 10 { + action permit + } + } +} +protocols { + rip { + default-distance 20 + default-information { + originate + } + interface eth0 + interface eth1.20 + interface eth1.200 + interface eth1.200.2000 + interface eth1.200.3000 + network 192.168.0.0/24 + redistribute { + connected { + } + } + } + ripng { + aggregate-address 2001:db8:1000::/48 + default-information { + originate + } + default-metric 8 + distribute-list { + access-list { + in 198 + out 199 + } + interface eth0 { + access-list { + in 198 + out 199 + } + prefix-list { + in foo-prefix + out bar-prefix + } + } + interface eth1 { + access-list { + in 198 + out 199 + } + prefix-list { + in foo-prefix + out bar-prefix + } + } + interface eth2 { + access-list { + in 198 + out 199 + } + prefix-list { + in foo-prefix + out bar-prefix + } + } + prefix-list { + in foo-prefix + out bar-prefix + } + } + interface eth0 + interface eth1 + interface eth2 + network 2001:db8:1000::/64 + network 2001:db8:1001::/64 + network 2001:db8:2000::/64 + network 2001:db8:2001::/64 + passive-interface default + redistribute { + connected { + metric 8 + route-map FooBar123 + } + static { + metric 8 + route-map FooBar123 + } + } + route 2001:db8:1000::/64 + } +} +service { + ssh { + port 22 + } +} +system { + config-management { + commit-revisions 100 + } + console { + device ttyS0 { + speed 115200 + } + } + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + ntp { + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } +} + +/* Warning: Do not remove the following line. */ +/* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pptp@1:qos@1:quagga@6:snmp@1:ssh@1:system@10:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */ +/* Release version: 1.2.6-S1 */ diff --git a/smoketest/configs/tunnel-broker b/smoketest/configs/tunnel-broker new file mode 100644 index 000000000..b52ba2541 --- /dev/null +++ b/smoketest/configs/tunnel-broker @@ -0,0 +1,142 @@ +interfaces { + dummy dum0 { + address 192.0.2.0/32 + } + dummy dum1 { + address 192.0.2.1/32 + } + dummy dum2 { + address 192.0.2.2/32 + } + dummy dum3 { + address 192.0.2.3/32 + } + dummy dum4 { + address 192.0.2.4/32 + } + ethernet eth0 { + duplex auto + smp-affinity auto + speed auto + address 172.18.202.10/24 + } + l2tpv3 l2tpeth10 { + description "L2 VPN Tunnel" + destination-port 5010 + encapsulation ip + local-ip 172.18.202.10 + mtu 1500 + peer-session-id 110 + peer-tunnel-id 10 + remote-ip 172.18.254.201 + session-id 110 + source-port 5010 + tunnel-id 10 + } + l2tpv3 l2tpeth20 { + description "L2 VPN Tunnel" + destination-port 5020 + encapsulation ip + local-ip 172.18.202.10 + mtu 1500 + peer-session-id 120 + peer-tunnel-id 20 + remote-ip 172.18.254.202 + session-id 120 + source-port 5020 + tunnel-id 20 + } + l2tpv3 l2tpeth30 { + description "L2 VPN Tunnel" + destination-port 5030 + encapsulation ip + local-ip 172.18.202.10 + mtu 1500 + peer-session-id 130 + peer-tunnel-id 30 + remote-ip 172.18.254.203 + session-id 130 + source-port 5030 + tunnel-id 30 + } + tunnel tun100 { + address 172.16.0.1/30 + encapsulation gre-bridge + local-ip 192.0.2.0 + remote-ip 192.0.2.100 + } + tunnel tun200 { + address 172.16.0.5/30 + encapsulation gre + local-ip 192.0.2.1 + remote-ip 192.0.2.101 + } + tunnel tun300 { + address 172.16.0.9/30 + encapsulation ipip + local-ip 192.0.2.2 + remote-ip 192.0.2.102 + } + tunnel tun400 { + address 172.16.0.13/30 + encapsulation gre-bridge + local-ip 192.0.2.3 + remote-ip 192.0.2.103 + } + tunnel tun500 { + address 172.16.0.17/30 + encapsulation gre + local-ip 192.0.2.4 + remote-ip 192.0.2.104 + } +} +protocols { + static { + route 0.0.0.0/0 { + next-hop 172.18.202.1 { + distance 10 + } + } + } +} +system { + config-management { + commit-revisions 100 + } + console { + device ttyS0 { + speed 115200 + } + } + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + ntp { + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } +} + +/* Warning: Do not remove the following line. */ +/* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pptp@1:qos@1:quagga@6:snmp@1:ssh@1:system@10:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */ +/* Release version: 1.2.6-S1 */ diff --git a/smoketest/configs/vrf-basic b/smoketest/configs/vrf-basic new file mode 100644 index 000000000..ded33f683 --- /dev/null +++ b/smoketest/configs/vrf-basic @@ -0,0 +1,231 @@ +interfaces { + ethernet eth0 { + address 192.0.2.1/24 + } + ethernet eth1 { + duplex auto + speed auto + vrf green + } + ethernet eth2 { + vrf red + } +} +protocols { + static { + route 0.0.0.0/0 { + next-hop 192.0.2.254 { + distance 10 + } + } + table 10 { + interface-route 1.0.0.0/8 { + next-hop-interface eth0 { + distance 20 + } + } + interface-route 2.0.0.0/8 { + next-hop-interface eth0 { + distance 20 + } + } + interface-route 3.0.0.0/8 { + next-hop-interface eth0 { + distance 20 + } + } + } + table 20 { + interface-route 4.0.0.0/8 { + next-hop-interface eth0 { + distance 20 + } + } + interface-route 5.0.0.0/8 { + next-hop-interface eth0 { + distance 50 + } + } + interface-route 6.0.0.0/8 { + next-hop-interface eth0 { + distance 60 + } + } + interface-route6 2001:db8:100::/40 { + next-hop-interface eth1 { + distance 20 + } + } + interface-route6 2001:db8::/40 { + next-hop-interface eth1 { + distance 10 + } + } + route 11.0.0.0/8 { + next-hop 1.1.1.1 { + next-hop-interface eth0 + } + } + route 12.0.0.0/8 { + next-hop 1.1.1.1 { + next-hop-interface eth0 + } + } + route 13.0.0.0/8 { + next-hop 1.1.1.1 { + next-hop-interface eth0 + } + } + } + table 30 { + interface-route6 2001:db8:200::/40 { + next-hop-interface eth1 { + distance 20 + } + } + route 14.0.0.0/8 { + next-hop 2.2.1.1 { + next-hop-interface eth1 + } + } + route 15.0.0.0/8 { + next-hop 2.2.1.1 { + next-hop-interface eth1 + } + } + } + } + vrf green { + static { + interface-route 100.0.0.0/8 { + next-hop-interface eth0 { + distance 200 + next-hop-vrf default + } + } + interface-route 101.0.0.0/8 { + next-hop-interface eth0 { + next-hop-vrf default + } + next-hop-interface eth1 { + } + } + interface-route6 2001:db8:300::/40 { + next-hop-interface eth1 { + distance 20 + next-hop-vrf default + } + } + route 20.0.0.0/8 { + next-hop 1.1.1.1 { + next-hop-interface eth1 + next-hop-vrf default + } + } + route 21.0.0.0/8 { + next-hop 2.2.1.1 { + next-hop-interface eth1 + next-hop-vrf default + } + } + route6 2001:db8:100::/40 { + next-hop fe80::1 { + interface eth0 + next-hop-vrf default + } + } + } + } + vrf red { + static { + interface-route 103.0.0.0/8 { + next-hop-interface eth0 { + distance 201 + next-hop-vrf default + } + } + interface-route 104.0.0.0/8 { + next-hop-interface eth0 { + next-hop-vrf default + } + next-hop-interface eth1 { + next-hop-vrf default + } + } + interface-route6 2001:db8:400::/40 { + next-hop-interface eth1 { + distance 24 + next-hop-vrf default + } + } + route 30.0.0.0/8 { + next-hop 1.1.1.1 { + next-hop-interface eth1 + } + } + route 40.0.0.0/8 { + next-hop 2.2.1.1 { + next-hop-interface eth1 + next-hop-vrf default + } + } + route6 2001:db8:100::/40 { + next-hop fe80::1 { + interface eth0 + next-hop-vrf default + } + } + } + } +} +system { + config-management { + commit-revisions 100 + } + console { + device ttyS0 { + speed 115200 + } + } + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + nt + ntp { + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } + time-zone Europe/Berlin +} +vrf { + name green { + table 1000 + } + name red { + table 2000 + } +} + +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@18:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@6:salt@1:snmp@2:ssh@2:sstp@3:system@20:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" +// Release version: 1.3-beta-202101231023 diff --git a/smoketest/configs/vrf-bgp b/smoketest/configs/vrf-bgp new file mode 100644 index 000000000..4ad372a36 --- /dev/null +++ b/smoketest/configs/vrf-bgp @@ -0,0 +1,166 @@ +interfaces { + ethernet eth0 { + address 192.0.2.1/24 + } + ethernet eth1 { + vrf black + } + ethernet eth2 { + vrf black + } +} +protocols { + ospf { + area 0 { + network 192.0.2.0/24 + } + interface eth0 { + authentication { + md5 { + key-id 10 { + md5-key ospfkey + } + } + } + } + log-adjacency-changes { + } + parameters { + abr-type cisco + router-id 1.2.3.4 + } + passive-interface default + passive-interface-exclude eth0 + } +} +system { + config-management { + commit-revisions 100 + } + console { + device ttyS0 { + speed 115200 + } + } + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + nt + ntp { + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } + time-zone Europe/Berlin +} +vrf { + name black { + protocols { + bgp 65000 { + address-family { + ipv4-unicast { + network 10.0.150.0/23 { + } + } + ipv6-unicast { + network 2001:db8:200::/40 { + } + } + } + neighbor 10.0.151.222 { + disable-send-community { + extended + standard + } + address-family { + ipv4-unicast { + default-originate { + } + soft-reconfiguration { + inbound + } + } + } + capability { + dynamic + } + remote-as 65010 + } + neighbor 10.0.151.252 { + peer-group VYOSv4 + } + neighbor 10.0.151.254 { + peer-group VYOSv4 + } + neighbor 2001:db8:200:ffff::3 { + peer-group VYOSv6 + } + neighbor 2001:db8:200:ffff::a { + peer-group VYOSv6 + } + neighbor 2001:db8:200:ff::101:2 { + remote-as 65010 + } + parameters { + default { + no-ipv4-unicast + } + log-neighbor-changes + router-id 10.0.151.251 + } + peer-group VYOSv4 { + address-family { + ipv4-unicast { + nexthop-self { + } + } + } + capability { + dynamic + } + remote-as 65000 + update-source dum0 + } + peer-group VYOSv6 { + address-family { + ipv6-unicast { + nexthop-self { + } + } + } + capability { + dynamic + } + remote-as 65000 + update-source dum0 + } + } + + } + table 2000 + } +} + + +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@2:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@20:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:nat66@1:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@9:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@20:vrf@2:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" +// Release version: 1.4-rolling-202103130218 diff --git a/smoketest/configs/vrf-ospf b/smoketest/configs/vrf-ospf new file mode 100644 index 000000000..7855e86bf --- /dev/null +++ b/smoketest/configs/vrf-ospf @@ -0,0 +1,145 @@ +interfaces { + ethernet eth0 { + address 192.0.2.1/24 + } + ethernet eth1 { + vrf red + } + ethernet eth2 { + vrf blue + } +} +protocols { + ospf { + area 0 { + network 192.0.2.0/24 + } + interface eth0 { + authentication { + md5 { + key-id 10 { + md5-key ospfkey + } + } + } + } + log-adjacency-changes { + } + parameters { + abr-type cisco + router-id 1.2.3.4 + } + passive-interface default + passive-interface-exclude eth0 + } +} +system { + config-management { + commit-revisions 100 + } + console { + device ttyS0 { + speed 115200 + } + } + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + nt + ntp { + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } + time-zone Europe/Berlin +} +vrf { + name blue { + protocols { + ospf { + area 0 { + network 172.18.201.0/24 + } + interface eth2 { + authentication { + md5 { + key-id 30 { + md5-key vyoskey456 + } + } + } + dead-interval 40 + hello-interval 10 + priority 1 + retransmit-interval 5 + transmit-delay 1 + } + log-adjacency-changes { + } + parameters { + abr-type cisco + router-id 5.6.7.8 + } + passive-interface default + passive-interface-exclude eth2 + } + } + table 2000 + } + name red { + protocols { + ospf { + area 0 { + network 172.18.202.0/24 + } + interface eth1 { + authentication { + md5 { + key-id 20 { + md5-key vyoskey123 + } + } + } + dead-interval 40 + hello-interval 10 + priority 1 + retransmit-interval 5 + transmit-delay 1 + } + log-adjacency-changes { + } + parameters { + abr-type cisco + router-id 9.10.11.12 + } + passive-interface default + passive-interface-exclude eth1 + } + } + table 1000 + } +} + + +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@2:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@20:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:nat66@1:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@9:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@20:vrf@2:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" +// Release version: 1.4-rolling-202103130218 diff --git a/smoketest/scripts/cli/base_accel_ppp_test.py b/smoketest/scripts/cli/base_accel_ppp_test.py index 705c932b4..b2acb03cc 100644 --- a/smoketest/scripts/cli/base_accel_ppp_test.py +++ b/smoketest/scripts/cli/base_accel_ppp_test.py @@ -12,10 +12,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import re import unittest +from base_vyostest_shim import VyOSUnitTestSHIM from configparser import ConfigParser from vyos.configsession import ConfigSession @@ -26,26 +26,22 @@ from vyos.util import get_half_cpus from vyos.util import process_named_running class BasicAccelPPPTest: - class BaseTest(unittest.TestCase): - + class TestCase(VyOSUnitTestSHIM.TestCase): def setUp(self): - self.session = ConfigSession(os.getpid()) self._gateway = '192.0.2.1' - # ensure we can also run this test on a live system - so lets clean # out the current configuration :) - self.session.delete(self._base_path) + self.cli_delete(self._base_path) def tearDown(self): - self.session.delete(self._base_path) - self.session.commit() - del self.session + self.cli_delete(self._base_path) + self.cli_commit() def set(self, path): - self.session.set(self._base_path + path) + self.cli_set(self._base_path + path) def delete(self, path): - self.session.delete(self._base_path + path) + self.cli_delete(self._base_path + path) def basic_config(self): # PPPoE local auth mode requires local users to be configured! @@ -65,7 +61,7 @@ class BasicAccelPPPTest: self.set(['name-server', ns]) # commit changes - self.session.commit() + self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters='=') @@ -95,11 +91,11 @@ class BasicAccelPPPTest: # upload rate-limit requires also download rate-limit with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() self.set(['authentication', 'local-users', 'username', user, 'rate-limit', 'download', download]) # commit changes - self.session.commit() + self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters='=') @@ -123,7 +119,7 @@ class BasicAccelPPPTest: # Check local-users default value(s) self.delete(['authentication', 'local-users', 'username', user, 'static-ip']) # commit changes - self.session.commit() + self.cli_commit() # check local users tmp = cmd(f'sudo cat {self._chap_secrets}') @@ -162,7 +158,7 @@ class BasicAccelPPPTest: self.set(['authentication', 'radius', 'source-address', source_address]) # commit changes - self.session.commit() + self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters='=') @@ -200,7 +196,7 @@ class BasicAccelPPPTest: self.set(['authentication', 'radius', 'server', radius_server, 'disable-accounting']) # commit changes - self.session.commit() + self.cli_commit() conf.read(self._config_file) diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 8ee5395d0..f897088ef 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -1,4 +1,4 @@ -# Copyright (C) 2019-2020 VyOS maintainers and contributors +# Copyright (C) 2019-2021 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 @@ -12,16 +12,17 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import re import os import unittest -import json from binascii import hexlify -from netifaces import ifaddresses from netifaces import AF_INET from netifaces import AF_INET6 +from netifaces import ifaddresses +from netifaces import interfaces + +from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSession from vyos.ifconfig import Interface @@ -30,6 +31,7 @@ from vyos.util import read_file from vyos.util import cmd from vyos.util import dict_search from vyos.util import process_named_running +from vyos.util import get_interface_config from vyos.validate import is_intf_addr_assigned from vyos.validate import is_ipv6_link_local @@ -51,24 +53,15 @@ def is_mirrored_to(interface, mirror_if, qdisc): ret_val = True return ret_val - -dhcp6c_config_file = '/run/dhcp6c/dhcp6c.{}.conf' -def get_dhcp6c_config_value(interface, key): - tmp = read_file(dhcp6c_config_file.format(interface)) - tmp = re.findall(r'\n?{}\s+(.*)'.format(key), tmp) - - out = [] - for item in tmp: - out.append(item.replace(';','')) - return out - class BasicInterfaceTest: - class BaseTest(unittest.TestCase): + class TestCase(VyOSUnitTestSHIM.TestCase): _test_ip = False _test_mtu = False _test_vlan = False _test_qinq = False _test_ipv6 = False + _test_ipv6_pd = False + _test_ipv6_dhcpc6 = False _test_mirror = False _base_path = [] @@ -84,37 +77,35 @@ class BasicInterfaceTest: _mtu = '1280' def setUp(self): - self.session = ConfigSession(os.getpid()) - # Setup mirror interfaces for SPAN (Switch Port Analyzer) for span in self._mirror_interfaces: section = Section.section(span) - self.session.set(['interfaces', section, span]) + self.cli_set(['interfaces', section, span]) def tearDown(self): - # Ethernet is handled in its derived class - if 'ethernet' not in self._base_path: - self.session.delete(self._base_path) - # Tear down mirror interfaces for SPAN (Switch Port Analyzer) for span in self._mirror_interfaces: section = Section.section(span) - self.session.delete(['interfaces', section, span]) + self.cli_delete(['interfaces', section, span]) - self.session.commit() - del self.session + self.cli_delete(self._base_path) + self.cli_commit() + + # Verify that no previously interface remained on the system + for intf in self._interfaces: + self.assertNotIn(intf, interfaces()) def test_span_mirror(self): if not self._mirror_interfaces: - return None + self.skipTest('not supported') # Check the two-way mirror rules of ingress and egress for mirror in self._mirror_interfaces: for interface in self._interfaces: - self.session.set(self._base_path + [interface, 'mirror', 'ingress', mirror]) - self.session.set(self._base_path + [interface, 'mirror', 'egress', mirror]) + self.cli_set(self._base_path + [interface, 'mirror', 'ingress', mirror]) + self.cli_set(self._base_path + [interface, 'mirror', 'egress', mirror]) - self.session.commit() + self.cli_commit() # Verify config for mirror in self._mirror_interfaces: @@ -122,45 +113,69 @@ class BasicInterfaceTest: self.assertTrue(is_mirrored_to(interface, mirror, 'ffff')) self.assertTrue(is_mirrored_to(interface, mirror, '1')) + def test_interface_disable(self): + # Check if description can be added to interface and + # can be read back + for intf in self._interfaces: + self.cli_set(self._base_path + [intf, 'disable']) + for option in self._options.get(intf, []): + self.cli_set(self._base_path + [intf] + option.split()) + + self.cli_commit() + + # Validate interface description + for intf in self._interfaces: + self.assertEqual(Interface(intf).get_admin_state(), 'down') def test_interface_description(self): # Check if description can be added to interface and # can be read back for intf in self._interfaces: test_string=f'Description-Test-{intf}' - self.session.set(self._base_path + [intf, 'description', test_string]) + self.cli_set(self._base_path + [intf, 'description', test_string]) for option in self._options.get(intf, []): - self.session.set(self._base_path + [intf] + option.split()) + self.cli_set(self._base_path + [intf] + option.split()) - self.session.commit() + self.cli_commit() # Validate interface description for intf in self._interfaces: test_string=f'Description-Test-{intf}' tmp = read_file(f'/sys/class/net/{intf}/ifalias') - self.assertTrue(tmp, test_string) + self.assertEqual(tmp, test_string) + self.assertEqual(Interface(intf).get_alias(), test_string) + self.cli_delete(self._base_path + [intf, 'description']) + + self.cli_commit() + + # Validate remove interface description "empty" + for intf in self._interfaces: + tmp = read_file(f'/sys/class/net/{intf}/ifalias') + self.assertEqual(tmp, str()) + self.assertEqual(Interface(intf).get_alias(), str()) def test_add_single_ip_address(self): addr = '192.0.2.0/31' for intf in self._interfaces: - self.session.set(self._base_path + [intf, 'address', addr]) + self.cli_set(self._base_path + [intf, 'address', addr]) for option in self._options.get(intf, []): - self.session.set(self._base_path + [intf] + option.split()) + self.cli_set(self._base_path + [intf] + option.split()) - self.session.commit() + self.cli_commit() for intf in self._interfaces: self.assertTrue(is_intf_addr_assigned(intf, addr)) + self.assertEqual(Interface(intf).get_admin_state(), 'up') def test_add_multiple_ip_addresses(self): # Add address for intf in self._interfaces: for addr in self._test_addr: - self.session.set(self._base_path + [intf, 'address', addr]) + self.cli_set(self._base_path + [intf, 'address', addr]) for option in self._options.get(intf, []): - self.session.set(self._base_path + [intf] + option.split()) + self.cli_set(self._base_path + [intf] + option.split()) - self.session.commit() + self.cli_commit() # Validate address for intf in self._interfaces: @@ -175,15 +190,15 @@ class BasicInterfaceTest: def test_ipv6_link_local_address(self): # Common function for IPv6 link-local address assignemnts if not self._test_ipv6: - return None + self.skipTest('not supported') for interface in self._interfaces: base = self._base_path + [interface] for option in self._options.get(interface, []): - self.session.set(base + option.split()) + self.cli_set(base + option.split()) # after commit we must have an IPv6 link-local address - self.session.commit() + self.cli_commit() for interface in self._interfaces: for addr in ifaddresses(interface)[AF_INET6]: @@ -192,26 +207,26 @@ class BasicInterfaceTest: # disable IPv6 link-local address assignment for interface in self._interfaces: base = self._base_path + [interface] - self.session.set(base + ['ipv6', 'address', 'no-default-link-local']) + self.cli_set(base + ['ipv6', 'address', 'no-default-link-local']) # after commit we must have no IPv6 link-local address - self.session.commit() + self.cli_commit() for interface in self._interfaces: self.assertTrue(AF_INET6 not in ifaddresses(interface)) def test_interface_mtu(self): if not self._test_mtu: - return None + self.skipTest('not supported') for intf in self._interfaces: base = self._base_path + [intf] - self.session.set(base + ['mtu', self._mtu]) + self.cli_set(base + ['mtu', self._mtu]) for option in self._options.get(intf, []): - self.session.set(base + option.split()) + self.cli_set(base + option.split()) # commit interface changes - self.session.commit() + self.cli_commit() # verify changed MTU for intf in self._interfaces: @@ -222,21 +237,21 @@ class BasicInterfaceTest: # Testcase if MTU can be changed to 1200 on non IPv6 # enabled interfaces if not self._test_mtu: - return None + self.skipTest('not supported') old_mtu = self._mtu self._mtu = '1200' for intf in self._interfaces: base = self._base_path + [intf] - self.session.set(base + ['mtu', self._mtu]) - self.session.set(base + ['ipv6', 'address', 'no-default-link-local']) + self.cli_set(base + ['mtu', self._mtu]) + self.cli_set(base + ['ipv6', 'address', 'no-default-link-local']) for option in self._options.get(intf, []): - self.session.set(base + option.split()) + self.cli_set(base + option.split()) # commit interface changes - self.session.commit() + self.cli_commit() # verify changed MTU for intf in self._interfaces: @@ -245,22 +260,26 @@ class BasicInterfaceTest: self._mtu = old_mtu - def test_8021q_vlan_interfaces(self): + def test_vif_8021q_interfaces(self): + # XXX: This testcase is not allowed to run as first testcase, reason + # is the Wireless test will first load the wifi kernel hwsim module + # which creates a wlan0 and wlan1 interface which will fail the + # tearDown() test in the end that no interface is allowed to survive! if not self._test_vlan: - return None + self.skipTest('not supported') for interface in self._interfaces: base = self._base_path + [interface] for option in self._options.get(interface, []): - self.session.set(base + option.split()) + self.cli_set(base + option.split()) for vlan in self._vlan_range: base = self._base_path + [interface, 'vif', vlan] - self.session.set(base + ['mtu', self._mtu]) + self.cli_set(base + ['mtu', self._mtu]) for address in self._test_addr: - self.session.set(base + ['address', address]) + self.cli_set(base + ['address', address]) - self.session.commit() + self.cli_commit() for intf in self._interfaces: for vlan in self._vlan_range: @@ -270,29 +289,70 @@ class BasicInterfaceTest: tmp = read_file(f'/sys/class/net/{vif}/mtu') self.assertEqual(tmp, self._mtu) + self.assertEqual(Interface(vif).get_admin_state(), 'up') + + def test_vif_8021q_lower_up_down(self): + # Testcase for https://phabricator.vyos.net/T3349 + if not self._test_vlan: + self.skipTest('not supported') + + for interface in self._interfaces: + base = self._base_path + [interface] + for option in self._options.get(interface, []): + self.cli_set(base + option.split()) + + # disable the lower interface + self.cli_set(base + ['disable']) + + for vlan in self._vlan_range: + vlan_base = self._base_path + [interface, 'vif', vlan] + # disable the vlan interface + self.cli_set(vlan_base + ['disable']) + self.cli_commit() - def test_8021ad_qinq_vlan_interfaces(self): + # re-enable all lower interfaces + for interface in self._interfaces: + base = self._base_path + [interface] + self.cli_delete(base + ['disable']) + + self.cli_commit() + + # verify that the lower interfaces are admin up and the vlan + # interfaces are all admin down + for interface in self._interfaces: + self.assertEqual(Interface(interface).get_admin_state(), 'up') + + for vlan in self._vlan_range: + ifname = f'{interface}.{vlan}' + self.assertEqual(Interface(ifname).get_admin_state(), 'down') + + + def test_vif_s_8021ad_vlan_interfaces(self): + # XXX: This testcase is not allowed to run as first testcase, reason + # is the Wireless test will first load the wifi kernel hwsim module + # which creates a wlan0 and wlan1 interface which will fail the + # tearDown() test in the end that no interface is allowed to survive! if not self._test_qinq: - return None + self.skipTest('not supported') for interface in self._interfaces: base = self._base_path + [interface] for option in self._options.get(interface, []): - self.session.set(base + option.split()) + self.cli_set(base + option.split()) for vif_s in self._qinq_range: for vif_c in self._vlan_range: base = self._base_path + [interface, 'vif-s', vif_s, 'vif-c', vif_c] - self.session.set(base + ['mtu', self._mtu]) + self.cli_set(base + ['mtu', self._mtu]) for address in self._test_addr: - self.session.set(base + ['address', address]) + self.cli_set(base + ['address', address]) - self.session.commit() + self.cli_commit() for interface in self._interfaces: for vif_s in self._qinq_range: - tmp = json.loads(cmd(f'ip -d -j link show dev {interface}.{vif_s}'))[0] + tmp = get_interface_config(f'{interface}.{vif_s}') self.assertEqual(dict_search('linkinfo.info_data.protocol', tmp), '802.1ad') for vif_c in self._vlan_range: @@ -305,26 +365,26 @@ class BasicInterfaceTest: def test_interface_ip_options(self): if not self._test_ip: - return None + self.skipTest('not supported') for interface in self._interfaces: arp_tmo = '300' path = self._base_path + [interface] for option in self._options.get(interface, []): - self.session.set(path + option.split()) + self.cli_set(path + option.split()) # Options - self.session.set(path + ['ip', 'arp-cache-timeout', arp_tmo]) - self.session.set(path + ['ip', 'disable-arp-filter']) - self.session.set(path + ['ip', 'disable-forwarding']) - self.session.set(path + ['ip', 'enable-arp-accept']) - self.session.set(path + ['ip', 'enable-arp-announce']) - self.session.set(path + ['ip', 'enable-arp-ignore']) - self.session.set(path + ['ip', 'enable-proxy-arp']) - self.session.set(path + ['ip', 'proxy-arp-pvlan']) - self.session.set(path + ['ip', 'source-validation', 'loose']) - - self.session.commit() + self.cli_set(path + ['ip', 'arp-cache-timeout', arp_tmo]) + self.cli_set(path + ['ip', 'disable-arp-filter']) + self.cli_set(path + ['ip', 'disable-forwarding']) + self.cli_set(path + ['ip', 'enable-arp-accept']) + self.cli_set(path + ['ip', 'enable-arp-announce']) + self.cli_set(path + ['ip', 'enable-arp-ignore']) + self.cli_set(path + ['ip', 'enable-proxy-arp']) + self.cli_set(path + ['ip', 'proxy-arp-pvlan']) + self.cli_set(path + ['ip', 'source-validation', 'loose']) + + self.cli_commit() for interface in self._interfaces: tmp = read_file(f'/proc/sys/net/ipv4/neigh/{interface}/base_reachable_time_ms') @@ -356,19 +416,19 @@ class BasicInterfaceTest: def test_interface_ipv6_options(self): if not self._test_ipv6: - return None + self.skipTest('not supported') for interface in self._interfaces: dad_transmits = '10' path = self._base_path + [interface] for option in self._options.get(interface, []): - self.session.set(path + option.split()) + self.cli_set(path + option.split()) # Options - self.session.set(path + ['ipv6', 'disable-forwarding']) - self.session.set(path + ['ipv6', 'dup-addr-detect-transmits', dad_transmits]) + self.cli_set(path + ['ipv6', 'disable-forwarding']) + self.cli_set(path + ['ipv6', 'dup-addr-detect-transmits', dad_transmits]) - self.session.commit() + self.cli_commit() for interface in self._interfaces: tmp = read_file(f'/proc/sys/net/ipv6/conf/{interface}/forwarding') @@ -377,40 +437,156 @@ class BasicInterfaceTest: tmp = read_file(f'/proc/sys/net/ipv6/conf/{interface}/dad_transmits') self.assertEqual(dad_transmits, tmp) + def test_dhcpv6_clinet_options(self): + if not self._test_ipv6_dhcpc6: + self.skipTest('not supported') - def test_ipv6_dhcpv6_prefix_delegation(self): - if not self._test_ipv6: - return None + duid_base = 10 + for interface in self._interfaces: + duid = '00:01:00:01:27:71:db:f0:00:50:00:00:00:{}'.format(duid_base) + path = self._base_path + [interface] + for option in self._options.get(interface, []): + self.cli_set(path + option.split()) + + # Enable DHCPv6 client + self.cli_set(path + ['address', 'dhcpv6']) + self.cli_set(path + ['dhcpv6-options', 'rapid-commit']) + self.cli_set(path + ['dhcpv6-options', 'parameters-only']) + self.cli_set(path + ['dhcpv6-options', 'duid', duid]) + duid_base += 1 + + self.cli_commit() + + duid_base = 10 + for interface in self._interfaces: + duid = '00:01:00:01:27:71:db:f0:00:50:00:00:00:{}'.format(duid_base) + dhcpc6_config = read_file(f'/run/dhcp6c/dhcp6c.{interface}.conf') + self.assertIn(f'interface {interface} ' + '{', dhcpc6_config) + self.assertIn(f' request domain-name-servers;', dhcpc6_config) + self.assertIn(f' request domain-name;', dhcpc6_config) + self.assertIn(f' information-only;', dhcpc6_config) + self.assertIn(f' send ia-na 0;', dhcpc6_config) + self.assertIn(f' send rapid-commit;', dhcpc6_config) + self.assertIn(f' send client-id {duid};', dhcpc6_config) + self.assertIn('};', dhcpc6_config) + duid_base += 1 + + # Check for running process + self.assertTrue(process_named_running('dhcp6c')) + + def test_dhcpv6pd_auto_sla_id(self): + if not self._test_ipv6_pd: + self.skipTest('not supported') + + prefix_len = '56' + sla_len = str(64 - int(prefix_len)) + + delegatees = ['dum2340', 'dum2341', 'dum2342', 'dum2343', 'dum2344'] - address = '1' - sla_id = '0' - sla_len = '8' for interface in self._interfaces: path = self._base_path + [interface] for option in self._options.get(interface, []): - self.session.set(path + option.split()) + self.cli_set(path + option.split()) + address = '1' # prefix delegation stuff pd_base = path + ['dhcpv6-options', 'pd', '0'] - self.session.set(pd_base + ['length', '56']) - self.session.set(pd_base + ['interface', interface, 'address', address]) - self.session.set(pd_base + ['interface', interface, 'sla-id', sla_id]) + self.cli_set(pd_base + ['length', prefix_len]) + + for delegatee in delegatees: + section = Section.section(delegatee) + self.cli_set(['interfaces', section, delegatee]) + self.cli_set(pd_base + ['interface', delegatee, 'address', address]) + # increment interface address + address = str(int(address) + 1) - self.session.commit() + self.cli_commit() for interface in self._interfaces: + dhcpc6_config = read_file(f'/run/dhcp6c/dhcp6c.{interface}.conf') + # verify DHCPv6 prefix delegation - # will return: ['delegation', '::/56 infinity;'] - tmp = get_dhcp6c_config_value(interface, 'prefix')[1].split()[0] # mind the whitespace - self.assertEqual(tmp, '::/56') - tmp = get_dhcp6c_config_value(interface, 'prefix-interface')[0].split()[0] - self.assertEqual(tmp, interface) - tmp = get_dhcp6c_config_value(interface, 'ifid')[0] - self.assertEqual(tmp, address) - tmp = get_dhcp6c_config_value(interface, 'sla-id')[0] - self.assertEqual(tmp, sla_id) - tmp = get_dhcp6c_config_value(interface, 'sla-len')[0] - self.assertEqual(tmp, sla_len) + self.assertIn(f'prefix ::/{prefix_len} infinity;', dhcpc6_config) + + address = '1' + sla_id = '0' + for delegatee in delegatees: + self.assertIn(f'prefix-interface {delegatee}' + r' {', dhcpc6_config) + self.assertIn(f'ifid {address};', dhcpc6_config) + self.assertIn(f'sla-id {sla_id};', dhcpc6_config) + self.assertIn(f'sla-len {sla_len};', dhcpc6_config) + + # increment sla-id + sla_id = str(int(sla_id) + 1) + # increment interface address + address = str(int(address) + 1) # Check for running process self.assertTrue(process_named_running('dhcp6c')) + + for delegatee in delegatees: + # we can already cleanup the test delegatee interface here + # as until commit() is called, nothing happens + section = Section.section(delegatee) + self.cli_delete(['interfaces', section, delegatee]) + + def test_dhcpv6pd_manual_sla_id(self): + if not self._test_ipv6_pd: + self.skipTest('not supported') + + prefix_len = '56' + sla_len = str(64 - int(prefix_len)) + + delegatees = ['dum3340', 'dum3341', 'dum3342', 'dum3343', 'dum3344'] + + for interface in self._interfaces: + path = self._base_path + [interface] + for option in self._options.get(interface, []): + self.cli_set(path + option.split()) + + # prefix delegation stuff + address = '1' + sla_id = '1' + pd_base = path + ['dhcpv6-options', 'pd', '0'] + self.cli_set(pd_base + ['length', prefix_len]) + + for delegatee in delegatees: + section = Section.section(delegatee) + self.cli_set(['interfaces', section, delegatee]) + self.cli_set(pd_base + ['interface', delegatee, 'address', address]) + self.cli_set(pd_base + ['interface', delegatee, 'sla-id', sla_id]) + + # increment interface address + address = str(int(address) + 1) + sla_id = str(int(sla_id) + 1) + + self.cli_commit() + + # Verify dhcpc6 client configuration + for interface in self._interfaces: + address = '1' + sla_id = '1' + dhcpc6_config = read_file(f'/run/dhcp6c/dhcp6c.{interface}.conf') + + # verify DHCPv6 prefix delegation + self.assertIn(f'prefix ::/{prefix_len} infinity;', dhcpc6_config) + + for delegatee in delegatees: + self.assertIn(f'prefix-interface {delegatee}' + r' {', dhcpc6_config) + self.assertIn(f'ifid {address};', dhcpc6_config) + self.assertIn(f'sla-id {sla_id};', dhcpc6_config) + self.assertIn(f'sla-len {sla_len};', dhcpc6_config) + + # increment sla-id + sla_id = str(int(sla_id) + 1) + # increment interface address + address = str(int(address) + 1) + + # Check for running process + self.assertTrue(process_named_running('dhcp6c')) + + for delegatee in delegatees: + # we can already cleanup the test delegatee interface here + # as until commit() is called, nothing happens + section = Section.section(delegatee) + self.cli_delete(['interfaces', section, delegatee]) diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py new file mode 100644 index 000000000..18e49f47f --- /dev/null +++ b/smoketest/scripts/cli/base_vyostest_shim.py @@ -0,0 +1,90 @@ +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import unittest + +from time import sleep + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos import ConfigError +from vyos.util import cmd + +save_config = '/tmp/vyos-smoketest-save' + +# This class acts as shim between individual Smoketests developed for VyOS and +# the Python UnitTest framework. Before every test is loaded, we dump the current +# system configuration and reload it after the test - despite the test results. +# +# Using this approach we can not render a live system useless while running any +# kind of smoketest. In addition it adds debug capabilities like printing the +# command used to execute the test. +class VyOSUnitTestSHIM: + class TestCase(unittest.TestCase): + # if enabled in derived class, print out each and every set/del command + # on the CLI. This is usefull to grap all the commands required to + # trigger the certain failure condition. + # Use "self.debug = True" in derived classes setUp() method + debug = False + + @classmethod + def setUpClass(cls): + cls._session = ConfigSession(os.getpid()) + cls._session.save_config(save_config) + pass + + @classmethod + def tearDownClass(cls): + # discard any pending changes which might caused a messed up config + cls._session.discard() + # ... and restore the initial state + cls._session.migrate_and_load_config(save_config) + + try: + cls._session.commit() + except (ConfigError, ConfigSessionError): + cls._session.discard() + cls.fail(cls) + + def cli_set(self, config): + if self.debug: + print('set ' + ' '.join(config)) + self._session.set(config) + + def cli_delete(self, config): + if self.debug: + print('del ' + ' '.join(config)) + self._session.delete(config) + + def cli_commit(self): + self._session.commit() + + def getFRRconfig(self, string, end='$', endsection='^!'): + """ Retrieve current "running configuration" from FRR """ + command = f'vtysh -c "show run" | sed -n "/^{string}{end}/,/{endsection}/p"' + + count = 0 + tmp = '' + while count < 10 and tmp == '': + # Let FRR settle after a config change first before harassing it again + sleep(1) + tmp = cmd(command) + count += 1 + + if self.debug or tmp == '': + import pprint + print(f'\n\ncommand "{command}" returned:\n') + pprint.pprint(tmp) + return tmp diff --git a/smoketest/scripts/cli/test_interfaces_bonding.py b/smoketest/scripts/cli/test_interfaces_bonding.py index a35682b7c..03cdafb8d 100755 --- a/smoketest/scripts/cli/test_interfaces_bonding.py +++ b/smoketest/scripts/cli/test_interfaces_bonding.py @@ -24,32 +24,36 @@ from vyos.ifconfig.interface import Interface from vyos.configsession import ConfigSessionError from vyos.util import read_file -class BondingInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._test_mtu = True - self._test_vlan = True - self._test_qinq = True - self._test_ipv6 = True - self._base_path = ['interfaces', 'bonding'] - self._interfaces = ['bond0'] - self._mirror_interfaces = ['dum21354'] - self._members = [] +class BondingInterfaceTest(BasicInterfaceTest.TestCase): + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._test_ipv6_pd = True + cls._test_ipv6_dhcpc6 = True + cls._test_mtu = True + cls._test_vlan = True + cls._test_qinq = True + cls._base_path = ['interfaces', 'bonding'] + cls._interfaces = ['bond0'] + cls._mirror_interfaces = ['dum21354'] + cls._members = [] # we need to filter out VLAN interfaces identified by a dot (.) # in their name - just in case! if 'TEST_ETH' in os.environ: - self._members = os.environ['TEST_ETH'].split() + cls._members = os.environ['TEST_ETH'].split() else: - for tmp in Section.interfaces("ethernet"): + for tmp in Section.interfaces('ethernet'): if not '.' in tmp: - self._members.append(tmp) + cls._members.append(tmp) - self._options['bond0'] = [] - for member in self._members: - self._options['bond0'].append(f'member interface {member}') - - super().setUp() + cls._options['bond0'] = [] + for member in cls._members: + cls._options['bond0'].append(f'member interface {member}') + # call base-classes classmethod + super(cls, cls).setUpClass() def test_add_single_ip_address(self): super().test_add_single_ip_address() @@ -58,8 +62,8 @@ class BondingInterfaceTest(BasicInterfaceTest.BaseTest): slaves = read_file(f'/sys/class/net/{interface}/bonding/slaves').split() self.assertListEqual(slaves, self._members) - def test_8021q_vlan_interfaces(self): - super().test_8021q_vlan_interfaces() + def test_vif_8021q_interfaces(self): + super().test_vif_8021q_interfaces() for interface in self._interfaces: slaves = read_file(f'/sys/class/net/{interface}/bonding/slaves').split() @@ -73,16 +77,16 @@ class BondingInterfaceTest(BasicInterfaceTest.BaseTest): # configure member interfaces for interface in self._interfaces: for option in self._options.get(interface, []): - self.session.set(self._base_path + [interface] + option.split()) + self.cli_set(self._base_path + [interface] + option.split()) - self.session.commit() + self.cli_commit() # remove single bond member port for interface in self._interfaces: remove_member = self._members[0] - self.session.delete(self._base_path + [interface, 'member', 'interface', remove_member]) + self.cli_delete(self._base_path + [interface, 'member', 'interface', remove_member]) - self.session.commit() + self.cli_commit() # removed member port must be admin-up for interface in self._interfaces: diff --git a/smoketest/scripts/cli/test_interfaces_bridge.py b/smoketest/scripts/cli/test_interfaces_bridge.py index 7444701c1..21f20c781 100755 --- a/smoketest/scripts/cli/test_interfaces_bridge.py +++ b/smoketest/scripts/cli/test_interfaces_bridge.py @@ -25,72 +25,126 @@ from netifaces import interfaces from vyos.ifconfig import Section from vyos.util import cmd from vyos.util import read_file - -class BridgeInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._test_ipv6 = True - self._test_vlan = True - self._test_qinq = True - self._base_path = ['interfaces', 'bridge'] - self._mirror_interfaces = ['dum21354'] - self._members = [] +from vyos.util import get_interface_config +from vyos.validate import is_intf_addr_assigned + +class BridgeInterfaceTest(BasicInterfaceTest.TestCase): + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._test_ipv6_pd = True + cls._test_ipv6_dhcpc6 = True + cls._test_vlan = True + cls._base_path = ['interfaces', 'bridge'] + cls._mirror_interfaces = ['dum21354'] + cls._members = [] # we need to filter out VLAN interfaces identified by a dot (.) # in their name - just in case! if 'TEST_ETH' in os.environ: - self._members = os.environ['TEST_ETH'].split() + cls._members = os.environ['TEST_ETH'].split() else: - for tmp in Section.interfaces("ethernet"): + for tmp in Section.interfaces('ethernet'): if not '.' in tmp: - self._members.append(tmp) + cls._members.append(tmp) + + cls._options['br0'] = [] + for member in cls._members: + cls._options['br0'].append(f'member interface {member}') + cls._interfaces = list(cls._options) + + # call base-classes classmethod + super(cls, cls).setUpClass() + + def tearDown(self): + for intf in self._interfaces: + self.cli_delete(self._base_path + [intf]) + + super().tearDown() + + def test_isolated_interfaces(self): + # Add member interfaces to bridge and set STP cost/priority + for interface in self._interfaces: + base = self._base_path + [interface] + self.cli_set(base + ['stp']) - self._options['br0'] = [] - for member in self._members: - self._options['br0'].append(f'member interface {member}') - self._interfaces = list(self._options) + # assign members to bridge interface + for member in self._members: + base_member = base + ['member', 'interface', member] + self.cli_set(base_member + ['isolated']) + + # commit config + self.cli_commit() + + for interface in self._interfaces: + tmp = get_interface_config(interface) + # STP must be enabled as configured above + self.assertEqual(1, tmp['linkinfo']['info_data']['stp_state']) + + # validate member interface configuration + for member in self._members: + tmp = get_interface_config(member) + # Isolated must be enabled as configured above + self.assertTrue(tmp['linkinfo']['info_slave_data']['isolated']) - super().setUp() def test_add_remove_bridge_member(self): # Add member interfaces to bridge and set STP cost/priority for interface in self._interfaces: base = self._base_path + [interface] - self.session.set(base + ['stp']) - self.session.set(base + ['address', '192.0.2.1/24']) + self.cli_set(base + ['stp']) + self.cli_set(base + ['address', '192.0.2.1/24']) cost = 1000 priority = 10 # assign members to bridge interface for member in self._members: base_member = base + ['member', 'interface', member] - self.session.set(base_member + ['cost', str(cost)]) - self.session.set(base_member + ['priority', str(priority)]) + self.cli_set(base_member + ['cost', str(cost)]) + self.cli_set(base_member + ['priority', str(priority)]) cost += 1 priority += 1 # commit config - self.session.commit() + self.cli_commit() - # check member interfaces are added on the bridge - bridge_members = [] - for tmp in glob(f'/sys/class/net/{interface}/lower_*'): - bridge_members.append(os.path.basename(tmp).replace('lower_', '')) + # Add member interfaces to bridge and set STP cost/priority + for interface in self._interfaces: + cost = 1000 + priority = 10 + for member in self._members: + tmp = get_interface_config(member) + self.assertEqual(interface, tmp['master']) + self.assertFalse( tmp['linkinfo']['info_slave_data']['isolated']) + self.assertEqual(cost, tmp['linkinfo']['info_slave_data']['cost']) + self.assertEqual(priority, tmp['linkinfo']['info_slave_data']['priority']) - for member in self._members: - self.assertIn(member, bridge_members) + cost += 1 + priority += 1 - # delete all members + + def test_vif_8021q_interfaces(self): for interface in self._interfaces: - self.session.delete(self._base_path + [interface, 'member']) + base = self._base_path + [interface] + self.cli_set(base + ['enable-vlan']) + super().test_vif_8021q_interfaces() - self.session.commit() + def test_vif_8021q_lower_up_down(self): + for interface in self._interfaces: + base = self._base_path + [interface] + self.cli_set(base + ['enable-vlan']) + super().test_vif_8021q_interfaces() def test_bridge_vlan_filter(self): + vif_vlan = 2 # Add member interface to bridge and set VLAN filter for interface in self._interfaces: base = self._base_path + [interface] - self.session.set(base + ['vif', '1', 'address', '192.0.2.1/24']) - self.session.set(base + ['vif', '2', 'address', '192.0.3.1/24']) + self.cli_set(base + ['enable-vlan']) + self.cli_set(base + ['address', '192.0.2.1/24']) + self.cli_set(base + ['vif', str(vif_vlan), 'address', '192.0.3.1/24']) + self.cli_set(base + ['vif', str(vif_vlan), 'mtu', self._mtu]) vlan_id = 101 allowed_vlan = 2 @@ -98,13 +152,13 @@ class BridgeInterfaceTest(BasicInterfaceTest.BaseTest): # assign members to bridge interface for member in self._members: base_member = base + ['member', 'interface', member] - self.session.set(base_member + ['allowed-vlan', str(allowed_vlan)]) - self.session.set(base_member + ['allowed-vlan', allowed_vlan_range]) - self.session.set(base_member + ['native-vlan', str(vlan_id)]) + self.cli_set(base_member + ['allowed-vlan', str(allowed_vlan)]) + self.cli_set(base_member + ['allowed-vlan', allowed_vlan_range]) + self.cli_set(base_member + ['native-vlan', str(vlan_id)]) vlan_id += 1 # commit config - self.session.commit() + self.cli_commit() # Detect the vlan filter function for interface in self._interfaces: @@ -160,7 +214,7 @@ class BridgeInterfaceTest(BasicInterfaceTest.BaseTest): # delete all members for interface in self._interfaces: - self.session.delete(self._base_path + [interface, 'member']) + self.cli_delete(self._base_path + [interface, 'member']) def test_bridge_vlan_members(self): @@ -169,10 +223,10 @@ class BridgeInterfaceTest(BasicInterfaceTest.BaseTest): for interface in self._interfaces: for member in self._members: for vif in vifs: - self.session.set(['interfaces', 'ethernet', member, 'vif', vif]) - self.session.set(['interfaces', 'bridge', interface, 'member', 'interface', f'{member}.{vif}']) + self.cli_set(['interfaces', 'ethernet', member, 'vif', vif]) + self.cli_set(['interfaces', 'bridge', interface, 'member', 'interface', f'{member}.{vif}']) - self.session.commit() + self.cli_commit() # Verify config for interface in self._interfaces: @@ -181,10 +235,12 @@ class BridgeInterfaceTest(BasicInterfaceTest.BaseTest): # member interface must be assigned to the bridge self.assertTrue(os.path.exists(f'/sys/class/net/{interface}/lower_{member}.{vif}')) - # remove VLAN interfaces - for vif in vifs: - self.session.delete(['interfaces', 'ethernet', member, 'vif', vif]) + # delete all members + for interface in self._interfaces: + for member in self._members: + for vif in vifs: + self.cli_delete(['interfaces', 'ethernet', member, 'vif', vif]) + self.cli_delete(['interfaces', 'bridge', interface, 'member', 'interface', f'{member}.{vif}']) if __name__ == '__main__': - unittest.main(verbosity=2, failfast=True) - + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_interfaces_dummy.py b/smoketest/scripts/cli/test_interfaces_dummy.py index c482a6f0b..dedc6fe05 100755 --- a/smoketest/scripts/cli/test_interfaces_dummy.py +++ b/smoketest/scripts/cli/test_interfaces_dummy.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 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 @@ -18,11 +18,13 @@ import unittest from base_interfaces_test import BasicInterfaceTest -class DummyInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._base_path = ['interfaces', 'dummy'] - self._interfaces = ['dum0', 'dum1', 'dum2'] - super().setUp() +class DummyInterfaceTest(BasicInterfaceTest.TestCase): + @classmethod + def setUpClass(cls): + cls._base_path = ['interfaces', 'dummy'] + cls._interfaces = ['dum435', 'dum8677', 'dum0931', 'dum089'] + # call base-classes classmethod + super(cls, cls).setUpClass() if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_interfaces_ethernet.py b/smoketest/scripts/cli/test_interfaces_ethernet.py index 3c4796283..cb0c8a426 100755 --- a/smoketest/scripts/cli/test_interfaces_ethernet.py +++ b/smoketest/scripts/cli/test_interfaces_ethernet.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 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 @@ -19,6 +19,7 @@ import re import unittest from base_interfaces_test import BasicInterfaceTest +from vyos.configsession import ConfigSessionError from vyos.ifconfig import Section from vyos.util import cmd from vyos.util import process_named_running @@ -33,37 +34,35 @@ def get_wpa_supplicant_value(interface, key): tmp = re.findall(r'\n?{}=(.*)'.format(key), tmp) return tmp[0] -class EthernetInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._test_ip = True - self._test_mtu = True - self._test_vlan = True - self._test_qinq = True - self._test_ipv6 = True - self._base_path = ['interfaces', 'ethernet'] - self._mirror_interfaces = ['dum21354'] +class EthernetInterfaceTest(BasicInterfaceTest.TestCase): + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._test_ipv6_pd = True + cls._test_ipv6_dhcpc6 = True + cls._test_mtu = True + cls._test_vlan = True + cls._test_qinq = True + cls._base_path = ['interfaces', 'ethernet'] + cls._mirror_interfaces = ['dum21354'] # we need to filter out VLAN interfaces identified by a dot (.) # in their name - just in case! if 'TEST_ETH' in os.environ: tmp = os.environ['TEST_ETH'].split() - self._interfaces = tmp + cls._interfaces = tmp else: - for tmp in Section.interfaces("ethernet"): + for tmp in Section.interfaces('ethernet'): if not '.' in tmp: - self._interfaces.append(tmp) + cls._interfaces.append(tmp) - self._macs = {} - for interface in self._interfaces: - try: - mac = self.session.show_config(self._base_path + - [interface, 'hw-id']).split()[1] - except: - # during initial system startup there is no hw-id node - mac = read_file(f'/sys/class/net/{interface}/address') - self._macs[interface] = mac + cls._macs = {} + for interface in cls._interfaces: + cls._macs[interface] = read_file(f'/sys/class/net/{interface}/address') - super().setUp() + # call base-classes classmethod + super(cls, cls).setUpClass() def tearDown(self): @@ -71,31 +70,34 @@ class EthernetInterfaceTest(BasicInterfaceTest.BaseTest): # when using a dedicated interface to test via TEST_ETH environment # variable only this one will be cleared in the end - usable to test # ethernet interfaces via SSH - self.session.delete(self._base_path + [interface]) - self.session.set(self._base_path + [interface, 'duplex', 'auto']) - self.session.set(self._base_path + [interface, 'speed', 'auto']) - self.session.set(self._base_path + [interface, 'hw-id', self._macs[interface]]) + self.cli_delete(self._base_path + [interface]) + self.cli_set(self._base_path + [interface, 'duplex', 'auto']) + self.cli_set(self._base_path + [interface, 'speed', 'auto']) + self.cli_set(self._base_path + [interface, 'hw-id', self._macs[interface]]) - super().tearDown() + # Tear down mirror interfaces for SPAN (Switch Port Analyzer) + for span in self._mirror_interfaces: + section = Section.section(span) + self.cli_delete(['interfaces', section, span]) + self.cli_commit() def test_dhcp_disable_interface(self): # When interface is configured as admin down, it must be admin down # even when dhcpc starts on the given interface for interface in self._interfaces: - self.session.set(self._base_path + [interface, 'disable']) + self.cli_set(self._base_path + [interface, 'disable']) # Also enable DHCP (ISC DHCP always places interface in admin up # state so we check that we do not start DHCP client. # https://phabricator.vyos.net/T2767 - self.session.set(self._base_path + [interface, 'address', 'dhcp']) + self.cli_set(self._base_path + [interface, 'address', 'dhcp']) - self.session.commit() + self.cli_commit() # Validate interface state for interface in self._interfaces: - with open(f'/sys/class/net/{interface}/flags', 'r') as f: - flags = f.read() + flags = read_file(f'/sys/class/net/{interface}/flags') self.assertEqual(int(flags, 16) & 1, 0) def test_offloading_rps(self): @@ -111,9 +113,9 @@ class EthernetInterfaceTest(BasicInterfaceTest.BaseTest): rps_cpus &= ~1 for interface in self._interfaces: - self.session.set(self._base_path + [interface, 'offload', 'rps']) + self.cli_set(self._base_path + [interface, 'offload', 'rps']) - self.session.commit() + self.cli_commit() for interface in self._interfaces: cpus = read_file('/sys/class/net/eth1/queues/rx-0/rps_cpus') @@ -123,15 +125,37 @@ class EthernetInterfaceTest(BasicInterfaceTest.BaseTest): self.assertEqual(f'{cpus:x}', f'{rps_cpus:x}') + def test_non_existing_interface(self): + unknonw_interface = self._base_path + ['eth667'] + self.cli_set(unknonw_interface) + + # check validate() - interface does not exist + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + # we need to remove this wrong interface from the configuration + # manually, else tearDown() will have problem in commit() + self.cli_delete(unknonw_interface) + + def test_speed_duplex_verify(self): + for interface in self._interfaces: + self.cli_set(self._base_path + [interface, 'speed', '1000']) + + # check validate() - if either speed or duplex is not auto, the + # other one must be manually configured, too + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(self._base_path + [interface, 'speed', 'auto']) + self.cli_commit() def test_eapol_support(self): for interface in self._interfaces: # Enable EAPoL - self.session.set(self._base_path + [interface, 'eapol', 'ca-cert-file', ca_cert]) - self.session.set(self._base_path + [interface, 'eapol', 'cert-file', ssl_cert]) - self.session.set(self._base_path + [interface, 'eapol', 'key-file', ssl_key]) + self.cli_set(self._base_path + [interface, 'eapol', 'ca-cert-file', ca_cert]) + self.cli_set(self._base_path + [interface, 'eapol', 'cert-file', ssl_cert]) + self.cli_set(self._base_path + [interface, 'eapol', 'key-file', ssl_key]) - self.session.commit() + self.cli_commit() # Check for running process self.assertTrue(process_named_running('wpa_supplicant')) @@ -169,12 +193,12 @@ if __name__ == '__main__': # Generate mandatory SSL certificate tmp = f'openssl req -newkey rsa:4096 -new -nodes -x509 -days 3650 '\ f'-keyout {ssl_key} -out {ssl_cert} -subj {subject}' - print(cmd(tmp)) + cmd(tmp) if not os.path.isfile(ca_cert): # Generate "CA" tmp = f'openssl req -new -x509 -key {ssl_key} -out {ca_cert} -subj {subject}' - print(cmd(tmp)) + cmd(tmp) for file in [ca_cert, ssl_cert, ssl_key]: cmd(f'sudo chown radius_priv_user:vyattacfg {file}') diff --git a/smoketest/scripts/cli/test_interfaces_geneve.py b/smoketest/scripts/cli/test_interfaces_geneve.py index 98f55210f..129ee71e5 100755 --- a/smoketest/scripts/cli/test_interfaces_geneve.py +++ b/smoketest/scripts/cli/test_interfaces_geneve.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -17,17 +17,65 @@ import unittest from vyos.configsession import ConfigSession +from vyos.ifconfig import Interface +from vyos.util import get_interface_config + from base_interfaces_test import BasicInterfaceTest -class GeneveInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._base_path = ['interfaces', 'geneve'] - self._options = { +class GeneveInterfaceTest(BasicInterfaceTest.TestCase): + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._base_path = ['interfaces', 'geneve'] + cls._options = { 'gnv0': ['vni 10', 'remote 127.0.1.1'], 'gnv1': ['vni 20', 'remote 127.0.1.2'], + 'gnv1': ['vni 30', 'remote 2001:db8::1', 'parameters ipv6 flowlabel 0x1000'], } - self._interfaces = list(self._options) - super().setUp() + cls._interfaces = list(cls._options) + # call base-classes classmethod + super(cls, cls).setUpClass() + + def test_geneve_parameters(self): + tos = '40' + ttl = 20 + for intf in self._interfaces: + for option in self._options.get(intf, []): + self.cli_set(self._base_path + [intf] + option.split()) + + self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'dont-fragment']) + self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'tos', tos]) + self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'ttl', str(ttl)]) + ttl += 10 + + self.cli_commit() + + ttl = 20 + for interface in self._interfaces: + options = get_interface_config(interface) + + vni = options['linkinfo']['info_data']['id'] + self.assertIn(f'vni {vni}', self._options[interface]) + + if any('remote' in s for s in self._options[interface]): + key = 'remote' + if 'remote6' in options['linkinfo']['info_data']: + key = 'remote6' + + remote = options['linkinfo']['info_data'][key] + self.assertIn(f'remote {remote}', self._options[interface]) + + if any('flowlabel' in s for s in self._options[interface]): + label = options['linkinfo']['info_data']['label'] + self.assertIn(f'parameters ipv6 flowlabel {label}', self._options[interface]) + + self.assertEqual('geneve', options['linkinfo']['info_kind']) + self.assertEqual('set', options['linkinfo']['info_data']['df']) + self.assertEqual(f'0x{tos}', options['linkinfo']['info_data']['tos']) + self.assertEqual(ttl, options['linkinfo']['info_data']['ttl']) + self.assertEqual(Interface(interface).get_admin_state(), 'up') + ttl += 10 if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_interfaces_l2tpv3.py b/smoketest/scripts/cli/test_interfaces_l2tpv3.py index c756bfdd5..24cb9464e 100755 --- a/smoketest/scripts/cli/test_interfaces_l2tpv3.py +++ b/smoketest/scripts/cli/test_interfaces_l2tpv3.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 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 @@ -20,21 +20,25 @@ import unittest from base_interfaces_test import BasicInterfaceTest from vyos.util import cmd -class GeneveInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._base_path = ['interfaces', 'l2tpv3'] - self._options = { - 'l2tpeth10': ['local-ip 127.0.0.1', 'remote-ip 127.10.10.10', +class GeneveInterfaceTest(BasicInterfaceTest.TestCase): + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._base_path = ['interfaces', 'l2tpv3'] + cls._options = { + 'l2tpeth10': ['source-address 127.0.0.1', 'remote 127.10.10.10', 'tunnel-id 100', 'peer-tunnel-id 10', 'session-id 100', 'peer-session-id 10', 'source-port 1010', 'destination-port 10101'], - 'l2tpeth20': ['local-ip 127.0.0.1', 'peer-session-id 20', - 'peer-tunnel-id 200', 'remote-ip 127.20.20.20', + 'l2tpeth20': ['source-address 127.0.0.1', 'peer-session-id 20', + 'peer-tunnel-id 200', 'remote 127.20.20.20', 'session-id 20', 'tunnel-id 200', 'source-port 2020', 'destination-port 20202'], } - self._interfaces = list(self._options) - super().setUp() + cls._interfaces = list(cls._options) + # call base-classes classmethod + super(cls, cls).setUpClass() def test_add_single_ip_address(self): super().test_add_single_ip_address() diff --git a/smoketest/scripts/cli/test_interfaces_loopback.py b/smoketest/scripts/cli/test_interfaces_loopback.py index 79225a1bd..85b5ca6d6 100755 --- a/smoketest/scripts/cli/test_interfaces_loopback.py +++ b/smoketest/scripts/cli/test_interfaces_loopback.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -17,25 +17,40 @@ import unittest from base_interfaces_test import BasicInterfaceTest +from netifaces import interfaces + from vyos.validate import is_intf_addr_assigned -class LoopbackInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - super().setUp() - # these addresses are never allowed to be removed from the system - self._loopback_addresses = ['127.0.0.1', '::1'] - self._base_path = ['interfaces', 'loopback'] - self._interfaces = ['lo'] +loopbacks = ['127.0.0.1', '::1'] + +class LoopbackInterfaceTest(BasicInterfaceTest.TestCase): + @classmethod + def setUpClass(cls): + cls._base_path = ['interfaces', 'loopback'] + cls._interfaces = ['lo'] + # call base-classes classmethod + super(cls, cls).setUpClass() + + def tearDown(self): + self.cli_delete(self._base_path) + self.cli_commit() + + # loopback interface must persist! + for intf in self._interfaces: + self.assertIn(intf, interfaces()) def test_add_single_ip_address(self): super().test_add_single_ip_address() - for addr in self._loopback_addresses: + for addr in loopbacks: self.assertTrue(is_intf_addr_assigned('lo', addr)) def test_add_multiple_ip_addresses(self): super().test_add_multiple_ip_addresses() - for addr in self._loopback_addresses: + for addr in loopbacks: self.assertTrue(is_intf_addr_assigned('lo', addr)) + def test_interface_disable(self): + self.skipTest('not supported') + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py index d9635951f..e4280a5b7 100755 --- a/smoketest/scripts/cli/test_interfaces_macsec.py +++ b/smoketest/scripts/cli/test_interfaces_macsec.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -14,6 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import os import re import unittest @@ -22,7 +23,9 @@ from netifaces import interfaces from vyos.configsession import ConfigSessionError from vyos.ifconfig import Section +from vyos.util import cmd from vyos.util import read_file +from vyos.util import get_interface_config from vyos.util import process_named_running def get_config_value(interface, key): @@ -30,18 +33,26 @@ def get_config_value(interface, key): tmp = re.findall(r'\n?{}=(.*)'.format(key), tmp) return tmp[0] -class MACsecInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - super().setUp() - self._base_path = ['interfaces', 'macsec'] - self._options = { 'macsec0': ['source-interface eth0', 'security cipher gcm-aes-128'] } +def get_cipher(interface): + tmp = get_interface_config(interface) + return tmp['linkinfo']['info_data']['cipher_suite'].lower() - # if we have a physical eth1 interface, add a second macsec instance - if 'eth1' in Section.interfaces("ethernet"): - macsec = { 'macsec1': [f'source-interface eth1', 'security cipher gcm-aes-128'] } - self._options.update(macsec) +class MACsecInterfaceTest(BasicInterfaceTest.TestCase): + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._base_path = ['interfaces', 'macsec'] + cls._options = { 'macsec0': ['source-interface eth0', 'security cipher gcm-aes-128'] } - self._interfaces = list(self._options) + # if we have a physical eth1 interface, add a second macsec instance + if 'eth1' in Section.interfaces('ethernet'): + macsec = { 'macsec1': [f'source-interface eth1', 'security cipher gcm-aes-128'] } + cls._options.update(macsec) + + cls._interfaces = list(cls._options) + # call base-classes classmethod + super(cls, cls).setUpClass() def test_macsec_encryption(self): # MACsec can be operating in authentication and encryption mode - both @@ -57,31 +68,31 @@ class MACsecInterfaceTest(BasicInterfaceTest.BaseTest): if option.split()[0] == 'source-interface': src_interface = option.split()[1] - self.session.set(self._base_path + [interface] + option.split()) + self.cli_set(self._base_path + [interface] + option.split()) # Encrypt link - self.session.set(self._base_path + [interface, 'security', 'encrypt']) + self.cli_set(self._base_path + [interface, 'security', 'encrypt']) # check validate() - Physical source interface MTU must be higher then our MTU - self.session.set(self._base_path + [interface, 'mtu', '1500']) + self.cli_set(self._base_path + [interface, 'mtu', '1500']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(self._base_path + [interface, 'mtu']) + self.cli_commit() + self.cli_delete(self._base_path + [interface, 'mtu']) # check validate() - MACsec security keys mandartory when encryption is enabled with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'security', 'mka', 'cak', mak_cak]) + self.cli_commit() + self.cli_set(self._base_path + [interface, 'security', 'mka', 'cak', mak_cak]) # check validate() - MACsec security keys mandartory when encryption is enabled with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'security', 'mka', 'ckn', mak_ckn]) + self.cli_commit() + self.cli_set(self._base_path + [interface, 'security', 'mka', 'ckn', mak_ckn]) - self.session.set(self._base_path + [interface, 'security', 'replay-window', replay_window]) + self.cli_set(self._base_path + [interface, 'security', 'replay-window', replay_window]) # final commit of settings - self.session.commit() + self.cli_commit() tmp = get_config_value(src_interface, 'macsec_integ_only') self.assertTrue("0" in tmp) @@ -105,23 +116,46 @@ class MACsecInterfaceTest(BasicInterfaceTest.BaseTest): # Check for running process self.assertTrue(process_named_running('wpa_supplicant')) - def test_macsec_mandatory_options(self): + def test_macsec_gcm_aes_128(self): interface = 'macsec1' - self.session.set(self._base_path + [interface]) + cipher = 'gcm-aes-128' + self.cli_set(self._base_path + [interface]) + + # check validate() - source interface is mandatory + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(self._base_path + [interface, 'source-interface', 'eth0']) + + # check validate() - cipher is mandatory + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(self._base_path + [interface, 'security', 'cipher', cipher]) + + # final commit and verify + self.cli_commit() + self.assertIn(interface, interfaces()) + self.assertIn(interface, interfaces()) + self.assertEqual(cipher, get_cipher(interface)) + + def test_macsec_gcm_aes_256(self): + interface = 'macsec4' + cipher = 'gcm-aes-256' + self.cli_set(self._base_path + [interface]) # check validate() - source interface is mandatory with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'source-interface', 'eth0']) + self.cli_commit() + self.cli_set(self._base_path + [interface, 'source-interface', 'eth0']) # check validate() - cipher is mandatory with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'security', 'cipher', 'gcm-aes-128']) + self.cli_commit() + self.cli_set(self._base_path + [interface, 'security', 'cipher', cipher]) # final commit and verify - self.session.commit() + self.cli_commit() self.assertIn(interface, interfaces()) + self.assertEqual(cipher, get_cipher(interface)) def test_macsec_source_interface(self): # Ensure source-interface can bot be part of any other bond or bridge @@ -131,24 +165,24 @@ class MACsecInterfaceTest(BasicInterfaceTest.BaseTest): for interface, option_value in self._options.items(): for option in option_value: - self.session.set(self._base_path + [interface] + option.split()) + self.cli_set(self._base_path + [interface] + option.split()) if option.split()[0] == 'source-interface': src_interface = option.split()[1] - self.session.set(base_bridge + ['member', 'interface', src_interface]) + self.cli_set(base_bridge + ['member', 'interface', src_interface]) # check validate() - Source interface must not already be a member of a bridge with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(base_bridge) + self.cli_commit() + self.cli_delete(base_bridge) - self.session.set(base_bond + ['member', 'interface', src_interface]) + self.cli_set(base_bond + ['member', 'interface', src_interface]) # check validate() - Source interface must not already be a member of a bridge with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(base_bond) + self.cli_commit() + self.cli_delete(base_bond) # final commit and verify - self.session.commit() + self.cli_commit() self.assertIn(interface, interfaces()) if __name__ == '__main__': diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py index 00db3f667..655ee770d 100755 --- a/smoketest/scripts/cli/test_interfaces_openvpn.py +++ b/smoketest/scripts/cli/test_interfaces_openvpn.py @@ -21,6 +21,8 @@ from glob import glob from ipaddress import IPv4Network from netifaces import interfaces +from base_vyostest_shim import VyOSUnitTestSHIM + from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError from vyos.util import cmd @@ -58,85 +60,83 @@ def get_vrf(interface): tmp = tmp.replace('upper_', '') return tmp -class TestInterfacesOpenVPN(unittest.TestCase): +class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase): def setUp(self): - self.session = ConfigSession(os.getpid()) - self.session.set(['interfaces', 'dummy', dummy_if, 'address', '192.0.2.1/32']) - self.session.set(['vrf', 'name', vrf_name, 'table', '12345']) + self.cli_set(['interfaces', 'dummy', dummy_if, 'address', '192.0.2.1/32']) + self.cli_set(['vrf', 'name', vrf_name, 'table', '12345']) def tearDown(self): - self.session.delete(base_path) - self.session.delete(['interfaces', 'dummy', dummy_if]) - self.session.delete(['vrf']) - self.session.commit() - del self.session + self.cli_delete(base_path) + self.cli_delete(['interfaces', 'dummy', dummy_if]) + self.cli_delete(['vrf']) + self.cli_commit() def test_openvpn_client_verify(self): # Create OpenVPN client interface and test verify() steps. interface = 'vtun2000' path = base_path + [interface] - self.session.set(path + ['mode', 'client']) + self.cli_set(path + ['mode', 'client']) # check validate() - cannot specify both "encryption disable-ncp" and # "encryption ncp-ciphers" at the same time - self.session.set(path + ['encryption', 'disable-ncp']) - self.session.set(path + ['encryption', 'ncp-ciphers', 'aes192gcm']) + self.cli_set(path + ['encryption', 'disable-ncp']) + self.cli_set(path + ['encryption', 'ncp-ciphers', 'aes192gcm']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['encryption', 'ncp-ciphers']) + self.cli_commit() + self.cli_delete(path + ['encryption', 'ncp-ciphers']) # check validate() - cannot specify local-port in client mode - self.session.set(path + ['local-port', '5000']) + self.cli_set(path + ['local-port', '5000']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['local-port']) + self.cli_commit() + self.cli_delete(path + ['local-port']) # check validate() - cannot specify local-host in client mode - self.session.set(path + ['local-host', '127.0.0.1']) + self.cli_set(path + ['local-host', '127.0.0.1']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['local-host']) + self.cli_commit() + self.cli_delete(path + ['local-host']) # check validate() - cannot specify protocol tcp-passive in client mode - self.session.set(path + ['protocol', 'tcp-passive']) + self.cli_set(path + ['protocol', 'tcp-passive']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['protocol']) + self.cli_commit() + self.cli_delete(path + ['protocol']) # check validate() - remote-host must be set in client mode with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(path + ['remote-host', '192.0.9.9']) + self.cli_commit() + self.cli_set(path + ['remote-host', '192.0.9.9']) # check validate() - cannot specify "tls dh-file" in client mode - self.session.set(path + ['tls', 'dh-file', dh_pem]) + self.cli_set(path + ['tls', 'dh-file', dh_pem]) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['tls']) + self.cli_commit() + self.cli_delete(path + ['tls']) # check validate() - must specify one of "shared-secret-key-file" and "tls" with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(path + ['shared-secret-key-file', s2s_key]) + self.cli_commit() + self.cli_set(path + ['shared-secret-key-file', s2s_key]) # check validate() - must specify one of "shared-secret-key-file" and "tls" with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['shared-secret-key-file', s2s_key]) + self.cli_commit() + self.cli_delete(path + ['shared-secret-key-file', s2s_key]) - self.session.set(path + ['tls', 'ca-cert-file', ca_cert]) - self.session.set(path + ['tls', 'cert-file', ssl_cert]) - self.session.set(path + ['tls', 'key-file', ssl_key]) + self.cli_set(path + ['tls', 'ca-cert-file', ca_cert]) + self.cli_set(path + ['tls', 'cert-file', ssl_cert]) + self.cli_set(path + ['tls', 'key-file', ssl_key]) # check validate() - can not have auth username without a password - self.session.set(path + ['authentication', 'username', 'vyos']) + self.cli_set(path + ['authentication', 'username', 'vyos']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(path + ['authentication', 'password', 'vyos']) + self.cli_commit() + self.cli_set(path + ['authentication', 'password', 'vyos']) # client commit must pass - self.session.commit() + self.cli_commit() self.assertTrue(process_named_running(PROCESS_NAME)) self.assertIn(interface, interfaces()) @@ -152,22 +152,22 @@ class TestInterfacesOpenVPN(unittest.TestCase): path = base_path + [interface] auth_hash = 'sha1' - self.session.set(path + ['device-type', 'tun']) - self.session.set(path + ['encryption', 'cipher', 'aes256']) - self.session.set(path + ['hash', auth_hash]) - self.session.set(path + ['mode', 'client']) - self.session.set(path + ['persistent-tunnel']) - self.session.set(path + ['protocol', protocol]) - self.session.set(path + ['remote-host', remote_host]) - self.session.set(path + ['remote-port', remote_port]) - self.session.set(path + ['tls', 'ca-cert-file', ca_cert]) - self.session.set(path + ['tls', 'cert-file', ssl_cert]) - self.session.set(path + ['tls', 'key-file', ssl_key]) - self.session.set(path + ['vrf', vrf_name]) - self.session.set(path + ['authentication', 'username', interface+'user']) - self.session.set(path + ['authentication', 'password', interface+'secretpw']) - - self.session.commit() + self.cli_set(path + ['device-type', 'tun']) + self.cli_set(path + ['encryption', 'cipher', 'aes256']) + self.cli_set(path + ['hash', auth_hash]) + self.cli_set(path + ['mode', 'client']) + self.cli_set(path + ['persistent-tunnel']) + self.cli_set(path + ['protocol', protocol]) + self.cli_set(path + ['remote-host', remote_host]) + self.cli_set(path + ['remote-port', remote_port]) + self.cli_set(path + ['tls', 'ca-cert-file', ca_cert]) + self.cli_set(path + ['tls', 'cert-file', ssl_cert]) + self.cli_set(path + ['tls', 'key-file', ssl_key]) + self.cli_set(path + ['vrf', vrf_name]) + self.cli_set(path + ['authentication', 'username', interface+'user']) + self.cli_set(path + ['authentication', 'password', interface+'secretpw']) + + self.cli_commit() for ii in num_range: interface = f'vtun{ii}' @@ -200,8 +200,8 @@ class TestInterfacesOpenVPN(unittest.TestCase): self.assertIn(f'{interface}secretpw', pw) # check that no interface remained after deleting them - self.session.delete(base_path) - self.session.commit() + self.cli_delete(base_path) + self.cli_commit() for ii in num_range: interface = f'vtun{ii}' @@ -213,104 +213,104 @@ class TestInterfacesOpenVPN(unittest.TestCase): path = base_path + [interface] # check validate() - must speciy operating mode - self.session.set(path) + self.cli_set(path) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(path + ['mode', 'server']) + self.cli_commit() + self.cli_set(path + ['mode', 'server']) # check validate() - cannot specify protocol tcp-active in server mode - self.session.set(path + ['protocol', 'tcp-active']) + self.cli_set(path + ['protocol', 'tcp-active']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['protocol']) + self.cli_commit() + self.cli_delete(path + ['protocol']) # check validate() - cannot specify local-port in client mode - self.session.set(path + ['remote-port', '5000']) + self.cli_set(path + ['remote-port', '5000']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['remote-port']) + self.cli_commit() + self.cli_delete(path + ['remote-port']) # check validate() - cannot specify local-host in client mode - self.session.set(path + ['remote-host', '127.0.0.1']) + self.cli_set(path + ['remote-host', '127.0.0.1']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['remote-host']) + self.cli_commit() + self.cli_delete(path + ['remote-host']) # check validate() - must specify "tls dh-file" when not using EC keys # in server mode with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(path + ['tls', 'dh-file', dh_pem]) + self.cli_commit() + self.cli_set(path + ['tls', 'dh-file', dh_pem]) # check validate() - must specify "server subnet" or add interface to # bridge in server mode with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() # check validate() - server client-ip-pool is too large # [100.64.0.4 -> 100.127.255.251 = 4194295], maximum is 65536 addresses. - self.session.set(path + ['server', 'subnet', '100.64.0.0/10']) + self.cli_set(path + ['server', 'subnet', '100.64.0.0/10']) with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() # check validate() - cannot specify more than 1 IPv4 and 1 IPv6 server subnet - self.session.set(path + ['server', 'subnet', '100.64.0.0/20']) + self.cli_set(path + ['server', 'subnet', '100.64.0.0/20']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['server', 'subnet', '100.64.0.0/10']) + self.cli_commit() + self.cli_delete(path + ['server', 'subnet', '100.64.0.0/10']) # check validate() - must specify "tls ca-cert-file" with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(path + ['tls', 'ca-cert-file', ca_cert]) + self.cli_commit() + self.cli_set(path + ['tls', 'ca-cert-file', ca_cert]) # check validate() - must specify "tls cert-file" with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(path + ['tls', 'cert-file', ssl_cert]) + self.cli_commit() + self.cli_set(path + ['tls', 'cert-file', ssl_cert]) # check validate() - must specify "tls key-file" with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(path + ['tls', 'key-file', ssl_key]) + self.cli_commit() + self.cli_set(path + ['tls', 'key-file', ssl_key]) # check validate() - cannot specify "tls role" in client-server mode' - self.session.set(path + ['tls', 'role', 'active']) + self.cli_set(path + ['tls', 'role', 'active']) with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() # check validate() - cannot specify "tls role" in client-server mode' - self.session.set(path + ['tls', 'auth-file', auth_key]) + self.cli_set(path + ['tls', 'auth-file', auth_key]) with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() # check validate() - cannot specify "tcp-passive" when "tls role" is "active" - self.session.set(path + ['protocol', 'tcp-passive']) + self.cli_set(path + ['protocol', 'tcp-passive']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['protocol']) + self.cli_commit() + self.cli_delete(path + ['protocol']) # check validate() - cannot specify "tls dh-file" when "tls role" is "active" - self.session.set(path + ['tls', 'dh-file', dh_pem]) + self.cli_set(path + ['tls', 'dh-file', dh_pem]) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['tls', 'dh-file']) + self.cli_commit() + self.cli_delete(path + ['tls', 'dh-file']) # Now test the other path with tls role passive - self.session.set(path + ['tls', 'role', 'passive']) + self.cli_set(path + ['tls', 'role', 'passive']) # check validate() - cannot specify "tcp-active" when "tls role" is "passive" - self.session.set(path + ['protocol', 'tcp-active']) + self.cli_set(path + ['protocol', 'tcp-active']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['protocol']) + self.cli_commit() + self.cli_delete(path + ['protocol']) # check validate() - must specify "tls dh-file" when "tls role" is "passive" with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(path + ['tls', 'dh-file', dh_pem]) + self.cli_commit() + self.cli_set(path + ['tls', 'dh-file', dh_pem]) - self.session.commit() + self.cli_commit() self.assertTrue(process_named_running(PROCESS_NAME)) self.assertIn(interface, interfaces()) @@ -330,29 +330,29 @@ class TestInterfacesOpenVPN(unittest.TestCase): path = base_path + [interface] port = str(2000 + ii) - self.session.set(path + ['device-type', 'tun']) - self.session.set(path + ['encryption', 'cipher', 'aes192']) - self.session.set(path + ['hash', auth_hash]) - self.session.set(path + ['mode', 'server']) - self.session.set(path + ['local-port', port]) - self.session.set(path + ['server', 'subnet', subnet]) - self.session.set(path + ['server', 'topology', 'subnet']) - self.session.set(path + ['keep-alive', 'failure-count', '5']) - self.session.set(path + ['keep-alive', 'interval', '5']) + self.cli_set(path + ['device-type', 'tun']) + self.cli_set(path + ['encryption', 'cipher', 'aes192']) + self.cli_set(path + ['hash', auth_hash]) + self.cli_set(path + ['mode', 'server']) + self.cli_set(path + ['local-port', port]) + self.cli_set(path + ['server', 'subnet', subnet]) + self.cli_set(path + ['server', 'topology', 'subnet']) + self.cli_set(path + ['keep-alive', 'failure-count', '5']) + self.cli_set(path + ['keep-alive', 'interval', '5']) # clients - self.session.set(path + ['server', 'client', 'client1', 'ip', client_ip]) + self.cli_set(path + ['server', 'client', 'client1', 'ip', client_ip]) for route in client1_routes: - self.session.set(path + ['server', 'client', 'client1', 'subnet', route]) + self.cli_set(path + ['server', 'client', 'client1', 'subnet', route]) - self.session.set(path + ['replace-default-route']) - self.session.set(path + ['tls', 'ca-cert-file', ca_cert]) - self.session.set(path + ['tls', 'cert-file', ssl_cert]) - self.session.set(path + ['tls', 'key-file', ssl_key]) - self.session.set(path + ['tls', 'dh-file', dh_pem]) - self.session.set(path + ['vrf', vrf_name]) + self.cli_set(path + ['replace-default-route']) + self.cli_set(path + ['tls', 'ca-cert-file', ca_cert]) + self.cli_set(path + ['tls', 'cert-file', ssl_cert]) + self.cli_set(path + ['tls', 'key-file', ssl_key]) + self.cli_set(path + ['tls', 'dh-file', dh_pem]) + self.cli_set(path + ['vrf', vrf_name]) - self.session.commit() + self.cli_commit() for ii in num_range: interface = f'vtun{ii}' @@ -404,8 +404,8 @@ class TestInterfacesOpenVPN(unittest.TestCase): self.assertIn(interface, interfaces()) # check that no interface remained after deleting them - self.session.delete(base_path) - self.session.commit() + self.cli_delete(base_path) + self.cli_commit() for ii in num_range: interface = f'vtun{ii}' @@ -423,23 +423,23 @@ class TestInterfacesOpenVPN(unittest.TestCase): path = base_path + [interface] port = str(2000 + ii) - self.session.set(path + ['device-type', 'tun']) - self.session.set(path + ['encryption', 'cipher', 'aes192']) - self.session.set(path + ['hash', auth_hash]) - self.session.set(path + ['mode', 'server']) - self.session.set(path + ['local-port', port]) - self.session.set(path + ['server', 'subnet', subnet]) - self.session.set(path + ['server', 'topology', 'net30']) - self.session.set(path + ['replace-default-route']) - self.session.set(path + ['keep-alive', 'failure-count', '10']) - self.session.set(path + ['keep-alive', 'interval', '5']) - self.session.set(path + ['tls', 'ca-cert-file', ca_cert]) - self.session.set(path + ['tls', 'cert-file', ssl_cert]) - self.session.set(path + ['tls', 'key-file', ssl_key]) - self.session.set(path + ['tls', 'dh-file', dh_pem]) - self.session.set(path + ['vrf', vrf_name]) - - self.session.commit() + self.cli_set(path + ['device-type', 'tun']) + self.cli_set(path + ['encryption', 'cipher', 'aes192']) + self.cli_set(path + ['hash', auth_hash]) + self.cli_set(path + ['mode', 'server']) + self.cli_set(path + ['local-port', port]) + self.cli_set(path + ['server', 'subnet', subnet]) + self.cli_set(path + ['server', 'topology', 'net30']) + self.cli_set(path + ['replace-default-route']) + self.cli_set(path + ['keep-alive', 'failure-count', '10']) + self.cli_set(path + ['keep-alive', 'interval', '5']) + self.cli_set(path + ['tls', 'ca-cert-file', ca_cert]) + self.cli_set(path + ['tls', 'cert-file', ssl_cert]) + self.cli_set(path + ['tls', 'key-file', ssl_key]) + self.cli_set(path + ['tls', 'dh-file', dh_pem]) + self.cli_set(path + ['vrf', vrf_name]) + + self.cli_commit() for ii in num_range: interface = f'vtun{ii}' @@ -479,8 +479,8 @@ class TestInterfacesOpenVPN(unittest.TestCase): self.assertIn(interface, interfaces()) # check that no interface remained after deleting them - self.session.delete(base_path) - self.session.commit() + self.cli_delete(base_path) + self.cli_commit() for ii in num_range: interface = f'vtun{ii}' @@ -493,57 +493,57 @@ class TestInterfacesOpenVPN(unittest.TestCase): interface = 'vtun5000' path = base_path + [interface] - self.session.set(path + ['mode', 'site-to-site']) + self.cli_set(path + ['mode', 'site-to-site']) # check validate() - encryption ncp-ciphers cannot be specified in site-to-site mode - self.session.set(path + ['encryption', 'ncp-ciphers', 'aes192gcm']) + self.cli_set(path + ['encryption', 'ncp-ciphers', 'aes192gcm']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['encryption']) + self.cli_commit() + self.cli_delete(path + ['encryption']) # check validate() - must specify "local-address" or add interface to bridge with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(path + ['local-address', '10.0.0.1']) - self.session.set(path + ['local-address', '2001:db8:1::1']) + self.cli_commit() + self.cli_set(path + ['local-address', '10.0.0.1']) + self.cli_set(path + ['local-address', '2001:db8:1::1']) # check validate() - cannot specify more than 1 IPv4 local-address - self.session.set(path + ['local-address', '10.0.0.2']) + self.cli_set(path + ['local-address', '10.0.0.2']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['local-address', '10.0.0.2']) + self.cli_commit() + self.cli_delete(path + ['local-address', '10.0.0.2']) # check validate() - cannot specify more than 1 IPv6 local-address - self.session.set(path + ['local-address', '2001:db8:1::2']) + self.cli_set(path + ['local-address', '2001:db8:1::2']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['local-address', '2001:db8:1::2']) + self.cli_commit() + self.cli_delete(path + ['local-address', '2001:db8:1::2']) # check validate() - IPv4 "local-address" requires IPv4 "remote-address" # or IPv4 "local-address subnet" with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(path + ['remote-address', '192.168.0.1']) - self.session.set(path + ['remote-address', '2001:db8:ffff::1']) + self.cli_commit() + self.cli_set(path + ['remote-address', '192.168.0.1']) + self.cli_set(path + ['remote-address', '2001:db8:ffff::1']) # check validate() - Cannot specify more than 1 IPv4 "remote-address" - self.session.set(path + ['remote-address', '192.168.0.2']) + self.cli_set(path + ['remote-address', '192.168.0.2']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['remote-address', '192.168.0.2']) + self.cli_commit() + self.cli_delete(path + ['remote-address', '192.168.0.2']) # check validate() - Cannot specify more than 1 IPv6 "remote-address" - self.session.set(path + ['remote-address', '2001:db8:ffff::2']) + self.cli_set(path + ['remote-address', '2001:db8:ffff::2']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(path + ['remote-address', '2001:db8:ffff::2']) + self.cli_commit() + self.cli_delete(path + ['remote-address', '2001:db8:ffff::2']) # check validate() - Must specify one of "shared-secret-key-file" and "tls" with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(path + ['shared-secret-key-file', s2s_key]) + self.cli_commit() + self.cli_set(path + ['shared-secret-key-file', s2s_key]) - self.session.commit() + self.cli_commit() def test_openvpn_site2site_interfaces_tun(self): # Create two OpenVPN site-to-site interfaces @@ -561,23 +561,23 @@ class TestInterfacesOpenVPN(unittest.TestCase): path = base_path + [interface] port = str(3000 + ii) - self.session.set(path + ['local-address', local_address]) + self.cli_set(path + ['local-address', local_address]) # even numbers use tun type, odd numbers use tap type if ii % 2 == 0: - self.session.set(path + ['device-type', 'tun']) + self.cli_set(path + ['device-type', 'tun']) else: - self.session.set(path + ['device-type', 'tap']) - self.session.set(path + ['local-address', local_address, 'subnet-mask', local_address_subnet]) + self.cli_set(path + ['device-type', 'tap']) + self.cli_set(path + ['local-address', local_address, 'subnet-mask', local_address_subnet]) - self.session.set(path + ['mode', 'site-to-site']) - self.session.set(path + ['local-port', port]) - self.session.set(path + ['remote-port', port]) - self.session.set(path + ['shared-secret-key-file', s2s_key]) - self.session.set(path + ['remote-address', remote_address]) - self.session.set(path + ['vrf', vrf_name]) + self.cli_set(path + ['mode', 'site-to-site']) + self.cli_set(path + ['local-port', port]) + self.cli_set(path + ['remote-port', port]) + self.cli_set(path + ['shared-secret-key-file', s2s_key]) + self.cli_set(path + ['remote-address', remote_address]) + self.cli_set(path + ['vrf', vrf_name]) - self.session.commit() + self.cli_commit() for ii in num_range: interface = f'vtun{ii}' @@ -608,8 +608,8 @@ class TestInterfacesOpenVPN(unittest.TestCase): # check that no interface remained after deleting them - self.session.delete(base_path) - self.session.commit() + self.cli_delete(base_path) + self.cli_commit() for ii in num_range: interface = f'vtun{ii}' @@ -625,27 +625,27 @@ if __name__ == '__main__': # Generate mandatory SSL certificate tmp = f'openssl req -newkey rsa:4096 -new -nodes -x509 -days 3650 '\ f'-keyout {ssl_key} -out {ssl_cert} -subj {subject}' - print(cmd(tmp)) + cmd(tmp) if not os.path.isfile(ca_cert): # Generate "CA" tmp = f'openssl req -new -x509 -key {ssl_key} -out {ca_cert} -subj {subject}' - print(cmd(tmp)) + cmd(tmp) if not os.path.isfile(dh_pem): # Generate "DH" key tmp = f'openssl dhparam -out {dh_pem} 2048' - print(cmd(tmp)) + cmd(tmp) if not os.path.isfile(s2s_key): # Generate site-2-site key tmp = f'openvpn --genkey --secret {s2s_key}' - print(cmd(tmp)) + cmd(tmp) if not os.path.isfile(auth_key): # Generate TLS auth key tmp = f'openvpn --genkey --secret {auth_key}' - print(cmd(tmp)) + cmd(tmp) for file in [ca_cert, ssl_cert, ssl_key, dh_pem, s2s_key, auth_key]: cmd(f'sudo chown openvpn:openvpn {file}') diff --git a/smoketest/scripts/cli/test_interfaces_pppoe.py b/smoketest/scripts/cli/test_interfaces_pppoe.py index 6bfe35d86..b8682fe71 100755 --- a/smoketest/scripts/cli/test_interfaces_pppoe.py +++ b/smoketest/scripts/cli/test_interfaces_pppoe.py @@ -15,11 +15,13 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import re -import os import unittest from psutil import process_iter -from vyos.configsession import ConfigSession, ConfigSessionError +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError from vyos.util import read_file config_file = '/etc/ppp/peers/{}' @@ -42,16 +44,14 @@ def get_dhcp6c_config_value(interface, key): out.append(item.replace(';','')) return out -class PPPoEInterfaceTest(unittest.TestCase): +class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase): def setUp(self): - self.session = ConfigSession(os.getpid()) self._interfaces = ['pppoe10', 'pppoe20', 'pppoe30'] self._source_interface = 'eth0' def tearDown(self): - self.session.delete(base_path) - self.session.commit() - del self.session + self.cli_delete(base_path) + self.cli_commit() def test_pppoe_client(self): # Check if PPPoE dialer can be configured and runs @@ -60,19 +60,19 @@ class PPPoEInterfaceTest(unittest.TestCase): passwd = 'VyOS-passwd-' + interface mtu = '1400' - self.session.set(base_path + [interface, 'authentication', 'user', user]) - self.session.set(base_path + [interface, 'authentication', 'password', passwd]) - self.session.set(base_path + [interface, 'default-route', 'auto']) - self.session.set(base_path + [interface, 'mtu', mtu]) - self.session.set(base_path + [interface, 'no-peer-dns']) + self.cli_set(base_path + [interface, 'authentication', 'user', user]) + self.cli_set(base_path + [interface, 'authentication', 'password', passwd]) + self.cli_set(base_path + [interface, 'default-route', 'auto']) + self.cli_set(base_path + [interface, 'mtu', mtu]) + self.cli_set(base_path + [interface, 'no-peer-dns']) # check validate() - a source-interface is required with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(base_path + [interface, 'source-interface', self._source_interface]) + self.cli_commit() + self.cli_set(base_path + [interface, 'source-interface', self._source_interface]) # commit changes - self.session.commit() + self.cli_commit() # verify configuration file(s) for interface in self._interfaces: @@ -97,27 +97,48 @@ class PPPoEInterfaceTest(unittest.TestCase): self.assertTrue(running) + + def test_pppoe_clent_disabled_interface(self): + # Check if PPPoE Client can be disabled + for interface in self._interfaces: + self.cli_set(base_path + [interface, 'authentication', 'user', 'vyos']) + self.cli_set(base_path + [interface, 'authentication', 'password', 'vyos']) + self.cli_set(base_path + [interface, 'source-interface', self._source_interface]) + self.cli_set(base_path + [interface, 'disable']) + + self.cli_commit() + + # Validate PPPoE client process + running = False + for interface in self._interfaces: + for proc in process_iter(): + if interface in proc.cmdline(): + running = True + + self.assertFalse(running) + + def test_pppoe_dhcpv6pd(self): # Check if PPPoE dialer can be configured with DHCPv6-PD address = '1' sla_id = '0' sla_len = '8' for interface in self._interfaces: - self.session.set(base_path + [interface, 'authentication', 'user', 'vyos']) - self.session.set(base_path + [interface, 'authentication', 'password', 'vyos']) - self.session.set(base_path + [interface, 'default-route', 'none']) - self.session.set(base_path + [interface, 'no-peer-dns']) - self.session.set(base_path + [interface, 'source-interface', self._source_interface]) - self.session.set(base_path + [interface, 'ipv6', 'address', 'autoconf']) + self.cli_set(base_path + [interface, 'authentication', 'user', 'vyos']) + self.cli_set(base_path + [interface, 'authentication', 'password', 'vyos']) + self.cli_set(base_path + [interface, 'default-route', 'none']) + self.cli_set(base_path + [interface, 'no-peer-dns']) + self.cli_set(base_path + [interface, 'source-interface', self._source_interface]) + self.cli_set(base_path + [interface, 'ipv6', 'address', 'autoconf']) # prefix delegation stuff dhcpv6_pd_base = base_path + [interface, 'dhcpv6-options', 'pd', '0'] - self.session.set(dhcpv6_pd_base + ['length', '56']) - self.session.set(dhcpv6_pd_base + ['interface', self._source_interface, 'address', address]) - self.session.set(dhcpv6_pd_base + ['interface', self._source_interface, 'sla-id', sla_id]) + self.cli_set(dhcpv6_pd_base + ['length', '56']) + self.cli_set(dhcpv6_pd_base + ['interface', self._source_interface, 'address', address]) + self.cli_set(dhcpv6_pd_base + ['interface', self._source_interface, 'sla-id', sla_id]) # commit changes - self.session.commit() + self.cli_commit() # verify "normal" PPPoE value - 1492 is default MTU tmp = get_config_value(interface, 'mtu')[1] diff --git a/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py b/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py index 85e5e70bd..ff343bb87 100755 --- a/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py +++ b/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 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 @@ -18,20 +18,24 @@ import unittest from base_interfaces_test import BasicInterfaceTest -class PEthInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._test_ip = True - self._test_ipv6 = True - self._test_mtu = True - self._test_vlan = True - self._test_qinq = True - self._base_path = ['interfaces', 'pseudo-ethernet'] - self._options = { +class PEthInterfaceTest(BasicInterfaceTest.TestCase): + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._test_ipv6_pd = True + cls._test_ipv6_dhcpc6 = True + cls._test_mtu = True + cls._test_vlan = True + cls._test_qinq = True + cls._base_path = ['interfaces', 'pseudo-ethernet'] + cls._options = { 'peth0': ['source-interface eth1'], 'peth1': ['source-interface eth1'], } - self._interfaces = list(self._options) - super().setUp() + cls._interfaces = list(cls._options) + # call base-classes classmethod + super(cls, cls).setUpClass() if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_interfaces_tunnel.py b/smoketest/scripts/cli/test_interfaces_tunnel.py index ca68cb8ba..6af31ddff 100755 --- a/smoketest/scripts/cli/test_interfaces_tunnel.py +++ b/smoketest/scripts/cli/test_interfaces_tunnel.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -15,365 +15,222 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import unittest -import json - -from vyos.configsession import ConfigSession -from vyos.configsession import ConfigSessionError -from vyos.util import cmd from base_interfaces_test import BasicInterfaceTest +from vyos.configsession import ConfigSessionError +from vyos.util import get_interface_config +from vyos.template import inc_ip + remote_ip4 = '192.0.2.100' remote_ip6 = '2001:db8::ffff' source_if = 'dum2222' mtu = 1476 -def tunnel_conf(interface): - tmp = cmd(f'ip -d -j link show {interface}') - # {'address': '2.2.2.2', - # 'broadcast': '192.0.2.10', - # 'flags': ['POINTOPOINT', 'NOARP', 'UP', 'LOWER_UP'], - # 'group': 'default', - # 'gso_max_segs': 65535, - # 'gso_max_size': 65536, - # 'ifindex': 10, - # 'ifname': 'tun10', - # 'inet6_addr_gen_mode': 'none', - # 'link': None, - # 'link_pointtopoint': True, - # 'link_type': 'gre', - # 'linkinfo': {'info_data': {'local': '2.2.2.2', - # 'pmtudisc': True, - # 'remote': '192.0.2.10', - # 'tos': '0x1', - # 'ttl': 255}, - # 'info_kind': 'gre'}, - # 'linkmode': 'DEFAULT', - # 'max_mtu': 65511, - # 'min_mtu': 68, - # 'mtu': 1476, - # 'num_rx_queues': 1, - # 'num_tx_queues': 1, - # 'operstate': 'UNKNOWN', - # 'promiscuity': 0, - # 'qdisc': 'noqueue', - # 'txqlen': 1000} - return json.loads(tmp)[0] - -class TunnelInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._test_mtu = True - self._base_path = ['interfaces', 'tunnel'] - self.local_v4 = '192.0.2.1' - self.local_v6 = '2001:db8::1' - - self._options = { - 'tun10': ['encapsulation ipip', 'remote-ip 192.0.2.10', 'local-ip ' + self.local_v4], - 'tun20': ['encapsulation gre', 'remote-ip 192.0.2.20', 'local-ip ' + self.local_v4], +class TunnelInterfaceTest(BasicInterfaceTest.TestCase): + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._test_mtu = True + cls._base_path = ['interfaces', 'tunnel'] + cls.local_v4 = '192.0.2.1' + cls.local_v6 = '2001:db8::1' + cls._options = { + 'tun10': ['encapsulation ipip', 'remote 192.0.2.10', 'source-address ' + cls.local_v4], + 'tun20': ['encapsulation gre', 'remote 192.0.2.20', 'source-address ' + cls.local_v4], } + cls._interfaces = list(cls._options) + # call base-classes classmethod + super(cls, cls).setUpClass() - self._interfaces = list(self._options) + def setUp(self): super().setUp() - - self.session.set(['interfaces', 'dummy', source_if, 'address', self.local_v4 + '/32']) - self.session.set(['interfaces', 'dummy', source_if, 'address', self.local_v6 + '/128']) + self.cli_set(['interfaces', 'dummy', source_if, 'address', self.local_v4 + '/32']) + self.cli_set(['interfaces', 'dummy', source_if, 'address', self.local_v6 + '/128']) def tearDown(self): - self.session.delete(['interfaces', 'dummy', source_if]) + self.cli_delete(['interfaces', 'dummy', source_if]) super().tearDown() - def test_ipip(self): - interface = 'tun100' - encapsulation = 'ipip' - local_if_addr = '10.10.10.1/24' - - self.session.set(self._base_path + [interface, 'address', local_if_addr]) - - # Must provide an "encapsulation" for tunnel tun10 - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'encapsulation', encapsulation]) - - # Must configure either local-ip or dhcp-interface for tunnel ipip tun100 - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'local-ip', self.local_v4]) - - # missing required option remote for ipip - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4]) - - # Configure Tunnel Source interface - self.session.set(self._base_path + [interface, 'source-interface', source_if]) - - self.session.commit() - - conf = tunnel_conf(interface) - self.assertEqual(interface, conf['ifname']) - self.assertEqual(encapsulation, conf['link_type']) - self.assertEqual(mtu, conf['mtu']) - self.assertEqual(source_if, conf['link']) - - self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local']) - self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote']) - - def test_ipip6(self): - interface = 'tun110' - encapsulation = 'ipip6' - local_if_addr = '10.10.10.1/24' - - self.session.set(self._base_path + [interface, 'address', local_if_addr]) - - # Must provide an "encapsulation" for tunnel tun10 - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'encapsulation', encapsulation]) - - # Must configure either local-ip or dhcp-interface for tunnel ipip tun100 - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'local-ip', self.local_v6]) - - # missing required option remote for ipip - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'remote-ip', remote_ip6]) - - # Configure Tunnel Source interface - self.session.set(self._base_path + [interface, 'source-interface', source_if]) - - self.session.commit() - - conf = tunnel_conf(interface) - self.assertEqual(interface, conf['ifname']) - self.assertEqual('tunnel6', conf['link_type']) - self.assertEqual(mtu, conf['mtu']) - self.assertEqual(source_if, conf['link']) - - self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local']) - self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote']) - - def test_tunnel_verify_ipv4_local_remote_addr(self): + def test_ipv4_encapsulations(self): # When running tests ensure that for certain encapsulation types the # local and remote IP address is actually an IPv4 address interface = f'tun1000' local_if_addr = f'10.10.200.1/24' - for encapsulation in ['ipip', 'sit', 'gre']: - self.session.set(self._base_path + [interface, 'address', local_if_addr]) - self.session.set(self._base_path + [interface, 'encapsulation', encapsulation]) - self.session.set(self._base_path + [interface, 'local-ip', self.local_v6]) - self.session.set(self._base_path + [interface, 'remote-ip', remote_ip6]) + for encapsulation in ['ipip', 'sit', 'gre', 'gretap']: + self.cli_set(self._base_path + [interface, 'address', local_if_addr]) + self.cli_set(self._base_path + [interface, 'encapsulation', encapsulation]) + self.cli_set(self._base_path + [interface, 'source-address', self.local_v6]) + self.cli_set(self._base_path + [interface, 'remote', remote_ip6]) - # Encapsulation mode requires IPv4 local-ip + # Encapsulation mode requires IPv4 source-address with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'local-ip', self.local_v4]) + self.cli_commit() + self.cli_set(self._base_path + [interface, 'source-address', self.local_v4]) - # Encapsulation mode requires IPv4 local-ip + # Encapsulation mode requires IPv4 remote with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4]) + self.cli_commit() + self.cli_set(self._base_path + [interface, 'remote', remote_ip4]) + self.cli_set(self._base_path + [interface, 'source-interface', source_if]) + + # Source interface can not be used with sit and gretap + if encapsulation in ['sit', 'gretap']: + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(self._base_path + [interface, 'source-interface']) # Check if commit is ok - self.session.commit() + self.cli_commit() + + conf = get_interface_config(interface) + if encapsulation not in ['sit', 'gretap']: + self.assertEqual(source_if, conf['link']) + + self.assertEqual(interface, conf['ifname']) + self.assertEqual(mtu, conf['mtu']) + self.assertEqual(encapsulation, conf['linkinfo']['info_kind']) + self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local']) + self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote']) + self.assertTrue(conf['linkinfo']['info_data']['pmtudisc']) # cleanup this instance - self.session.delete(self._base_path + [interface]) - self.session.commit() + self.cli_delete(self._base_path + [interface]) + self.cli_commit() - def test_tunnel_verify_ipv6_local_remote_addr(self): + def test_ipv6_encapsulations(self): # When running tests ensure that for certain encapsulation types the # local and remote IP address is actually an IPv6 address interface = f'tun1010' local_if_addr = f'10.10.200.1/24' - for encapsulation in ['ipip6', 'ip6ip6', 'ip6gre']: - self.session.set(self._base_path + [interface, 'address', local_if_addr]) - self.session.set(self._base_path + [interface, 'encapsulation', encapsulation]) - self.session.set(self._base_path + [interface, 'local-ip', self.local_v4]) - self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4]) + for encapsulation in ['ipip6', 'ip6ip6', 'ip6gre', 'ip6gretap']: + self.cli_set(self._base_path + [interface, 'address', local_if_addr]) + self.cli_set(self._base_path + [interface, 'encapsulation', encapsulation]) + self.cli_set(self._base_path + [interface, 'source-address', self.local_v4]) + self.cli_set(self._base_path + [interface, 'remote', remote_ip4]) - # Encapsulation mode requires IPv6 local-ip + # Encapsulation mode requires IPv6 source-address with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'local-ip', self.local_v6]) + self.cli_commit() + self.cli_set(self._base_path + [interface, 'source-address', self.local_v6]) - # Encapsulation mode requires IPv6 local-ip + # Encapsulation mode requires IPv6 remote with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'remote-ip', remote_ip6]) + self.cli_commit() + self.cli_set(self._base_path + [interface, 'remote', remote_ip6]) - # Check if commit is ok - self.session.commit() + # Configure Tunnel Source interface + self.cli_set(self._base_path + [interface, 'source-interface', source_if]) + # Source interface can not be used with ip6gretap + if encapsulation in ['ip6gretap']: + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(self._base_path + [interface, 'source-interface']) - # cleanup this instance - self.session.delete(self._base_path + [interface]) - self.session.commit() + # Check if commit is ok + self.cli_commit() - def test_tunnel_verify_local_dhcp(self): - # We can not use local-ip and dhcp-interface at the same time + conf = get_interface_config(interface) + if encapsulation not in ['ip6gretap']: + self.assertEqual(source_if, conf['link']) - interface = f'tun1020' - local_if_addr = f'10.0.0.1/24' + self.assertEqual(interface, conf['ifname']) + self.assertEqual(mtu, conf['mtu']) - self.session.set(self._base_path + [interface, 'address', local_if_addr]) - self.session.set(self._base_path + [interface, 'encapsulation', 'gre']) - self.session.set(self._base_path + [interface, 'local-ip', self.local_v4]) - self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4]) - self.session.set(self._base_path + [interface, 'dhcp-interface', 'eth0']) + # Not applicable for ip6gre + if 'proto' in conf['linkinfo']['info_data']: + self.assertEqual(encapsulation, conf['linkinfo']['info_data']['proto']) - # local-ip and dhcp-interface can not be used at the same time - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(self._base_path + [interface, 'dhcp-interface']) + # remap encapsulation protocol(s) only for ipip6, ip6ip6 + if encapsulation in ['ipip6', 'ip6ip6']: + encapsulation = 'ip6tnl' - # Check if commit is ok - self.session.commit() + self.assertEqual(encapsulation, conf['linkinfo']['info_kind']) + self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local']) + self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote']) - def test_tunnel_ip6ip6(self): - interface = 'tun120' - encapsulation = 'ip6ip6' - local_if_addr = '2001:db8:f00::1/24' + # cleanup this instance + self.cli_delete(self._base_path + [interface]) + self.cli_commit() - self.session.set(self._base_path + [interface, 'address', local_if_addr]) + def test_tunnel_verify_local_dhcp(self): + # We can not use source-address and dhcp-interface at the same time - # Must provide an "encapsulation" for tunnel tun10 - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'encapsulation', encapsulation]) + interface = f'tun1020' + local_if_addr = f'10.0.0.1/24' - # Must configure either local-ip or dhcp-interface for tunnel ipip tun100 - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'local-ip', self.local_v6]) + self.cli_set(self._base_path + [interface, 'address', local_if_addr]) + self.cli_set(self._base_path + [interface, 'encapsulation', 'gre']) + self.cli_set(self._base_path + [interface, 'source-address', self.local_v4]) + self.cli_set(self._base_path + [interface, 'remote', remote_ip4]) + self.cli_set(self._base_path + [interface, 'dhcp-interface', 'eth0']) - # missing required option remote for ipip + # source-address and dhcp-interface can not be used at the same time with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'remote-ip', remote_ip6]) - - # Configure Tunnel Source interface - self.session.set(self._base_path + [interface, 'source-interface', source_if]) + self.cli_commit() + self.cli_delete(self._base_path + [interface, 'dhcp-interface']) - self.session.commit() - - conf = tunnel_conf(interface) - self.assertEqual(interface, conf['ifname']) - self.assertEqual('tunnel6', conf['link_type']) - self.assertEqual(mtu, conf['mtu']) - self.assertEqual(source_if, conf['link']) - - self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local']) - self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote']) + # Check if commit is ok + self.cli_commit() - def test_tunnel_gre_ipv4(self): - interface = 'tun200' + def test_tunnel_parameters_gre(self): + interface = f'tun1030' + gre_key = '10' encapsulation = 'gre' - local_if_addr = '172.16.1.1/24' + tos = '20' - self.session.set(self._base_path + [interface, 'address', local_if_addr]) + self.cli_set(self._base_path + [interface, 'encapsulation', encapsulation]) + self.cli_set(self._base_path + [interface, 'source-address', self.local_v4]) + self.cli_set(self._base_path + [interface, 'remote', remote_ip4]) - # Must provide an "encapsulation" for tunnel tun10 - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'encapsulation', encapsulation]) - - # Must configure either local-ip or dhcp-interface - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'local-ip', self.local_v4]) - - # No assertion is raised for GRE remote-ip when missing - self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4]) - - # Configure Tunnel Source interface - self.session.set(self._base_path + [interface, 'source-interface', source_if]) - - self.session.commit() + self.cli_set(self._base_path + [interface, 'parameters', 'ip', 'no-pmtu-discovery']) + self.cli_set(self._base_path + [interface, 'parameters', 'ip', 'key', gre_key]) + self.cli_set(self._base_path + [interface, 'parameters', 'ip', 'tos', tos]) - conf = tunnel_conf(interface) - self.assertEqual(interface, conf['ifname']) - self.assertEqual(encapsulation, conf['link_type']) - self.assertEqual(mtu, conf['mtu']) - self.assertEqual(source_if, conf['link']) + # Check if commit is ok + self.cli_commit() + conf = get_interface_config(interface) + self.assertEqual(mtu, conf['mtu']) + self.assertEqual(interface, conf['ifname']) + self.assertEqual(encapsulation, conf['linkinfo']['info_kind']) self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local']) - self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote']) - - - def test_gre_ipv6(self): - interface = 'tun210' - encapsulation = 'ip6gre' - local_if_addr = '2001:db8:f01::1/24' + self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote']) + self.assertEqual(0, conf['linkinfo']['info_data']['ttl']) + self.assertFalse( conf['linkinfo']['info_data']['pmtudisc']) - self.session.set(self._base_path + [interface, 'address', local_if_addr]) + def test_gretap_parameters_change(self): + interface = f'tun1040' + gre_key = '10' + encapsulation = 'gretap' + tos = '20' - # Must provide an "encapsulation" for tunnel tun10 - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'encapsulation', encapsulation]) - - # Must configure either local-ip or dhcp-interface - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'local-ip', self.local_v6]) + self.cli_set(self._base_path + [interface, 'encapsulation', encapsulation]) + self.cli_set(self._base_path + [interface, 'source-address', self.local_v4]) + self.cli_set(self._base_path + [interface, 'remote', remote_ip4]) - # No assertion is raised for GRE remote-ip when missing - self.session.set(self._base_path + [interface, 'remote-ip', remote_ip6]) - - # Configure Tunnel Source interface - self.session.set(self._base_path + [interface, 'source-interface', source_if]) - - self.session.commit() - - conf = tunnel_conf(interface) - self.assertEqual(interface, conf['ifname']) - self.assertEqual(encapsulation, conf['link_type']) - self.assertEqual(mtu, conf['mtu']) - self.assertEqual(source_if, conf['link']) - - self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local']) - self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote']) - - - def test_tunnel_sit(self): - interface = 'tun300' - encapsulation = 'sit' - local_if_addr = '172.16.2.1/24' - - self.session.set(self._base_path + [interface, 'address', local_if_addr]) - - # Must provide an "encapsulation" for tunnel tun10 - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'encapsulation', encapsulation]) - - # Must configure either local-ip or dhcp-interface - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'local-ip', self.local_v4]) - - # No assertion is raised for GRE remote-ip when missing - self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4]) - - # Source interface can not be used with sit - self.session.set(self._base_path + [interface, 'source-interface', source_if]) - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(self._base_path + [interface, 'source-interface']) - - self.session.commit() - - conf = tunnel_conf(interface) - self.assertEqual(interface, conf['ifname']) - self.assertEqual(encapsulation, conf['link_type']) - self.assertEqual(mtu, conf['mtu']) + # Check if commit is ok + self.cli_commit() + conf = get_interface_config(interface) + self.assertEqual(mtu, conf['mtu']) + self.assertEqual(interface, conf['ifname']) + self.assertEqual(encapsulation, conf['linkinfo']['info_kind']) self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local']) - self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote']) + self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote']) + self.assertEqual(0, conf['linkinfo']['info_data']['ttl']) + + # Change remote ip address (inc host by 2 + new_remote = inc_ip(remote_ip4, 2) + self.cli_set(self._base_path + [interface, 'remote', new_remote]) + # Check if commit is ok + self.cli_commit() + conf = get_interface_config(interface) + self.assertEqual(new_remote, conf['linkinfo']['info_data']['remote']) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_interfaces_vxlan.py b/smoketest/scripts/cli/test_interfaces_vxlan.py index a9b0fc5a1..7b420cd51 100755 --- a/smoketest/scripts/cli/test_interfaces_vxlan.py +++ b/smoketest/scripts/cli/test_interfaces_vxlan.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -16,19 +16,75 @@ import unittest -from vyos.configsession import ConfigSession, ConfigSessionError +from vyos.configsession import ConfigSession +from vyos.ifconfig import Interface +from vyos.util import get_interface_config + from base_interfaces_test import BasicInterfaceTest -class VXLANInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._test_mtu = True - self._base_path = ['interfaces', 'vxlan'] - self._options = { - 'vxlan0': ['vni 10', 'remote 127.0.0.2'], - 'vxlan1': ['vni 20', 'group 239.1.1.1', 'source-interface eth0'], +class VXLANInterfaceTest(BasicInterfaceTest.TestCase): + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._test_mtu = True + cls._base_path = ['interfaces', 'vxlan'] + cls._options = { + 'vxlan10': ['vni 10', 'remote 127.0.0.2'], + 'vxlan20': ['vni 20', 'group 239.1.1.1', 'source-interface eth0'], + 'vxlan30': ['vni 30', 'remote 2001:db8:2000::1', 'source-address 2001:db8:1000::1', 'parameters ipv6 flowlabel 0x1000'], } - self._interfaces = list(self._options) - super().setUp() + cls._interfaces = list(cls._options) + # call base-classes classmethod + super(cls, cls).setUpClass() + + def test_vxlan_parameters(self): + tos = '40' + ttl = 20 + for intf in self._interfaces: + for option in self._options.get(intf, []): + self.cli_set(self._base_path + [intf] + option.split()) + + self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'dont-fragment']) + self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'tos', tos]) + self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'ttl', str(ttl)]) + ttl += 10 + + self.cli_commit() + + ttl = 20 + for interface in self._interfaces: + options = get_interface_config(interface) + + vni = options['linkinfo']['info_data']['id'] + self.assertIn(f'vni {vni}', self._options[interface]) + + if any('link' in s for s in self._options[interface]): + link = options['linkinfo']['info_data']['link'] + self.assertIn(f'source-interface {link}', self._options[interface]) + + if any('local6' in s for s in self._options[interface]): + remote = options['linkinfo']['info_data']['local6'] + self.assertIn(f'source-address {local6}', self._options[interface]) + + if any('remote6' in s for s in self._options[interface]): + remote = options['linkinfo']['info_data']['remote6'] + self.assertIn(f'remote {remote}', self._options[interface]) + + if any('group' in s for s in self._options[interface]): + group = options['linkinfo']['info_data']['group'] + self.assertIn(f'group {group}', self._options[interface]) + + if any('flowlabel' in s for s in self._options[interface]): + label = options['linkinfo']['info_data']['label'] + self.assertIn(f'parameters ipv6 flowlabel {label}', self._options[interface]) + + self.assertEqual('vxlan', options['linkinfo']['info_kind']) + self.assertEqual('set', options['linkinfo']['info_data']['df']) + self.assertEqual(f'0x{tos}', options['linkinfo']['info_data']['tos']) + self.assertEqual(ttl, options['linkinfo']['info_data']['ttl']) + self.assertEqual(Interface(interface).get_admin_state(), 'up') + ttl += 10 if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_interfaces_wireguard.py b/smoketest/scripts/cli/test_interfaces_wireguard.py index d9a51b146..d31ec0332 100755 --- a/smoketest/scripts/cli/test_interfaces_wireguard.py +++ b/smoketest/scripts/cli/test_interfaces_wireguard.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -17,8 +17,10 @@ import os import unittest -from vyos.configsession import ConfigSession, ConfigSessionError -from base_interfaces_test import BasicInterfaceTest +from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError + # Generate WireGuard default keypair if not os.path.isdir('/config/auth/wireguard/default'): @@ -26,17 +28,15 @@ if not os.path.isdir('/config/auth/wireguard/default'): base_path = ['interfaces', 'wireguard'] -class WireGuardInterfaceTest(unittest.TestCase): +class WireGuardInterfaceTest(VyOSUnitTestSHIM.TestCase): def setUp(self): - self.session = ConfigSession(os.getpid()) self._test_addr = ['192.0.2.1/26', '192.0.2.255/31', '192.0.2.64/32', '2001:db8:1::ffff/64', '2001:db8:101::1/112'] self._interfaces = ['wg0', 'wg1'] def tearDown(self): - self.session.delete(base_path) - self.session.commit() - del self.session + self.cli_delete(base_path) + self.cli_commit() def test_wireguard_peer(self): # Create WireGuard interfaces with associated peers @@ -46,19 +46,19 @@ class WireGuardInterfaceTest(unittest.TestCase): pubkey = 'n6ZZL7ph/QJUJSUUTyu19c77my1dRCDHkMzFQUO9Z3A=' for addr in self._test_addr: - self.session.set(base_path + [intf, 'address', addr]) + self.cli_set(base_path + [intf, 'address', addr]) - self.session.set(base_path + [intf, 'peer', peer, 'address', '127.0.0.1']) - self.session.set(base_path + [intf, 'peer', peer, 'port', '1337']) + self.cli_set(base_path + [intf, 'peer', peer, 'address', '127.0.0.1']) + self.cli_set(base_path + [intf, 'peer', peer, 'port', '1337']) # Allow different prefixes to traverse the tunnel allowed_ips = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'] for ip in allowed_ips: - self.session.set(base_path + [intf, 'peer', peer, 'allowed-ips', ip]) + self.cli_set(base_path + [intf, 'peer', peer, 'allowed-ips', ip]) - self.session.set(base_path + [intf, 'peer', peer, 'preshared-key', psk]) - self.session.set(base_path + [intf, 'peer', peer, 'pubkey', pubkey]) - self.session.commit() + self.cli_set(base_path + [intf, 'peer', peer, 'preshared-key', psk]) + self.cli_set(base_path + [intf, 'peer', peer, 'pubkey', pubkey]) + self.cli_commit() self.assertTrue(os.path.isdir(f'/sys/class/net/{intf}')) @@ -71,26 +71,26 @@ class WireGuardInterfaceTest(unittest.TestCase): pubkey_1 = 'n1CUsmR0M2LUUsyicBd6blZICwUqqWWHbu4ifZ2/9gk=' pubkey_2 = 'ebFx/1G0ti8tvuZd94sEIosAZZIznX+dBAKG/8DFm0I=' - self.session.set(base_path + [interface, 'address', '172.16.0.1/24']) + self.cli_set(base_path + [interface, 'address', '172.16.0.1/24']) - self.session.set(base_path + [interface, 'peer', 'PEER01', 'pubkey', pubkey_1]) - self.session.set(base_path + [interface, 'peer', 'PEER01', 'port', port]) - self.session.set(base_path + [interface, 'peer', 'PEER01', 'allowed-ips', '10.205.212.10/32']) - self.session.set(base_path + [interface, 'peer', 'PEER01', 'address', '192.0.2.1']) + self.cli_set(base_path + [interface, 'peer', 'PEER01', 'pubkey', pubkey_1]) + self.cli_set(base_path + [interface, 'peer', 'PEER01', 'port', port]) + self.cli_set(base_path + [interface, 'peer', 'PEER01', 'allowed-ips', '10.205.212.10/32']) + self.cli_set(base_path + [interface, 'peer', 'PEER01', 'address', '192.0.2.1']) - self.session.set(base_path + [interface, 'peer', 'PEER02', 'pubkey', pubkey_2]) - self.session.set(base_path + [interface, 'peer', 'PEER02', 'port', port]) - self.session.set(base_path + [interface, 'peer', 'PEER02', 'allowed-ips', '10.205.212.11/32']) - self.session.set(base_path + [interface, 'peer', 'PEER02', 'address', '192.0.2.2']) + self.cli_set(base_path + [interface, 'peer', 'PEER02', 'pubkey', pubkey_2]) + self.cli_set(base_path + [interface, 'peer', 'PEER02', 'port', port]) + self.cli_set(base_path + [interface, 'peer', 'PEER02', 'allowed-ips', '10.205.212.11/32']) + self.cli_set(base_path + [interface, 'peer', 'PEER02', 'address', '192.0.2.2']) # Commit peers - self.session.commit() + self.cli_commit() self.assertTrue(os.path.isdir(f'/sys/class/net/{interface}')) # Delete second peer - self.session.delete(base_path + [interface, 'peer', 'PEER01']) - self.session.commit() + self.cli_delete(base_path + [interface, 'peer', 'PEER01']) + self.cli_commit() if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_interfaces_wireless.py b/smoketest/scripts/cli/test_interfaces_wireless.py index ffaa7d523..4f539a23c 100755 --- a/smoketest/scripts/cli/test_interfaces_wireless.py +++ b/smoketest/scripts/cli/test_interfaces_wireless.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 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 @@ -31,10 +31,12 @@ def get_config_value(interface, key): tmp = re.findall(f'{key}=+(.*)', tmp) return tmp[0] -class WirelessInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._base_path = ['interfaces', 'wireless'] - self._options = { +class WirelessInterfaceTest(BasicInterfaceTest.TestCase): + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._base_path = ['interfaces', 'wireless'] + cls._options = { 'wlan0': ['physical-device phy0', 'ssid VyOS-WIFI-0', 'type station', 'address 192.0.2.1/30'], 'wlan1': ['physical-device phy0', 'ssid VyOS-WIFI-1', 'country-code se', @@ -44,8 +46,9 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest): 'wlan11': ['physical-device phy1', 'ssid VyOS-WIFI-3', 'country-code se', 'type access-point', 'address 192.0.2.13/30', 'channel 0'], } - self._interfaces = list(self._options) - super().setUp() + cls._interfaces = list(cls._options) + # call base-classes classmethod + super(cls, cls).setUpClass() def test_wireless_add_single_ip_address(self): # derived method to check if member interfaces are enslaved properly @@ -66,12 +69,12 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest): interface = 'wlan0' ssid = 'ssid' - self.session.set(self._base_path + [interface, 'ssid', ssid]) - self.session.set(self._base_path + [interface, 'country-code', 'se']) - self.session.set(self._base_path + [interface, 'type', 'access-point']) + self.cli_set(self._base_path + [interface, 'ssid', ssid]) + self.cli_set(self._base_path + [interface, 'country-code', 'se']) + self.cli_set(self._base_path + [interface, 'type', 'access-point']) # auto-powersave is special - self.session.set(self._base_path + [interface, 'capabilities', 'ht', 'auto-powersave']) + self.cli_set(self._base_path + [interface, 'capabilities', 'ht', 'auto-powersave']) ht_opt = { # VyOS CLI option hostapd - ht_capab setting @@ -87,7 +90,7 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest): 'smps static' : '[SMPS-STATIC]', } for key in ht_opt: - self.session.set(self._base_path + [interface, 'capabilities', 'ht'] + key.split()) + self.cli_set(self._base_path + [interface, 'capabilities', 'ht'] + key.split()) vht_opt = { # VyOS CLI option hostapd - ht_capab setting @@ -104,9 +107,9 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest): 'short-gi 160' : '[SHORT-GI-160]', } for key in vht_opt: - self.session.set(self._base_path + [interface, 'capabilities', 'vht'] + key.split()) + self.cli_set(self._base_path + [interface, 'capabilities', 'vht'] + key.split()) - self.session.commit() + self.cli_commit() # # Validate Config @@ -147,29 +150,29 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest): mode = 'n' country = 'de' - self.session.set(self._base_path + [interface, 'physical-device', phy]) - self.session.set(self._base_path + [interface, 'type', 'access-point']) - self.session.set(self._base_path + [interface, 'mode', mode]) + self.cli_set(self._base_path + [interface, 'physical-device', phy]) + self.cli_set(self._base_path + [interface, 'type', 'access-point']) + self.cli_set(self._base_path + [interface, 'mode', mode]) # SSID must be set with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'ssid', ssid]) + self.cli_commit() + self.cli_set(self._base_path + [interface, 'ssid', ssid]) # Channel must be set with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'channel', channel]) + self.cli_commit() + self.cli_set(self._base_path + [interface, 'channel', channel]) # Country-Code must be set with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'country-code', country]) + self.cli_commit() + self.cli_set(self._base_path + [interface, 'country-code', country]) - self.session.set(self._base_path + [interface, 'security', 'wpa', 'mode', 'wpa2']) - self.session.set(self._base_path + [interface, 'security', 'wpa', 'passphrase', wpa_key]) + self.cli_set(self._base_path + [interface, 'security', 'wpa', 'mode', 'wpa2']) + self.cli_set(self._base_path + [interface, 'security', 'wpa', 'passphrase', wpa_key]) - self.session.commit() + self.cli_commit() # # Validate Config @@ -210,13 +213,13 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest): # We need a bridge where we can hook our access-point interface to bridge_path = ['interfaces', 'bridge', bridge] - self.session.set(bridge_path + ['member', 'interface', interface]) + self.cli_set(bridge_path + ['member', 'interface', interface]) - self.session.set(self._base_path + [interface, 'ssid', ssid]) - self.session.set(self._base_path + [interface, 'country-code', 'se']) - self.session.set(self._base_path + [interface, 'type', 'access-point']) + self.cli_set(self._base_path + [interface, 'ssid', ssid]) + self.cli_set(self._base_path + [interface, 'country-code', 'se']) + self.cli_set(self._base_path + [interface, 'type', 'access-point']) - self.session.commit() + self.cli_commit() # Check for running process self.assertTrue(process_named_running('hostapd')) @@ -227,9 +230,9 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest): self.assertIn(interface, bridge_members) - self.session.delete(bridge_path) - self.session.delete(self._base_path) - self.session.commit() + self.cli_delete(bridge_path) + self.cli_delete(self._base_path) + self.cli_commit() if __name__ == '__main__': check_kmod('mac80211_hwsim') diff --git a/smoketest/scripts/cli/test_interfaces_wirelessmodem.py b/smoketest/scripts/cli/test_interfaces_wirelessmodem.py index 45cd069f4..c36835ea7 100755 --- a/smoketest/scripts/cli/test_interfaces_wirelessmodem.py +++ b/smoketest/scripts/cli/test_interfaces_wirelessmodem.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 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 @@ -18,7 +18,10 @@ import os import unittest from psutil import process_iter -from vyos.configsession import ConfigSession, ConfigSessionError +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError config_file = '/etc/ppp/peers/{}' base_path = ['interfaces', 'wirelessmodem'] @@ -30,33 +33,31 @@ def get_config_value(interface, key): return list(line.split()) return [] -class WWANInterfaceTest(unittest.TestCase): +class WWANInterfaceTest(VyOSUnitTestSHIM.TestCase): def setUp(self): - self.session = ConfigSession(os.getpid()) self._interfaces = ['wlm0', 'wlm1'] def tearDown(self): - self.session.delete(base_path) - self.session.commit() - del self.session + self.cli_delete(base_path) + self.cli_commit() - def test_wlm_1(self): + def test_wwan(self): for interface in self._interfaces: - self.session.set(base_path + [interface, 'no-peer-dns']) - self.session.set(base_path + [interface, 'connect-on-demand']) + self.cli_set(base_path + [interface, 'no-peer-dns']) + self.cli_set(base_path + [interface, 'connect-on-demand']) # check validate() - APN must be configure with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(base_path + [interface, 'apn', 'vyos.net']) + self.cli_commit() + self.cli_set(base_path + [interface, 'apn', 'vyos.net']) # check validate() - device must be configure with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(base_path + [interface, 'device', 'ttyS0']) + self.cli_commit() + self.cli_set(base_path + [interface, 'device', 'ttyS0']) # commit changes - self.session.commit() + self.cli_commit() # verify configuration file(s) for interface in self._interfaces: diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py index 7ca82f86f..0706f234e 100755 --- a/smoketest/scripts/cli/test_nat.py +++ b/smoketest/scripts/cli/test_nat.py @@ -19,6 +19,7 @@ import jmespath import json import unittest +from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError from vyos.util import cmd @@ -28,16 +29,15 @@ base_path = ['nat'] src_path = base_path + ['source'] dst_path = base_path + ['destination'] -class TestNAT(unittest.TestCase): +class TestNAT(VyOSUnitTestSHIM.TestCase): def setUp(self): # ensure we can also run this test on a live system - so lets clean # out the current configuration :) - self.session = ConfigSession(os.getpid()) - self.session.delete(base_path) + self.cli_delete(base_path) def tearDown(self): - self.session.delete(base_path) - self.session.commit() + self.cli_delete(base_path) + self.cli_commit() def test_snat(self): rules = ['100', '110', '120', '130', '200', '210', '220', '230'] @@ -48,15 +48,15 @@ class TestNAT(unittest.TestCase): # depending of rule order we check either for source address for NAT # or configured destination address for NAT if int(rule) < 200: - self.session.set(src_path + ['rule', rule, 'source', 'address', network]) - self.session.set(src_path + ['rule', rule, 'outbound-interface', outbound_iface_100]) - self.session.set(src_path + ['rule', rule, 'translation', 'address', 'masquerade']) + self.cli_set(src_path + ['rule', rule, 'source', 'address', network]) + self.cli_set(src_path + ['rule', rule, 'outbound-interface', outbound_iface_100]) + self.cli_set(src_path + ['rule', rule, 'translation', 'address', 'masquerade']) else: - self.session.set(src_path + ['rule', rule, 'destination', 'address', network]) - self.session.set(src_path + ['rule', rule, 'outbound-interface', outbound_iface_200]) - self.session.set(src_path + ['rule', rule, 'exclude']) + self.cli_set(src_path + ['rule', rule, 'destination', 'address', network]) + self.cli_set(src_path + ['rule', rule, 'outbound-interface', outbound_iface_200]) + self.cli_set(src_path + ['rule', rule, 'exclude']) - self.session.commit() + self.cli_commit() tmp = cmd('sudo nft -j list table nat') data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp)) @@ -98,17 +98,17 @@ class TestNAT(unittest.TestCase): for rule in rules: port = f'10{rule}' - self.session.set(dst_path + ['rule', rule, 'source', 'port', port]) - self.session.set(dst_path + ['rule', rule, 'translation', 'address', '192.0.2.1']) - self.session.set(dst_path + ['rule', rule, 'translation', 'port', port]) + self.cli_set(dst_path + ['rule', rule, 'source', 'port', port]) + self.cli_set(dst_path + ['rule', rule, 'translation', 'address', '192.0.2.1']) + self.cli_set(dst_path + ['rule', rule, 'translation', 'port', port]) if int(rule) < 200: - self.session.set(dst_path + ['rule', rule, 'protocol', inbound_proto_100]) - self.session.set(dst_path + ['rule', rule, 'inbound-interface', inbound_iface_100]) + self.cli_set(dst_path + ['rule', rule, 'protocol', inbound_proto_100]) + self.cli_set(dst_path + ['rule', rule, 'inbound-interface', inbound_iface_100]) else: - self.session.set(dst_path + ['rule', rule, 'protocol', inbound_proto_200]) - self.session.set(dst_path + ['rule', rule, 'inbound-interface', inbound_iface_200]) + self.cli_set(dst_path + ['rule', rule, 'protocol', inbound_proto_200]) + self.cli_set(dst_path + ['rule', rule, 'inbound-interface', inbound_iface_200]) - self.session.commit() + self.cli_commit() tmp = cmd('sudo nft -j list table nat') data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp)) @@ -138,23 +138,45 @@ class TestNAT(unittest.TestCase): else: self.assertEqual(iface, inbound_iface_200) - def test_snat_required_translation_address(self): # T2813: Ensure translation address is specified rule = '5' - self.session.set(src_path + ['rule', rule, 'source', 'address', '192.0.2.0/24']) + self.cli_set(src_path + ['rule', rule, 'source', 'address', '192.0.2.0/24']) # check validate() - outbound-interface must be defined with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(src_path + ['rule', rule, 'outbound-interface', 'eth0']) + self.cli_commit() + self.cli_set(src_path + ['rule', rule, 'outbound-interface', 'eth0']) # check validate() - translation address not specified with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() + + self.cli_set(src_path + ['rule', rule, 'translation', 'address', 'masquerade']) + self.cli_commit() + + def test_dnat_negated_addresses(self): + # T3186: negated addresses are not accepted by nftables + rule = '1000' + self.cli_set(dst_path + ['rule', rule, 'destination', 'address', '!192.0.2.1']) + self.cli_set(dst_path + ['rule', rule, 'destination', 'port', '53']) + self.cli_set(dst_path + ['rule', rule, 'inbound-interface', 'eth0']) + self.cli_set(dst_path + ['rule', rule, 'protocol', 'tcp_udp']) + self.cli_set(dst_path + ['rule', rule, 'source', 'address', '!192.0.2.1']) + self.cli_set(dst_path + ['rule', rule, 'translation', 'address', '192.0.2.1']) + self.cli_set(dst_path + ['rule', rule, 'translation', 'port', '53']) + self.cli_commit() + + def test_nat_no_rules(self): + # T3206: deleting all rules but keep the direction 'destination' or + # 'source' resulteds in KeyError: 'rule'. + # + # Test that both 'nat destination' and 'nat source' nodes can exist + # without any rule + self.cli_set(src_path) + self.cli_set(dst_path) + self.cli_commit() - self.session.set(src_path + ['rule', rule, 'translation', 'address', 'masquerade']) - self.session.commit() if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_nat66.py b/smoketest/scripts/cli/test_nat66.py new file mode 100755 index 000000000..dca92c97d --- /dev/null +++ b/smoketest/scripts/cli/test_nat66.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import jmespath +import json +import unittest + +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.util import cmd +from vyos.util import dict_search + +base_path = ['nat66'] +src_path = base_path + ['source'] +dst_path = base_path + ['destination'] + +class TestNAT66(VyOSUnitTestSHIM.TestCase): + def setUp(self): + # ensure we can also run this test on a live system - so lets clean + # out the current configuration :) + self.cli_delete(base_path) + + def tearDown(self): + self.cli_delete(base_path) + self.cli_commit() + + def test_source_nat66(self): + source_prefix = 'fc00::/64' + translation_prefix = 'fc01::/64' + self.cli_set(src_path + ['rule', '1', 'outbound-interface', 'eth1']) + self.cli_set(src_path + ['rule', '1', 'source', 'prefix', source_prefix]) + self.cli_set(src_path + ['rule', '1', 'translation', 'address', translation_prefix]) + + # check validate() - outbound-interface must be defined + self.cli_commit() + + tmp = cmd('sudo nft -j list table ip6 nat') + data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp)) + + for idx in range(0, len(data_json)): + data = data_json[idx] + + self.assertEqual(data['chain'], 'POSTROUTING') + self.assertEqual(data['family'], 'ip6') + self.assertEqual(data['table'], 'nat') + + iface = dict_search('match.right', data['expr'][0]) + address = dict_search('match.right.prefix.addr', data['expr'][2]) + mask = dict_search('match.right.prefix.len', data['expr'][2]) + translation_address = dict_search('snat.addr.prefix.addr', data['expr'][3]) + translation_mask = dict_search('snat.addr.prefix.len', data['expr'][3]) + + self.assertEqual(iface, 'eth1') + # check for translation address + self.assertEqual(f'{translation_address}/{translation_mask}', translation_prefix) + self.assertEqual(f'{address}/{mask}', source_prefix) + + def test_source_nat66_address(self): + source_prefix = 'fc00::/64' + translation_address = 'fc00::1' + self.cli_set(src_path + ['rule', '1', 'outbound-interface', 'eth1']) + self.cli_set(src_path + ['rule', '1', 'source', 'prefix', source_prefix]) + self.cli_set(src_path + ['rule', '1', 'translation', 'address', translation_address]) + + # check validate() - outbound-interface must be defined + self.cli_commit() + + tmp = cmd('sudo nft -j list table ip6 nat') + data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp)) + + for idx in range(0, len(data_json)): + data = data_json[idx] + + self.assertEqual(data['chain'], 'POSTROUTING') + self.assertEqual(data['family'], 'ip6') + self.assertEqual(data['table'], 'nat') + + iface = dict_search('match.right', data['expr'][0]) + address = dict_search('match.right.prefix.addr', data['expr'][2]) + mask = dict_search('match.right.prefix.len', data['expr'][2]) + snat_address = dict_search('snat.addr', data['expr'][3]) + + self.assertEqual(iface, 'eth1') + # check for translation address + self.assertEqual(snat_address, translation_address) + self.assertEqual(f'{address}/{mask}', source_prefix) + + def test_destination_nat66(self): + destination_address = 'fc00::1' + translation_address = 'fc01::1' + self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'eth1']) + self.cli_set(dst_path + ['rule', '1', 'destination', 'address', destination_address]) + self.cli_set(dst_path + ['rule', '1', 'translation', 'address', translation_address]) + + # check validate() - outbound-interface must be defined + self.cli_commit() + + tmp = cmd('sudo nft -j list table ip6 nat') + data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp)) + + for idx in range(0, len(data_json)): + data = data_json[idx] + + self.assertEqual(data['chain'], 'PREROUTING') + self.assertEqual(data['family'], 'ip6') + self.assertEqual(data['table'], 'nat') + + iface = dict_search('match.right', data['expr'][0]) + dnat_addr = dict_search('dnat.addr', data['expr'][3]) + + self.assertEqual(dnat_addr, translation_address) + self.assertEqual(iface, 'eth1') + + def test_destination_nat66_prefix(self): + destination_prefix = 'fc00::/64' + translation_prefix = 'fc01::/64' + self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'eth1']) + self.cli_set(dst_path + ['rule', '1', 'destination', 'address', destination_prefix]) + self.cli_set(dst_path + ['rule', '1', 'translation', 'address', translation_prefix]) + + # check validate() - outbound-interface must be defined + self.cli_commit() + + tmp = cmd('sudo nft -j list table ip6 nat') + data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp)) + + for idx in range(0, len(data_json)): + data = data_json[idx] + + self.assertEqual(data['chain'], 'PREROUTING') + self.assertEqual(data['family'], 'ip6') + self.assertEqual(data['table'], 'nat') + + iface = dict_search('match.right', data['expr'][0]) + translation_address = dict_search('dnat.addr.prefix.addr', data['expr'][3]) + translation_mask = dict_search('dnat.addr.prefix.len', data['expr'][3]) + + self.assertEqual(f'{translation_address}/{translation_mask}', translation_prefix) + self.assertEqual(iface, 'eth1') + + def test_source_nat66_required_translation_prefix(self): + # T2813: Ensure translation address is specified + rule = '5' + source_prefix = 'fc00::/64' + self.cli_set(src_path + ['rule', rule, 'source', 'prefix', source_prefix]) + + # check validate() - outbound-interface must be defined + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(src_path + ['rule', rule, 'outbound-interface', 'eth0']) + + # check validate() - translation address not specified + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + self.cli_set(src_path + ['rule', rule, 'translation', 'address', 'masquerade']) + self.cli_commit() + + def test_nat66_no_rules(self): + # T3206: deleting all rules but keep the direction 'destination' or + # 'source' resulteds in KeyError: 'rule'. + # + # Test that both 'nat destination' and 'nat source' nodes can exist + # without any rule + self.cli_set(src_path) + self.cli_set(dst_path) + self.cli_commit() + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py new file mode 100755 index 000000000..57557af68 --- /dev/null +++ b/smoketest/scripts/cli/test_policy.py @@ -0,0 +1,702 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 unittest + +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.util import cmd + +base_path = ['policy'] + +class TestPolicy(VyOSUnitTestSHIM.TestCase): + def tearDown(self): + self.cli_delete(base_path) + self.cli_commit() + + def test_access_list(self): + acls = { + '50' : { + 'rule' : { + '5' : { + 'action' : 'permit', + 'source' : { 'any' : '' }, + }, + '10' : { + 'action' : 'deny', + 'source' : { 'host' : '1.2.3.4' }, + }, + }, + }, + '150' : { + 'rule' : { + '10' : { + 'action' : 'permit', + 'source' : { 'any' : '' }, + 'destination' : { 'host' : '2.2.2.2' }, + }, + '10' : { + 'action' : 'deny', + 'source' : { 'any' : '' }, + 'destination' : { 'any' : '' }, + }, + }, + }, + '2000' : { + 'rule' : { + '10' : { + 'action' : 'permit', + 'destination' : { 'any' : '' }, + 'source' : { 'network' : '10.0.0.0', 'inverse-mask' : '0.255.255.255' }, + }, + '20' : { + 'action' : 'permit', + 'destination' : { 'any' : '' }, + 'source' : { 'network' : '172.16.0.0', 'inverse-mask' : '0.15.255.255' }, + }, + '30' : { + 'action' : 'permit', + 'destination' : { 'any' : '' }, + 'source' : { 'network' : '192.168.0.0', 'inverse-mask' : '0.0.255.255' }, + }, + '50' : { + 'action' : 'permit', + 'destination' : { 'network' : '172.16.0.0', 'inverse-mask' : '0.15.255.255' }, + 'source' : { 'network' : '10.0.0.0', 'inverse-mask' : '0.255.255.255' }, + }, + '60' : { + 'action' : 'deny', + 'destination' : { 'network' : '192.168.0.0', 'inverse-mask' : '0.0.255.255' }, + 'source' : { 'network' : '172.16.0.0', 'inverse-mask' : '0.15.255.255' }, + }, + '70' : { + 'action' : 'deny', + 'destination' : { 'any' : '' }, + 'source' : { 'any' : '' }, + }, + }, + }, + } + + for acl, acl_config in acls.items(): + path = base_path + ['access-list', acl] + self.cli_set(path + ['description', f'VyOS-ACL-{acl}']) + if 'rule' not in acl_config: + continue + + for rule, rule_config in acl_config['rule'].items(): + self.cli_set(path + ['rule', rule, 'action', rule_config['action']]) + for direction in ['source', 'destination']: + if direction in rule_config: + if 'any' in rule_config[direction]: + self.cli_set(path + ['rule', rule, direction, 'any']) + if 'host' in rule_config[direction]: + self.cli_set(path + ['rule', rule, direction, 'host', rule_config[direction]['host']]) + if 'network' in rule_config[direction]: + self.cli_set(path + ['rule', rule, direction, 'network', rule_config[direction]['network']]) + self.cli_set(path + ['rule', rule, direction, 'inverse-mask', rule_config[direction]['inverse-mask']]) + + self.cli_commit() + + config = self.getFRRconfig('access-list', end='') + for acl, acl_config in acls.items(): + seq = '5' + for rule, rule_config in acl_config['rule'].items(): + tmp = f'access-list {acl} seq {seq}' + if rule_config['action'] == 'permit': + tmp += ' permit' + else: + tmp += ' deny' + + if {'source', 'destination'} <= set(rule_config): + tmp += ' ip' + + for direction in ['source', 'destination']: + if direction in rule_config: + if 'any' in rule_config[direction]: + tmp += ' any' + if 'host' in rule_config[direction]: + tmp += ' ' + rule_config[direction]['host'] + if 'network' in rule_config[direction]: + tmp += ' ' + rule_config[direction]['network'] + ' ' + rule_config[direction]['inverse-mask'] + + self.assertIn(tmp, config) + seq = int(seq) + 5 + + def test_access_list6(self): + acls = { + '50' : { + 'rule' : { + '5' : { + 'action' : 'permit', + 'source' : { 'any' : '' }, + }, + '10' : { + 'action' : 'deny', + 'source' : { 'network' : '2001:db8:10::/48', 'exact-match' : '' }, + }, + '10' : { + 'action' : 'deny', + 'source' : { 'network' : '2001:db8:20::/48' }, + }, + }, + }, + '100' : { + 'rule' : { + '5' : { + 'action' : 'deny', + 'source' : { 'network' : '2001:db8:10::/64', 'exact-match' : '' }, + }, + '10' : { + 'action' : 'deny', + 'source' : { 'network' : '2001:db8:20::/64', }, + }, + '15' : { + 'action' : 'deny', + 'source' : { 'network' : '2001:db8:30::/64', 'exact-match' : '' }, + }, + '20' : { + 'action' : 'deny', + 'source' : { 'network' : '2001:db8:40::/64', 'exact-match' : '' }, + }, + '100' : { + 'action' : 'deny', + 'source' : { 'any' : '' }, + }, + }, + }, + } + + for acl, acl_config in acls.items(): + path = base_path + ['access-list6', acl] + self.cli_set(path + ['description', f'VyOS-ACL-{acl}']) + if 'rule' not in acl_config: + continue + + for rule, rule_config in acl_config['rule'].items(): + self.cli_set(path + ['rule', rule, 'action', rule_config['action']]) + for direction in ['source', 'destination']: + if direction in rule_config: + if 'any' in rule_config[direction]: + self.cli_set(path + ['rule', rule, direction, 'any']) + if 'network' in rule_config[direction]: + self.cli_set(path + ['rule', rule, direction, 'network', rule_config[direction]['network']]) + if 'exact-match' in rule_config[direction]: + self.cli_set(path + ['rule', rule, direction, 'exact-match']) + + self.cli_commit() + + config = self.getFRRconfig('ipv6 access-list', end='') + for acl, acl_config in acls.items(): + seq = '5' + for rule, rule_config in acl_config['rule'].items(): + tmp = f'ipv6 access-list {acl} seq {seq}' + if rule_config['action'] == 'permit': + tmp += ' permit' + else: + tmp += ' deny' + + if {'source', 'destination'} <= set(rule_config): + tmp += ' ip' + + for direction in ['source', 'destination']: + if direction in rule_config: + if 'any' in rule_config[direction]: + tmp += ' any' + if 'network' in rule_config[direction]: + tmp += ' ' + rule_config[direction]['network'] + if 'exact-match' in rule_config[direction]: + tmp += ' exact-match' + + self.assertIn(tmp, config) + seq = int(seq) + 5 + + + def test_as_path_list(self): + test_data = { + 'VyOS' : { + 'rule' : { + '10' : { + 'action' : 'permit', + 'regex' : '^44501 64502$', + }, + '20' : { + 'action' : 'permit', + 'regex' : '44501|44502|44503', + }, + '30' : { + 'action' : 'permit', + 'regex' : '^44501_([0-9]+_)+', + }, + }, + }, + 'Customers' : { + 'rule' : { + '10' : { + 'action' : 'permit', + 'regex' : '_10_', + }, + '20' : { + 'action' : 'permit', + 'regex' : '_20_', + }, + '30' : { + 'action' : 'permit', + 'regex' : '_30_', + }, + '30' : { + 'action' : 'deny', + 'regex' : '_40_', + }, + }, + }, + 'bogons' : { + 'rule' : { + '10' : { + 'action' : 'permit', + 'regex' : '_0_', + }, + '20' : { + 'action' : 'permit', + 'regex' : '_23456_', + }, + '30' : { + 'action' : 'permit', + 'regex' : '_6449[6-9]_|_65[0-4][0-9][0-9]_|_655[0-4][0-9]_|_6555[0-1]_', + }, + '30' : { + 'action' : 'permit', + 'regex' : '_6555[2-9]_|_655[6-9][0-9]_|_65[6-9][0-9][0-9]_|_6[6-9][0-9][0-9][0-]_|_[7-9][0-9][0-9][0-9][0-9]_|_1[0-2][0-9][0-9][0-9][0-9]_|_130[0-9][0-9][0-9]_|_1310[0-6][0-9]_|_13107[01]_', + }, + }, + }, + } + + for as_path, as_path_config in test_data.items(): + path = base_path + ['as-path-list', as_path] + self.cli_set(path + ['description', f'VyOS-ASPATH-{as_path}']) + if 'rule' not in as_path_config: + continue + + for rule, rule_config in as_path_config['rule'].items(): + if 'action' in rule_config: + self.cli_set(path + ['rule', rule, 'action', rule_config['action']]) + if 'regex' in rule_config: + self.cli_set(path + ['rule', rule, 'regex', rule_config['regex']]) + + self.cli_commit() + + config = self.getFRRconfig('bgp as-path access-list', end='') + for as_path, as_path_config in test_data.items(): + if 'rule' not in as_path_config: + continue + + for rule, rule_config in as_path_config['rule'].items(): + tmp = f'bgp as-path access-list {as_path}' + if rule_config['action'] == 'permit': + tmp += ' permit' + else: + tmp += ' deny' + + tmp += ' ' + rule_config['regex'] + + self.assertIn(tmp, config) + + def test_community_list(self): + test_data = { + '100' : { + 'rule' : { + '4' : { + 'action' : 'permit', + 'regex' : '.*', + }, + }, + }, + '200' : { + 'rule' : { + '1' : { + 'action' : 'deny', + 'regex' : '^1:201$', + }, + '2' : { + 'action' : 'deny', + 'regex' : '1:101$', + }, + '3' : { + 'action' : 'deny', + 'regex' : '^1:100$', + }, + }, + }, + } + + for comm_list, comm_list_config in test_data.items(): + path = base_path + ['community-list', comm_list] + self.cli_set(path + ['description', f'VyOS-COMM-{comm_list}']) + if 'rule' not in comm_list_config: + continue + + for rule, rule_config in comm_list_config['rule'].items(): + if 'action' in rule_config: + self.cli_set(path + ['rule', rule, 'action', rule_config['action']]) + if 'regex' in rule_config: + self.cli_set(path + ['rule', rule, 'regex', rule_config['regex']]) + + self.cli_commit() + + config = self.getFRRconfig('bgp community-list', end='') + for comm_list, comm_list_config in test_data.items(): + if 'rule' not in comm_list_config: + continue + + seq = '5' + for rule, rule_config in comm_list_config['rule'].items(): + tmp = f'bgp community-list {comm_list} seq {seq}' + if rule_config['action'] == 'permit': + tmp += ' permit' + else: + tmp += ' deny' + + tmp += ' ' + rule_config['regex'] + + self.assertIn(tmp, config) + seq = int(seq) + 5 + + def test_extended_community_list(self): + test_data = { + 'foo' : { + 'rule' : { + '4' : { + 'action' : 'permit', + 'regex' : '.*', + }, + }, + }, + '200' : { + 'rule' : { + '1' : { + 'action' : 'deny', + 'regex' : '^1:201$', + }, + '2' : { + 'action' : 'deny', + 'regex' : '1:101$', + }, + '3' : { + 'action' : 'deny', + 'regex' : '^1:100$', + }, + }, + }, + } + + for comm_list, comm_list_config in test_data.items(): + path = base_path + ['extcommunity-list', comm_list] + self.cli_set(path + ['description', f'VyOS-EXTCOMM-{comm_list}']) + if 'rule' not in comm_list_config: + continue + + for rule, rule_config in comm_list_config['rule'].items(): + if 'action' in rule_config: + self.cli_set(path + ['rule', rule, 'action', rule_config['action']]) + if 'regex' in rule_config: + self.cli_set(path + ['rule', rule, 'regex', rule_config['regex']]) + + self.cli_commit() + + config = self.getFRRconfig('bgp extcommunity-list', end='') + for comm_list, comm_list_config in test_data.items(): + if 'rule' not in comm_list_config: + continue + + seq = '5' + for rule, rule_config in comm_list_config['rule'].items(): + # if the community is not a number but a name, the expanded + # keyword is used + expanded = '' + if not comm_list.isnumeric(): + expanded = ' expanded' + tmp = f'bgp extcommunity-list{expanded} {comm_list} seq {seq}' + + if rule_config['action'] == 'permit': + tmp += ' permit' + else: + tmp += ' deny' + + tmp += ' ' + rule_config['regex'] + + self.assertIn(tmp, config) + seq = int(seq) + 5 + + + def test_large_community_list(self): + test_data = { + 'foo' : { + 'rule' : { + '10' : { + 'action' : 'permit', + 'regex' : '667:123:100', + }, + }, + }, + 'bar' : { + 'rule' : { + '10' : { + 'action' : 'permit', + 'regex' : '65000:120:10', + }, + '20' : { + 'action' : 'permit', + 'regex' : '65000:120:20', + }, + '30' : { + 'action' : 'permit', + 'regex' : '65000:120:30', + }, + }, + }, + } + + for comm_list, comm_list_config in test_data.items(): + path = base_path + ['large-community-list', comm_list] + self.cli_set(path + ['description', f'VyOS-LARGECOMM-{comm_list}']) + if 'rule' not in comm_list_config: + continue + + for rule, rule_config in comm_list_config['rule'].items(): + if 'action' in rule_config: + self.cli_set(path + ['rule', rule, 'action', rule_config['action']]) + if 'regex' in rule_config: + self.cli_set(path + ['rule', rule, 'regex', rule_config['regex']]) + + self.cli_commit() + + config = self.getFRRconfig('bgp large-community-list', end='') + for comm_list, comm_list_config in test_data.items(): + if 'rule' not in comm_list_config: + continue + + seq = '5' + for rule, rule_config in comm_list_config['rule'].items(): + tmp = f'bgp large-community-list expanded {comm_list} seq {seq}' + + if rule_config['action'] == 'permit': + tmp += ' permit' + else: + tmp += ' deny' + + tmp += ' ' + rule_config['regex'] + + self.assertIn(tmp, config) + seq = int(seq) + 5 + + + def test_prefix_list(self): + test_data = { + 'foo' : { + 'rule' : { + '10' : { + 'action' : 'permit', + 'prefix' : '10.0.0.0/8', + 'ge' : '16', + 'le' : '24', + }, + '20' : { + 'action' : 'deny', + 'prefix' : '172.16.0.0/12', + 'ge' : '16', + }, + '30' : { + 'action' : 'permit', + 'prefix' : '192.168.0.0/16', + }, + }, + }, + 'bar' : { + 'rule' : { + '10' : { + 'action' : 'permit', + 'prefix' : '10.0.10.0/24', + 'ge' : '25', + 'le' : '26', + }, + '20' : { + 'action' : 'deny', + 'prefix' : '10.0.20.0/24', + 'le' : '25', + }, + '25' : { + 'action' : 'permit', + 'prefix' : '10.0.25.0/24', + }, + }, + }, + } + + for prefix_list, prefix_list_config in test_data.items(): + path = base_path + ['prefix-list', prefix_list] + self.cli_set(path + ['description', f'VyOS-PFX-LIST-{prefix_list}']) + if 'rule' not in prefix_list_config: + continue + + for rule, rule_config in prefix_list_config['rule'].items(): + if 'action' in rule_config: + self.cli_set(path + ['rule', rule, 'action', rule_config['action']]) + if 'prefix' in rule_config: + self.cli_set(path + ['rule', rule, 'prefix', rule_config['prefix']]) + if 'ge' in rule_config: + self.cli_set(path + ['rule', rule, 'ge', rule_config['ge']]) + if 'le' in rule_config: + self.cli_set(path + ['rule', rule, 'le', rule_config['le']]) + + self.cli_commit() + + config = self.getFRRconfig('ip prefix-list', end='') + for prefix_list, prefix_list_config in test_data.items(): + if 'rule' not in prefix_list_config: + continue + + for rule, rule_config in prefix_list_config['rule'].items(): + tmp = f'ip prefix-list {prefix_list} seq {rule}' + + if rule_config['action'] == 'permit': + tmp += ' permit' + else: + tmp += ' deny' + + tmp += ' ' + rule_config['prefix'] + + if 'ge' in rule_config: + tmp += ' ge ' + rule_config['ge'] + if 'le' in rule_config: + tmp += ' le ' + rule_config['le'] + + self.assertIn(tmp, config) + + + def test_prefix_list6(self): + test_data = { + 'foo' : { + 'rule' : { + '10' : { + 'action' : 'permit', + 'prefix' : '2001:db8::/32', + 'ge' : '40', + 'le' : '48', + }, + '20' : { + 'action' : 'deny', + 'prefix' : '2001:db8::/32', + 'ge' : '48', + }, + '30' : { + 'action' : 'permit', + 'prefix' : '2001:db8:1000::/64', + }, + }, + }, + 'bar' : { + 'rule' : { + '10' : { + 'action' : 'permit', + 'prefix' : '2001:db8:100::/40', + 'ge' : '48', + }, + '20' : { + 'action' : 'permit', + 'prefix' : '2001:db8:200::/40', + 'ge' : '48', + }, + '25' : { + 'action' : 'deny', + 'prefix' : '2001:db8:300::/40', + 'le' : '64', + }, + }, + }, + } + + for prefix_list, prefix_list_config in test_data.items(): + path = base_path + ['prefix-list6', prefix_list] + self.cli_set(path + ['description', f'VyOS-PFX-LIST-{prefix_list}']) + if 'rule' not in prefix_list_config: + continue + + for rule, rule_config in prefix_list_config['rule'].items(): + if 'action' in rule_config: + self.cli_set(path + ['rule', rule, 'action', rule_config['action']]) + if 'prefix' in rule_config: + self.cli_set(path + ['rule', rule, 'prefix', rule_config['prefix']]) + if 'ge' in rule_config: + self.cli_set(path + ['rule', rule, 'ge', rule_config['ge']]) + if 'le' in rule_config: + self.cli_set(path + ['rule', rule, 'le', rule_config['le']]) + + self.cli_commit() + + config = self.getFRRconfig('ipv6 prefix-list', end='') + for prefix_list, prefix_list_config in test_data.items(): + if 'rule' not in prefix_list_config: + continue + + for rule, rule_config in prefix_list_config['rule'].items(): + tmp = f'ipv6 prefix-list {prefix_list} seq {rule}' + + if rule_config['action'] == 'permit': + tmp += ' permit' + else: + tmp += ' deny' + + tmp += ' ' + rule_config['prefix'] + + if 'ge' in rule_config: + tmp += ' ge ' + rule_config['ge'] + if 'le' in rule_config: + tmp += ' le ' + rule_config['le'] + + self.assertIn(tmp, config) + + + # Test set table for some sources + def test_table_id(self): + path = base_path + ['local-route'] + + sources = ['203.0.113.1', '203.0.113.2'] + rule = '50' + table = '23' + for src in sources: + self.cli_set(path + ['rule', rule, 'set', 'table', table]) + self.cli_set(path + ['rule', rule, 'source', src]) + + self.cli_commit() + + # Check generated configuration + + # Expected values + original = """ + 50: from 203.0.113.1 lookup 23 + 50: from 203.0.113.2 lookup 23 + """ + tmp = cmd('ip rule show prio 50') + original = original.split() + tmp = tmp.split() + + self.assertEqual(tmp, original) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_policy_local-route.py b/smoketest/scripts/cli/test_policy_local-route.py deleted file mode 100755 index de1882a65..000000000 --- a/smoketest/scripts/cli/test_policy_local-route.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2020 VyOS maintainers and contributors -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 or later as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -import os -import unittest - -from vyos.configsession import ConfigSession -from vyos.configsession import ConfigSessionError -from vyos.util import cmd -from vyos.util import process_named_running - -class PolicyLocalRouteTest(unittest.TestCase): - def setUp(self): - self.session = ConfigSession(os.getpid()) - self._sources = ['203.0.113.1', '203.0.113.2'] - - def tearDown(self): - # Delete all policies - self.session.delete(['policy', 'local-route']) - self.session.commit() - del self.session - - # Test set table for some sources - def test_table_id(self): - base = ['policy', 'local-route'] - rule = '50' - table = '23' - for src in self._sources: - self.session.set(base + ['rule', rule, 'set', 'table', table]) - self.session.set(base + ['rule', rule, 'source', src]) - - self.session.commit() - - # Check generated configuration - - # Expected values - original = """ - 50: from 203.0.113.1 lookup 23 - 50: from 203.0.113.2 lookup 23 - """ - tmp = cmd('ip rule show prio 50') - original = original.split() - tmp = tmp.split() - - self.assertEqual(tmp, original) - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_bfd.py b/smoketest/scripts/cli/test_protocols_bfd.py new file mode 100755 index 000000000..a57f8d5f2 --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_bfd.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 unittest + +from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.util import process_named_running + +PROCESS_NAME = 'bfdd' +base_path = ['protocols', 'bfd'] + +dum_if = 'dum1001' +peers = { + '192.0.2.10' : { + 'intv_rx' : '500', + 'intv_tx' : '600', + 'multihop' : '', + 'source_addr': '192.0.2.254', + }, + '192.0.2.20' : { + 'echo_mode' : '', + 'intv_echo' : '100', + 'intv_mult' : '100', + 'intv_rx' : '222', + 'intv_tx' : '333', + 'shutdown' : '', + 'source_intf': dum_if, + }, + '2001:db8::a' : { + 'source_addr': '2001:db8::1', + 'source_intf': dum_if, + }, + '2001:db8::b' : { + 'source_addr': '2001:db8::1', + 'multihop' : '', + }, +} + +profiles = { + 'foo' : { + 'echo_mode' : '', + 'intv_echo' : '100', + 'intv_mult' : '101', + 'intv_rx' : '222', + 'intv_tx' : '333', + 'shutdown' : '', + }, + 'bar' : { + 'intv_mult' : '102', + 'intv_rx' : '444', + }, +} + +class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase): + def tearDown(self): + self.cli_delete(base_path) + self.cli_commit() + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + def test_bfd_peer(self): + for peer, peer_config in peers.items(): + if 'echo_mode' in peer_config: + self.cli_set(base_path + ['peer', peer, 'echo-mode']) + if 'intv_echo' in peer_config: + self.cli_set(base_path + ['peer', peer, 'interval', 'echo-interval', peer_config["intv_echo"]]) + if 'intv_mult' in peer_config: + self.cli_set(base_path + ['peer', peer, 'interval', 'multiplier', peer_config["intv_mult"]]) + if 'intv_rx' in peer_config: + self.cli_set(base_path + ['peer', peer, 'interval', 'receive', peer_config["intv_rx"]]) + if 'intv_tx' in peer_config: + self.cli_set(base_path + ['peer', peer, 'interval', 'transmit', peer_config["intv_tx"]]) + if 'multihop' in peer_config: + self.cli_set(base_path + ['peer', peer, 'multihop']) + if 'shutdown' in peer_config: + self.cli_set(base_path + ['peer', peer, 'shutdown']) + if 'source_addr' in peer_config: + self.cli_set(base_path + ['peer', peer, 'source', 'address', peer_config["source_addr"]]) + if 'source_intf' in peer_config: + self.cli_set(base_path + ['peer', peer, 'source', 'interface', peer_config["source_intf"]]) + + # commit changes + self.cli_commit() + + # Verify FRR bgpd configuration + frrconfig = self.getFRRconfig('bfd') + for peer, peer_config in peers.items(): + tmp = f'peer {peer}' + if 'multihop' in peer_config: + tmp += f' multihop' + if 'source_addr' in peer_config: + tmp += f' local-address {peer_config["source_addr"]}' + if 'source_intf' in peer_config: + tmp += f' interface {peer_config["source_intf"]}' + + self.assertIn(tmp, frrconfig) + peerconfig = self.getFRRconfig(f' peer {peer}', end='') + + if 'echo_mode' in peer_config: + self.assertIn(f'echo-mode', peerconfig) + if 'intv_echo' in peer_config: + self.assertIn(f'echo-interval {peer_config["intv_echo"]}', peerconfig) + if 'intv_mult' in peer_config: + self.assertIn(f'detect-multiplier {peer_config["intv_mult"]}', peerconfig) + if 'intv_rx' in peer_config: + self.assertIn(f'receive-interval {peer_config["intv_rx"]}', peerconfig) + if 'intv_tx' in peer_config: + self.assertIn(f'transmit-interval {peer_config["intv_tx"]}', peerconfig) + if 'shutdown' in peer_config: + self.assertIn(f'shutdown', peerconfig) + else: + self.assertNotIn(f'shutdown', peerconfig) + + def test_bfd_profile(self): + peer = '192.0.2.10' + + for profile, profile_config in profiles.items(): + if 'echo_mode' in profile_config: + self.cli_set(base_path + ['profile', profile, 'echo-mode']) + if 'intv_echo' in profile_config: + self.cli_set(base_path + ['profile', profile, 'interval', 'echo-interval', profile_config["intv_echo"]]) + if 'intv_mult' in profile_config: + self.cli_set(base_path + ['profile', profile, 'interval', 'multiplier', profile_config["intv_mult"]]) + if 'intv_rx' in profile_config: + self.cli_set(base_path + ['profile', profile, 'interval', 'receive', profile_config["intv_rx"]]) + if 'intv_tx' in profile_config: + self.cli_set(base_path + ['profile', profile, 'interval', 'transmit', profile_config["intv_tx"]]) + if 'shutdown' in profile_config: + self.cli_set(base_path + ['profile', profile, 'shutdown']) + + self.cli_set(base_path + ['peer', peer, 'profile', list(profiles)[0]]) + + # commit changes + self.cli_commit() + + # Verify FRR bgpd configuration + for profile, profile_config in profiles.items(): + config = self.getFRRconfig(f' profile {profile}', endsection='^ !') + if 'echo_mode' in profile_config: + self.assertIn(f'echo-mode', config) + if 'intv_echo' in profile_config: + self.assertIn(f'echo-interval {profile_config["intv_echo"]}', config) + if 'intv_mult' in profile_config: + self.assertIn(f'detect-multiplier {profile_config["intv_mult"]}', config) + if 'intv_rx' in profile_config: + self.assertIn(f'receive-interval {profile_config["intv_rx"]}', config) + if 'intv_tx' in profile_config: + self.assertIn(f'transmit-interval {profile_config["intv_tx"]}', config) + if 'shutdown' in profile_config: + self.assertIn(f'shutdown', config) + else: + self.assertNotIn(f'shutdown', config) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 1d93aeda4..4f39948c0 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -14,87 +14,147 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import unittest -from vyos.configsession import ConfigSession +from base_vyostest_shim import VyOSUnitTestSHIM + from vyos.configsession import ConfigSessionError -from vyos.util import cmd +from vyos.template import is_ipv6 from vyos.util import process_named_running PROCESS_NAME = 'bgpd' ASN = '64512' -base_path = ['protocols', 'bgp', ASN] +base_path = ['protocols', 'bgp'] + +route_map_in = 'foo-map-in' +route_map_out = 'foo-map-out' +prefix_list_in = 'pfx-foo-in' +prefix_list_out = 'pfx-foo-out' +prefix_list_in6 = 'pfx-foo-in6' +prefix_list_out6 = 'pfx-foo-out6' neighbor_config = { '192.0.2.1' : { - 'cap_dynamic' : '', - 'cap_ext_next': '', - 'remote_as' : '100', - 'adv_interv' : '400', - 'passive' : '', - 'password' : 'VyOS-Secure123', - 'shutdown' : '', - 'cap_over' : '', - 'ttl_security': '5', - 'local_as' : '300', + 'cap_dynamic' : '', + 'cap_ext_next' : '', + 'remote_as' : '100', + 'adv_interv' : '400', + 'passive' : '', + 'password' : 'VyOS-Secure123', + 'shutdown' : '', + 'cap_over' : '', + 'ttl_security' : '5', + 'local_as' : '300', + 'route_map_in' : route_map_in, + 'route_map_out': route_map_out, + 'no_send_comm_ext' : '', + 'addpath_all' : '', }, '192.0.2.2' : { - 'remote_as' : '200', - 'shutdown' : '', - 'no_cap_nego' : '', - 'port' : '667', - 'cap_strict' : '', + 'remote_as' : '200', + 'shutdown' : '', + 'no_cap_nego' : '', + 'port' : '667', + 'cap_strict' : '', + 'pfx_list_in' : prefix_list_in, + 'pfx_list_out' : prefix_list_out, + 'no_send_comm_std' : '', }, '192.0.2.3' : { -# XXX: not available in current Perl backend -# 'description' : 'foo bar baz', - 'remote_as' : '200', - 'passive' : '', - 'multi_hop' : '5', - 'update_src' : 'lo', + 'description' : 'foo bar baz', + 'remote_as' : '200', + 'passive' : '', + 'multi_hop' : '5', + 'update_src' : 'lo', + }, + '2001:db8::1' : { + 'cap_dynamic' : '', + 'cap_ext_next' : '', + 'remote_as' : '123', + 'adv_interv' : '400', + 'passive' : '', + 'password' : 'VyOS-Secure123', + 'shutdown' : '', + 'cap_over' : '', + 'ttl_security' : '5', + 'local_as' : '300', + 'route_map_in' : route_map_in, + 'route_map_out': route_map_out, + 'no_send_comm_std' : '', + 'addpath_per_as' : '', + }, + '2001:db8::2' : { + 'remote_as' : '456', + 'shutdown' : '', + 'no_cap_nego' : '', + 'port' : '667', + 'cap_strict' : '', + 'pfx_list_in' : prefix_list_in6, + 'pfx_list_out' : prefix_list_out6, + 'no_send_comm_ext' : '', }, } peer_group_config = { 'foo' : { - 'remote_as' : '100', - 'passive' : '', - 'password' : 'VyOS-Secure123', - 'shutdown' : '', - 'cap_over' : '', + 'remote_as' : '100', + 'passive' : '', + 'password' : 'VyOS-Secure123', + 'shutdown' : '', + 'cap_over' : '', # XXX: not available in current Perl backend # 'ttl_security': '5', }, 'bar' : { -# XXX: not available in current Perl backend -# 'description' : 'foo peer bar group', - 'remote_as' : '200', - 'shutdown' : '', - 'no_cap_nego' : '', - 'local_as' : '300', + 'description' : 'foo peer bar group', + 'remote_as' : '200', + 'shutdown' : '', + 'no_cap_nego' : '', + 'local_as' : '300', + 'pfx_list_in' : prefix_list_in, + 'pfx_list_out' : prefix_list_out, + 'no_send_comm_ext' : '', }, 'baz' : { - 'cap_dynamic' : '', - 'cap_ext_next': '', - 'remote_as' : '200', - 'passive' : '', - 'multi_hop' : '5', - 'update_src' : 'lo', + 'cap_dynamic' : '', + 'cap_ext_next' : '', + 'remote_as' : '200', + 'passive' : '', + 'multi_hop' : '5', + 'update_src' : 'lo', + 'route_map_in' : route_map_in, + 'route_map_out': route_map_out, }, } -def getFRRBGPconfig(): - return cmd(f'vtysh -c "show run" | sed -n "/router bgp {ASN}/,/^!/p"') -class TestProtocolsBGP(unittest.TestCase): +class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): def setUp(self): - self.session = ConfigSession(os.getpid()) + self.cli_set(['policy', 'route-map', route_map_in, 'rule', '10', 'action', 'permit']) + self.cli_set(['policy', 'route-map', route_map_out, 'rule', '10', 'action', 'permit']) + self.cli_set(['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'action', 'permit']) + self.cli_set(['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'prefix', '192.0.2.0/25']) + self.cli_set(['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'action', 'permit']) + self.cli_set(['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'prefix', '192.0.2.128/25']) + + self.cli_set(['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'action', 'permit']) + self.cli_set(['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'prefix', '2001:db8:1000::/64']) + self.cli_set(['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'action', 'deny']) + self.cli_set(['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'prefix', '2001:db8:2000::/64']) def tearDown(self): - self.session.delete(base_path) - self.session.commit() - del self.session + self.cli_delete(['policy', 'route-map', route_map_in]) + self.cli_delete(['policy', 'route-map', route_map_out]) + self.cli_delete(['policy', 'prefix-list', prefix_list_in]) + self.cli_delete(['policy', 'prefix-list', prefix_list_out]) + self.cli_delete(['policy', 'prefix-list6', prefix_list_in6]) + self.cli_delete(['policy', 'prefix-list6', prefix_list_out6]) + + self.cli_delete(base_path) + self.cli_commit() + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) def verify_frr_config(self, peer, peer_config, frrconfig): # recurring patterns to verify for both a simple neighbor and a peer-group @@ -124,121 +184,205 @@ class TestProtocolsBGP(unittest.TestCase): self.assertIn(f' neighbor {peer} ttl-security hops {peer_config["ttl_security"]}', frrconfig) if 'update_src' in peer_config: self.assertIn(f' neighbor {peer} update-source {peer_config["update_src"]}', frrconfig) + if 'route_map_in' in peer_config: + self.assertIn(f' neighbor {peer} route-map {peer_config["route_map_in"]} in', frrconfig) + if 'route_map_out' in peer_config: + self.assertIn(f' neighbor {peer} route-map {peer_config["route_map_out"]} out', frrconfig) + if 'pfx_list_in' in peer_config: + self.assertIn(f' neighbor {peer} prefix-list {peer_config["pfx_list_in"]} in', frrconfig) + if 'pfx_list_out' in peer_config: + self.assertIn(f' neighbor {peer} prefix-list {peer_config["pfx_list_out"]} out', frrconfig) + if 'no_send_comm_std' in peer_config: + self.assertIn(f' no neighbor {peer} send-community', frrconfig) + if 'no_send_comm_ext' in peer_config: + self.assertIn(f' no neighbor {peer} send-community extended', frrconfig) + if 'addpath_all' in peer_config: + self.assertIn(f' neighbor {peer} addpath-tx-all-paths', frrconfig) + if 'addpath_per_as' in peer_config: + self.assertIn(f' neighbor {peer} addpath-tx-bestpath-per-AS', frrconfig) + def test_bgp_01_simple(self): router_id = '127.0.0.1' local_pref = '500' - - self.session.set(base_path + ['parameters', 'router-id', router_id]) - self.session.set(base_path + ['parameters', 'log-neighbor-changes']) - # Default local preference (higher=more preferred) - self.session.set(base_path + ['parameters', 'default', 'local-pref', local_pref]) + stalepath_time = '60' + max_path_v4 = '2' + max_path_v4ibgp = '4' + max_path_v6 = '8' + max_path_v6ibgp = '16' + + self.cli_set(base_path + ['parameters', 'router-id', router_id]) + self.cli_set(base_path + ['parameters', 'log-neighbor-changes']) + + # Local AS number MUST be defined + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(base_path + ['local-as', ASN]) + + # Default local preference (higher = more preferred, default value is 100) + self.cli_set(base_path + ['parameters', 'default', 'local-pref', local_pref]) # Deactivate IPv4 unicast for a peer by default - self.session.set(base_path + ['parameters', 'default', 'no-ipv4-unicast']) + self.cli_set(base_path + ['parameters', 'default', 'no-ipv4-unicast']) + self.cli_set(base_path + ['parameters', 'graceful-restart', 'stalepath-time', stalepath_time]) + self.cli_set(base_path + ['parameters', 'graceful-shutdown']) + self.cli_set(base_path + ['parameters', 'ebgp-requires-policy']) + + # AFI maximum path support + self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ebgp', max_path_v4]) + self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ibgp', max_path_v4ibgp]) + self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'maximum-paths', 'ebgp', max_path_v6]) + self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'maximum-paths', 'ibgp', max_path_v6ibgp]) # commit changes - self.session.commit() + self.cli_commit() # Verify FRR bgpd configuration - frrconfig = getFRRBGPconfig() + frrconfig = self.getFRRconfig(f'router bgp {ASN}') self.assertIn(f'router bgp {ASN}', frrconfig) self.assertIn(f' bgp router-id {router_id}', frrconfig) self.assertIn(f' bgp log-neighbor-changes', frrconfig) self.assertIn(f' bgp default local-preference {local_pref}', frrconfig) self.assertIn(f' no bgp default ipv4-unicast', frrconfig) + self.assertIn(f' bgp graceful-restart stalepath-time {stalepath_time}', frrconfig) + self.assertIn(f' bgp graceful-shutdown', frrconfig) + self.assertNotIn(f'bgp ebgp-requires-policy', frrconfig) + + afiv4_config = self.getFRRconfig(' address-family ipv4 unicast') + self.assertIn(f' maximum-paths {max_path_v4}', afiv4_config) + self.assertIn(f' maximum-paths ibgp {max_path_v4ibgp}', afiv4_config) + + afiv6_config = self.getFRRconfig(' address-family ipv6 unicast') + self.assertIn(f' maximum-paths {max_path_v6}', afiv6_config) + self.assertIn(f' maximum-paths ibgp {max_path_v6ibgp}', afiv6_config) - # Check for running process - self.assertTrue(process_named_running(PROCESS_NAME)) def test_bgp_02_neighbors(self): + self.cli_set(base_path + ['local-as', ASN]) # Test out individual neighbor configuration items, not all of them are # also available to a peer-group! - for neighbor, config in neighbor_config.items(): - if 'adv_interv' in config: - self.session.set(base_path + ['neighbor', neighbor, 'advertisement-interval', config["adv_interv"]]) - if 'cap_dynamic' in config: - self.session.set(base_path + ['neighbor', neighbor, 'capability', 'dynamic']) - if 'cap_ext_next' in config: - self.session.set(base_path + ['neighbor', neighbor, 'capability', 'extended-nexthop']) - if 'description' in config: - self.session.set(base_path + ['neighbor', neighbor, 'description', config["description"]]) - if 'no_cap_nego' in config: - self.session.set(base_path + ['neighbor', neighbor, 'disable-capability-negotiation']) - if 'multi_hop' in config: - self.session.set(base_path + ['neighbor', neighbor, 'ebgp-multihop', config["multi_hop"]]) - if 'local_as' in config: - self.session.set(base_path + ['neighbor', neighbor, 'local-as', config["local_as"]]) - if 'cap_over' in config: - self.session.set(base_path + ['neighbor', neighbor, 'override-capability']) - if 'passive' in config: - self.session.set(base_path + ['neighbor', neighbor, 'passive']) - if 'password' in config: - self.session.set(base_path + ['neighbor', neighbor, 'password', config["password"]]) - if 'port' in config: - self.session.set(base_path + ['neighbor', neighbor, 'port', config["port"]]) - if 'remote_as' in config: - self.session.set(base_path + ['neighbor', neighbor, 'remote-as', config["remote_as"]]) - if 'cap_strict' in config: - self.session.set(base_path + ['neighbor', neighbor, 'strict-capability-match']) - if 'shutdown' in config: - self.session.set(base_path + ['neighbor', neighbor, 'shutdown']) - if 'ttl_security' in config: - self.session.set(base_path + ['neighbor', neighbor, 'ttl-security', 'hops', config["ttl_security"]]) - if 'update_src' in config: - self.session.set(base_path + ['neighbor', neighbor, 'update-source', config["update_src"]]) + for peer, peer_config in neighbor_config.items(): + afi = 'ipv4-unicast' + if is_ipv6(peer): + afi = 'ipv6-unicast' + + if 'adv_interv' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'advertisement-interval', peer_config["adv_interv"]]) + if 'cap_dynamic' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'capability', 'dynamic']) + if 'cap_ext_next' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'capability', 'extended-nexthop']) + if 'description' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'description', peer_config["description"]]) + if 'no_cap_nego' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'disable-capability-negotiation']) + if 'multi_hop' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'ebgp-multihop', peer_config["multi_hop"]]) + if 'local_as' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'local-as', peer_config["local_as"]]) + if 'cap_over' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'override-capability']) + if 'passive' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'passive']) + if 'password' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'password', peer_config["password"]]) + if 'port' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'port', peer_config["port"]]) + if 'remote_as' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'remote-as', peer_config["remote_as"]]) + if 'cap_strict' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'strict-capability-match']) + if 'shutdown' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'shutdown']) + if 'ttl_security' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'ttl-security', 'hops', peer_config["ttl_security"]]) + if 'update_src' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'update-source', peer_config["update_src"]]) + if 'route_map_in' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'route-map', 'import', peer_config["route_map_in"]]) + if 'route_map_out' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'route-map', 'export', peer_config["route_map_out"]]) + if 'pfx_list_in' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'prefix-list', 'import', peer_config["pfx_list_in"]]) + if 'pfx_list_out' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'prefix-list', 'export', peer_config["pfx_list_out"]]) + if 'no_send_comm_std' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'disable-send-community', 'standard']) + if 'no_send_comm_ext' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'disable-send-community', 'extended']) + if 'addpath_all' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-all']) + if 'addpath_per_as' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-per-as']) # commit changes - self.session.commit() + self.cli_commit() # Verify FRR bgpd configuration - frrconfig = getFRRBGPconfig() + frrconfig = self.getFRRconfig(f'router bgp {ASN}') self.assertIn(f'router bgp {ASN}', frrconfig) for peer, peer_config in neighbor_config.items(): - if 'adv_interv' in config: + if 'adv_interv' in peer_config: self.assertIn(f' neighbor {peer} advertisement-interval {peer_config["adv_interv"]}', frrconfig) - if 'port' in config: + if 'port' in peer_config: self.assertIn(f' neighbor {peer} port {peer_config["port"]}', frrconfig) - if 'cap_strict' in config: + if 'cap_strict' in peer_config: self.assertIn(f' neighbor {peer} strict-capability-match', frrconfig) self.verify_frr_config(peer, peer_config, frrconfig) def test_bgp_03_peer_groups(self): + self.cli_set(base_path + ['local-as', ASN]) # Test out individual peer-group configuration items for peer_group, config in peer_group_config.items(): if 'cap_dynamic' in config: - self.session.set(base_path + ['peer-group', peer_group, 'capability', 'dynamic']) + self.cli_set(base_path + ['peer-group', peer_group, 'capability', 'dynamic']) if 'cap_ext_next' in config: - self.session.set(base_path + ['peer-group', peer_group, 'capability', 'extended-nexthop']) + self.cli_set(base_path + ['peer-group', peer_group, 'capability', 'extended-nexthop']) if 'description' in config: - self.session.set(base_path + ['peer-group', peer_group, 'description', config["description"]]) + self.cli_set(base_path + ['peer-group', peer_group, 'description', config["description"]]) if 'no_cap_nego' in config: - self.session.set(base_path + ['peer-group', peer_group, 'disable-capability-negotiation']) + self.cli_set(base_path + ['peer-group', peer_group, 'disable-capability-negotiation']) if 'multi_hop' in config: - self.session.set(base_path + ['peer-group', peer_group, 'ebgp-multihop', config["multi_hop"]]) + self.cli_set(base_path + ['peer-group', peer_group, 'ebgp-multihop', config["multi_hop"]]) if 'local_as' in config: - self.session.set(base_path + ['peer-group', peer_group, 'local-as', config["local_as"]]) + self.cli_set(base_path + ['peer-group', peer_group, 'local-as', config["local_as"]]) if 'cap_over' in config: - self.session.set(base_path + ['peer-group', peer_group, 'override-capability']) + self.cli_set(base_path + ['peer-group', peer_group, 'override-capability']) if 'passive' in config: - self.session.set(base_path + ['peer-group', peer_group, 'passive']) + self.cli_set(base_path + ['peer-group', peer_group, 'passive']) if 'password' in config: - self.session.set(base_path + ['peer-group', peer_group, 'password', config["password"]]) + self.cli_set(base_path + ['peer-group', peer_group, 'password', config["password"]]) if 'remote_as' in config: - self.session.set(base_path + ['peer-group', peer_group, 'remote-as', config["remote_as"]]) + self.cli_set(base_path + ['peer-group', peer_group, 'remote-as', config["remote_as"]]) if 'shutdown' in config: - self.session.set(base_path + ['peer-group', peer_group, 'shutdown']) + self.cli_set(base_path + ['peer-group', peer_group, 'shutdown']) if 'ttl_security' in config: - self.session.set(base_path + ['peer-group', peer_group, 'ttl-security', 'hops', config["ttl_security"]]) + self.cli_set(base_path + ['peer-group', peer_group, 'ttl-security', 'hops', config["ttl_security"]]) if 'update_src' in config: - self.session.set(base_path + ['peer-group', peer_group, 'update-source', config["update_src"]]) + self.cli_set(base_path + ['peer-group', peer_group, 'update-source', config["update_src"]]) + if 'route_map_in' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'route-map', 'import', config["route_map_in"]]) + if 'route_map_out' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'route-map', 'export', config["route_map_out"]]) + if 'pfx_list_in' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'prefix-list', 'import', config["pfx_list_in"]]) + if 'pfx_list_out' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'prefix-list', 'export', config["pfx_list_out"]]) + if 'no_send_comm_std' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'disable-send-community', 'standard']) + if 'no_send_comm_ext' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'disable-send-community', 'extended']) + if 'addpath_all' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-all']) + if 'addpath_per_as' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-per-as']) # commit changes - self.session.commit() + self.cli_commit() # Verify FRR bgpd configuration - frrconfig = getFRRBGPconfig() + frrconfig = self.getFRRconfig(f'router bgp {ASN}') self.assertIn(f'router bgp {ASN}', frrconfig) for peer, peer_config in peer_group_config.items(): @@ -259,27 +403,29 @@ class TestProtocolsBGP(unittest.TestCase): }, } + self.cli_set(base_path + ['local-as', ASN]) + # We want to redistribute ... - redistributes = ['connected', 'kernel', 'ospf', 'rip', 'static'] + redistributes = ['connected', 'isis', 'kernel', 'ospf', 'rip', 'static'] for redistribute in redistributes: - self.session.set(base_path + ['address-family', 'ipv4-unicast', + self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'redistribute', redistribute]) for network, network_config in networks.items(): - self.session.set(base_path + ['address-family', 'ipv4-unicast', + self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'network', network]) if 'as_set' in network_config: - self.session.set(base_path + ['address-family', 'ipv4-unicast', + self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'aggregate-address', network, 'as-set']) if 'summary_only' in network_config: - self.session.set(base_path + ['address-family', 'ipv4-unicast', + self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'aggregate-address', network, 'summary-only']) # commit changes - self.session.commit() + self.cli_commit() # Verify FRR bgpd configuration - frrconfig = getFRRBGPconfig() + frrconfig = self.getFRRconfig(f'router bgp {ASN}') self.assertIn(f'router bgp {ASN}', frrconfig) self.assertIn(f' address-family ipv4 unicast', frrconfig) @@ -297,34 +443,39 @@ class TestProtocolsBGP(unittest.TestCase): def test_bgp_05_afi_ipv6(self): networks = { '2001:db8:100::/48' : { - }, + }, '2001:db8:200::/48' : { - }, + }, '2001:db8:300::/48' : { 'summary_only' : '', - }, + }, } + self.cli_set(base_path + ['local-as', ASN]) + # We want to redistribute ... redistributes = ['connected', 'kernel', 'ospfv3', 'ripng', 'static'] for redistribute in redistributes: - self.session.set(base_path + ['address-family', 'ipv6-unicast', + self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'redistribute', redistribute]) for network, network_config in networks.items(): - self.session.set(base_path + ['address-family', 'ipv6-unicast', + self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'network', network]) if 'summary_only' in network_config: - self.session.set(base_path + ['address-family', 'ipv6-unicast', + self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'aggregate-address', network, 'summary-only']) # commit changes - self.session.commit() + self.cli_commit() # Verify FRR bgpd configuration - frrconfig = getFRRBGPconfig() + frrconfig = self.getFRRconfig(f'router bgp {ASN}') self.assertIn(f'router bgp {ASN}', frrconfig) self.assertIn(f' address-family ipv6 unicast', frrconfig) + # T2100: By default ebgp-requires-policy is disabled to keep VyOS + # 1.3 and 1.2 backwards compatibility + self.assertIn(f' no bgp ebgp-requires-policy', frrconfig) for redistribute in redistributes: # FRR calls this OSPF6 @@ -338,5 +489,95 @@ class TestProtocolsBGP(unittest.TestCase): self.assertIn(f' aggregate-address {network} summary-only', frrconfig) + def test_bgp_06_listen_range(self): + # Implemented via T1875 + limit = '64' + listen_ranges = ['192.0.2.0/25', '192.0.2.128/25'] + peer_group = 'listenfoobar' + + self.cli_set(base_path + ['local-as', ASN]) + self.cli_set(base_path + ['listen', 'limit', limit]) + + for prefix in listen_ranges: + self.cli_set(base_path + ['listen', 'range', prefix]) + # check validate() - peer-group must be defined for range/prefix + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(base_path + ['listen', 'range', prefix, 'peer-group', peer_group]) + + # check validate() - peer-group does yet not exist! + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(base_path + ['peer-group', peer_group, 'remote-as', ASN]) + + # commit changes + self.cli_commit() + + # Verify FRR bgpd configuration + frrconfig = self.getFRRconfig(f'router bgp {ASN}') + self.assertIn(f'router bgp {ASN}', frrconfig) + self.assertIn(f' neighbor {peer_group} peer-group', frrconfig) + self.assertIn(f' neighbor {peer_group} remote-as {ASN}', frrconfig) + self.assertIn(f' bgp listen limit {limit}', frrconfig) + for prefix in listen_ranges: + self.assertIn(f' bgp listen range {prefix} peer-group {peer_group}', frrconfig) + + + def test_bgp_07_l2vpn_evpn(self): + vnis = ['10010', '10020', '10030'] + neighbors = ['192.0.2.10', '192.0.2.20', '192.0.2.30'] + + self.cli_set(base_path + ['local-as', ASN]) + + self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-all-vni']) + self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-default-gw']) + self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-svi-ip']) + self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'flooding', 'disable']) + for vni in vnis: + self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'vni', vni, 'advertise-default-gw']) + self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'vni', vni, 'advertise-svi-ip']) + + # commit changes + self.cli_commit() + + # Verify FRR bgpd configuration + frrconfig = self.getFRRconfig(f'router bgp {ASN}') + self.assertIn(f'router bgp {ASN}', frrconfig) + self.assertIn(f' address-family l2vpn evpn', frrconfig) + self.assertIn(f' advertise-all-vni', frrconfig) + self.assertIn(f' advertise-default-gw', frrconfig) + self.assertIn(f' advertise-svi-ip', frrconfig) + self.assertIn(f' flooding disable', frrconfig) + for vni in vnis: + vniconfig = self.getFRRconfig(f' vni {vni}') + self.assertIn(f'vni {vni}', vniconfig) + self.assertIn(f' advertise-default-gw', vniconfig) + self.assertIn(f' advertise-svi-ip', vniconfig) + + def test_bgp_08_vrf_simple(self): + router_id = '127.0.0.3' + vrfs = ['red', 'green', 'blue'] + + # It is safe to assume that when the basic VRF test works, all + # other BGP related features work, as we entirely inherit the CLI + # templates and Jinja2 FRR template. + table = '1000' + + for vrf in vrfs: + vrf_base = ['vrf', 'name', vrf] + self.cli_set(vrf_base + ['table', table]) + self.cli_set(vrf_base + ['protocols', 'bgp', 'local-as', ASN]) + self.cli_set(vrf_base + ['protocols', 'bgp', 'parameters', 'router-id', router_id]) + table = str(int(table) + 1000) + + self.cli_commit() + + for vrf in vrfs: + # Verify FRR bgpd configuration + frrconfig = self.getFRRconfig(f'router bgp {ASN} vrf {vrf}') + + self.assertIn(f'router bgp {ASN} vrf {vrf}', frrconfig) + self.assertIn(f' bgp router-id {router_id}', frrconfig) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_igmp-proxy.py b/smoketest/scripts/cli/test_protocols_igmp-proxy.py index 6aaad739d..1eaf21722 100755 --- a/smoketest/scripts/cli/test_protocols_igmp-proxy.py +++ b/smoketest/scripts/cli/test_protocols_igmp-proxy.py @@ -14,9 +14,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import unittest +from base_vyostest_shim import VyOSUnitTestSHIM + from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError from vyos.util import read_file @@ -28,46 +29,44 @@ base_path = ['protocols', 'igmp-proxy'] upstream_if = 'eth1' downstream_if = 'eth2' -class TestProtocolsIGMPProxy(unittest.TestCase): +class TestProtocolsIGMPProxy(VyOSUnitTestSHIM.TestCase): def setUp(self): - self.session = ConfigSession(os.getpid()) - self.session.set(['interfaces', 'ethernet', upstream_if, 'address', '172.16.1.1/24']) + self.cli_set(['interfaces', 'ethernet', upstream_if, 'address', '172.16.1.1/24']) def tearDown(self): - self.session.delete(['interfaces', 'ethernet', upstream_if, 'address']) - self.session.delete(base_path) - self.session.commit() - del self.session + self.cli_delete(['interfaces', 'ethernet', upstream_if, 'address']) + self.cli_delete(base_path) + self.cli_commit() def test_igmpproxy(self): threshold = '20' altnet = '192.0.2.0/24' whitelist = '10.0.0.0/8' - self.session.set(base_path + ['disable-quickleave']) - self.session.set(base_path + ['interface', upstream_if, 'threshold', threshold]) - self.session.set(base_path + ['interface', upstream_if, 'alt-subnet', altnet]) - self.session.set(base_path + ['interface', upstream_if, 'whitelist', whitelist]) + self.cli_set(base_path + ['disable-quickleave']) + self.cli_set(base_path + ['interface', upstream_if, 'threshold', threshold]) + self.cli_set(base_path + ['interface', upstream_if, 'alt-subnet', altnet]) + self.cli_set(base_path + ['interface', upstream_if, 'whitelist', whitelist]) # Must define an upstream and at least 1 downstream interface! with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(base_path + ['interface', upstream_if, 'role', 'upstream']) + self.cli_commit() + self.cli_set(base_path + ['interface', upstream_if, 'role', 'upstream']) # Interface does not exist - self.session.set(base_path + ['interface', 'eth20', 'role', 'upstream']) + self.cli_set(base_path + ['interface', 'eth20', 'role', 'upstream']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(base_path + ['interface', 'eth20']) + self.cli_commit() + self.cli_delete(base_path + ['interface', 'eth20']) # Only 1 upstream interface allowed - self.session.set(base_path + ['interface', downstream_if, 'role', 'upstream']) + self.cli_set(base_path + ['interface', downstream_if, 'role', 'upstream']) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(base_path + ['interface', downstream_if, 'role', 'downstream']) + self.cli_commit() + self.cli_set(base_path + ['interface', downstream_if, 'role', 'downstream']) # commit changes - self.session.commit() + self.cli_commit() # Check generated configuration config = read_file(IGMP_PROXY_CONF) diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py new file mode 100755 index 000000000..623cb044d --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_isis.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 unittest + +from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.ifconfig import Section +from vyos.util import process_named_running + +PROCESS_NAME = 'isisd' +base_path = ['protocols', 'isis'] + +domain = 'VyOS' +net = '49.0001.1921.6800.1002.00' + +class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): + def tearDown(self): + self.cli_delete(base_path) + self.cli_commit() + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + def test_isis_01_redistribute(self): + prefix_list = 'EXPORT-ISIS' + route_map = 'EXPORT-ISIS' + rule = '10' + self.cli_set(['policy', 'prefix-list', prefix_list, 'rule', rule, 'action', 'permit']) + self.cli_set(['policy', 'prefix-list', prefix_list, 'rule', rule, 'prefix', '203.0.113.0/24']) + self.cli_set(['policy', 'route-map', route_map, 'rule', rule, 'action', 'permit']) + self.cli_set(['policy', 'route-map', route_map, 'rule', rule, 'match', 'ip', 'address', 'prefix-list', prefix_list]) + + self.cli_set(base_path + ['net', net]) + self.cli_set(base_path + ['redistribute', 'ipv4', 'connected', 'level-2', 'route-map', route_map]) + + interfaces = Section.interfaces('ethernet') + for interface in interfaces: + self.cli_set(base_path + ['interface', interface]) + + # Commit all changes + self.cli_commit() + + # Verify all changes + tmp = self.getFRRconfig(f'router isis {domain}') + self.assertIn(f' net {net}', tmp) + self.assertIn(f' redistribute ipv4 connected level-2 route-map {route_map}', tmp) + + for interface in interfaces: + tmp = self.getFRRconfig(f'interface {interface}') + self.assertIn(f' ip router isis {domain}', tmp) + + self.cli_delete(['policy']) + + + def test_isis_02_vrfs(self): + vrfs = ['red', 'green', 'blue'] + # It is safe to assume that when the basic VRF test works, all other + # IS-IS related features work, as we entirely inherit the CLI templates + # and Jinja2 FRR template. + table = '1000' + vrf = 'red' + vrf_base = ['vrf', 'name', vrf] + vrf_iface = 'eth1' + self.cli_set(vrf_base + ['table', table]) + self.cli_set(vrf_base + ['protocols', 'isis', 'net', net]) + self.cli_set(vrf_base + ['protocols', 'isis', 'interface', vrf_iface]) + self.cli_set(['interfaces', 'ethernet', vrf_iface, 'vrf', vrf]) + + # Also set a default VRF IS-IS config + self.cli_set(base_path + ['net', net]) + self.cli_set(base_path + ['interface', 'eth0']) + self.cli_commit() + + # Verify FRR isisd configuration + tmp = self.getFRRconfig(f'router isis {domain}') + self.assertIn(f'router isis {domain}', tmp) + self.assertIn(f' net {net}', tmp) + + tmp = self.getFRRconfig(f'router isis {domain} vrf {vrf}') + self.assertIn(f'router isis {domain} vrf {vrf}', tmp) + self.assertIn(f' net {net}', tmp) + + self.cli_delete(['vrf', 'name', vrf]) + self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf']) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py new file mode 100755 index 000000000..8d94c86cb --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -0,0 +1,321 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 unittest + +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.ifconfig import Section +from vyos.util import process_named_running + +PROCESS_NAME = 'ospfd' +base_path = ['protocols', 'ospf'] + +route_map = 'foo-bar-baz10' + +class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): + def setUp(self): + self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) + self.cli_set(['policy', 'route-map', route_map, 'rule', '20', 'action', 'permit']) + + def tearDown(self): + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + self.cli_delete(['policy', 'route-map', route_map]) + self.cli_delete(base_path) + self.cli_commit() + + def test_ospf_01_defaults(self): + # commit changes + self.cli_set(base_path) + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' auto-cost reference-bandwidth 100', frrconfig) + self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults + + def test_ospf_02_simple(self): + router_id = '127.0.0.1' + abr_type = 'ibm' + bandwidth = '1000' + metric = '123' + + self.cli_set(base_path + ['auto-cost', 'reference-bandwidth', bandwidth]) + self.cli_set(base_path + ['parameters', 'router-id', router_id]) + self.cli_set(base_path + ['parameters', 'abr-type', abr_type]) + self.cli_set(base_path + ['log-adjacency-changes', 'detail']) + self.cli_set(base_path + ['default-metric', metric]) + + # commit changes + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' auto-cost reference-bandwidth {bandwidth}', frrconfig) + self.assertIn(f' ospf router-id {router_id}', frrconfig) + self.assertIn(f' ospf abr-type {abr_type}', frrconfig) + self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults + self.assertIn(f' default-metric {metric}', frrconfig) + + + def test_ospf_03_access_list(self): + acl = '100' + seq = '10' + protocols = ['bgp', 'connected', 'isis', 'kernel', 'rip', 'static'] + + self.cli_set(['policy', 'access-list', acl, 'rule', seq, 'action', 'permit']) + self.cli_set(['policy', 'access-list', acl, 'rule', seq, 'source', 'any']) + self.cli_set(['policy', 'access-list', acl, 'rule', seq, 'destination', 'any']) + for ptotocol in protocols: + self.cli_set(base_path + ['access-list', acl, 'export', ptotocol]) + + # commit changes + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults + for ptotocol in protocols: + self.assertIn(f' distribute-list {acl} out {ptotocol}', frrconfig) # defaults + self.cli_delete(['policy', 'access-list', acl]) + + + def test_ospf_04_default_originate(self): + seq = '100' + metric = '50' + metric_type = '1' + + self.cli_set(base_path + ['default-information', 'originate', 'metric', metric]) + self.cli_set(base_path + ['default-information', 'originate', 'metric-type', metric_type]) + self.cli_set(base_path + ['default-information', 'originate', 'route-map', route_map]) + + # commit changes + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults + self.assertIn(f' default-information originate metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) + + # Now set 'always' + self.cli_set(base_path + ['default-information', 'originate', 'always']) + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f' default-information originate always metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) + + + def test_ospf_05_options(self): + global_distance = '128' + intra_area = '100' + inter_area = '110' + external = '120' + on_startup = '30' + on_shutdown = '60' + refresh = '50' + + self.cli_set(base_path + ['distance', 'global', global_distance]) + self.cli_set(base_path + ['distance', 'ospf', 'external', external]) + self.cli_set(base_path + ['distance', 'ospf', 'intra-area', intra_area]) + + self.cli_set(base_path + ['max-metric', 'router-lsa', 'on-startup', on_startup]) + self.cli_set(base_path + ['max-metric', 'router-lsa', 'on-shutdown', on_shutdown]) + + self.cli_set(base_path + ['mpls-te', 'enable']) + self.cli_set(base_path + ['refresh', 'timers', refresh]) + + # commit changes + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' mpls-te on', frrconfig) + self.assertIn(f' mpls-te router-address 0.0.0.0', frrconfig) # default + self.assertIn(f' distance {global_distance}', frrconfig) + self.assertIn(f' distance ospf intra-area {intra_area} external {external}', frrconfig) + self.assertIn(f' max-metric router-lsa on-startup {on_startup}', frrconfig) + self.assertIn(f' max-metric router-lsa on-shutdown {on_shutdown}', frrconfig) + self.assertIn(f' refresh timer {refresh}', frrconfig) + + + # enable inter-area + self.cli_set(base_path + ['distance', 'ospf', 'inter-area', inter_area]) + self.cli_commit() + + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f' distance ospf intra-area {intra_area} inter-area {inter_area} external {external}', frrconfig) + + + def test_ospf_06_neighbor(self): + priority = '10' + poll_interval = '20' + neighbors = ['1.1.1.1', '2.2.2.2', '3.3.3.3'] + for neighbor in neighbors: + self.cli_set(base_path + ['neighbor', neighbor, 'priority', priority]) + self.cli_set(base_path + ['neighbor', neighbor, 'poll-interval', poll_interval]) + + # commit changes + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f'router ospf', frrconfig) + for neighbor in neighbors: + self.assertIn(f' neighbor {neighbor} priority {priority} poll-interval {poll_interval}', frrconfig) # default + + + def test_ospf_07_passive_interface(self): + self.cli_set(base_path + ['passive-interface', 'default']) + interfaces = Section.interfaces('ethernet') + for interface in interfaces: + self.cli_set(base_path + ['passive-interface-exclude', interface]) + + # commit changes + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' passive-interface default', frrconfig) # default + for interface in interfaces: + self.assertIn(f' no passive-interface {interface}', frrconfig) # default + + + def test_ospf_08_redistribute(self): + metric = '15' + metric_type = '1' + redistribute = ['bgp', 'connected', 'isis', 'kernel', 'rip', 'static'] + + for protocol in redistribute: + self.cli_set(base_path + ['redistribute', protocol, 'metric', metric]) + self.cli_set(base_path + ['redistribute', protocol, 'route-map', route_map]) + if protocol not in ['kernel', 'static']: + self.cli_set(base_path + ['redistribute', protocol, 'metric-type', metric_type]) + + # commit changes + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f'router ospf', frrconfig) + for protocol in redistribute: + if protocol in ['kernel', 'static']: + self.assertIn(f' redistribute {protocol} metric {metric} route-map {route_map}', frrconfig) + else: + self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) + + + def test_ospf_09_virtual_link(self): + networks = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'] + area = '10' + shortcut = 'enable' + virtual_link = '192.0.2.1' + hello = '6' + retransmit = '5' + transmit = '5' + dead = '40' + + self.cli_set(base_path + ['area', area, 'shortcut', shortcut]) + self.cli_set(base_path + ['area', area, 'virtual-link', virtual_link, 'hello-interval', hello]) + self.cli_set(base_path + ['area', area, 'virtual-link', virtual_link, 'retransmit-interval', retransmit]) + self.cli_set(base_path + ['area', area, 'virtual-link', virtual_link, 'transmit-delay', transmit]) + self.cli_set(base_path + ['area', area, 'virtual-link', virtual_link, 'dead-interval', dead]) + for network in networks: + self.cli_set(base_path + ['area', area, 'network', network]) + + # commit changes + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' area {area} shortcut {shortcut}', frrconfig) + self.assertIn(f' area {area} virtual-link {virtual_link} hello-interval {hello} retransmit-interval {retransmit} transmit-delay {transmit} dead-interval {dead}', frrconfig) + for network in networks: + self.assertIn(f' network {network} area {area}', frrconfig) + + + def test_ospf_10_interface_configureation(self): + interfaces = Section.interfaces('ethernet') + password = 'vyos1234' + bandwidth = '10000' + cost = '150' + network = 'point-to-point' + priority = '200' + + for interface in interfaces: + self.cli_set(base_path + ['interface', interface, 'authentication', 'plaintext-password', password]) + self.cli_set(base_path + ['interface', interface, 'bandwidth', bandwidth]) + self.cli_set(base_path + ['interface', interface, 'bfd']) + self.cli_set(base_path + ['interface', interface, 'cost', cost]) + self.cli_set(base_path + ['interface', interface, 'mtu-ignore']) + self.cli_set(base_path + ['interface', interface, 'network', network]) + self.cli_set(base_path + ['interface', interface, 'priority', priority]) + + # commit changes + self.cli_commit() + + for interface in interfaces: + config = self.getFRRconfig(f'interface {interface}') + self.assertIn(f'interface {interface}', config) + self.assertIn(f' ip ospf authentication-key {password}', config) + self.assertIn(f' ip ospf bfd', config) + self.assertIn(f' ip ospf cost {cost}', config) + self.assertIn(f' ip ospf mtu-ignore', config) + self.assertIn(f' ip ospf network {network}', config) + self.assertIn(f' ip ospf priority {priority}', config) + self.assertIn(f' bandwidth {bandwidth}', config) + + + def test_ospf_11_vrfs(self): + # It is safe to assume that when the basic VRF test works, all + # other OSPF related features work, as we entirely inherit the CLI + # templates and Jinja2 FRR template. + table = '1000' + vrf = 'blue' + vrf_base = ['vrf', 'name', vrf] + vrf_iface = 'eth1' + self.cli_set(vrf_base + ['table', table]) + self.cli_set(vrf_base + ['protocols', 'ospf', 'interface', vrf_iface]) + self.cli_set(['interfaces', 'ethernet', vrf_iface, 'vrf', vrf]) + + # Also set a default VRF OSPF config + self.cli_set(base_path) + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' auto-cost reference-bandwidth 100', frrconfig) + self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults + + frrconfig = self.getFRRconfig(f'router ospf vrf {vrf}') + self.assertIn(f'router ospf vrf {vrf}', frrconfig) + self.assertIn(f' auto-cost reference-bandwidth 100', frrconfig) + self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults + + self.cli_delete(['vrf', 'name', vrf]) + self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf']) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py new file mode 100755 index 000000000..6bb551642 --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_ospfv3.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 unittest + +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSession +from vyos.ifconfig import Section +from vyos.util import process_named_running + +PROCESS_NAME = 'ospf6d' +base_path = ['protocols', 'ospfv3'] + +router_id = '192.0.2.1' +default_area = '0' + +class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase): + def tearDown(self): + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + self.cli_delete(base_path) + self.cli_commit() + + def test_ospfv3_01_basic(self): + seq = '10' + prefix = '2001:db8::/32' + acl_name = 'foo-acl-100' + + self.cli_set(['policy', 'access-list6', acl_name, 'rule', seq, 'action', 'permit']) + self.cli_set(['policy', 'access-list6', acl_name, 'rule', seq, 'source', 'any']) + + self.cli_set(base_path + ['parameters', 'router-id', router_id]) + self.cli_set(base_path + ['area', default_area, 'range', prefix, 'advertise']) + self.cli_set(base_path + ['area', default_area, 'export-list', acl_name]) + self.cli_set(base_path + ['area', default_area, 'import-list', acl_name]) + + interfaces = Section.interfaces('ethernet') + for interface in interfaces: + self.cli_set(base_path + ['area', default_area, 'interface', interface]) + + # commit changes + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf6') + self.assertIn(f'router ospf6', frrconfig) + self.assertIn(f' area {default_area} range {prefix}', frrconfig) + self.assertIn(f' ospf6 router-id {router_id}', frrconfig) + self.assertIn(f' area {default_area} import-list {acl_name}', frrconfig) + self.assertIn(f' area {default_area} export-list {acl_name}', frrconfig) + + for interface in interfaces: + self.assertIn(f' interface {interface} area {default_area}', frrconfig) + + self.cli_delete(['policy', 'access-list6', acl_name]) + + + def test_ospfv3_02_distance(self): + dist_global = '200' + dist_external = '110' + dist_inter_area = '120' + dist_intra_area = '130' + + self.cli_set(base_path + ['distance', 'global', dist_global]) + self.cli_set(base_path + ['distance', 'ospfv3', 'external', dist_external]) + self.cli_set(base_path + ['distance', 'ospfv3', 'inter-area', dist_inter_area]) + self.cli_set(base_path + ['distance', 'ospfv3', 'intra-area', dist_intra_area]) + + # commit changes + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf6') + self.assertIn(f'router ospf6', frrconfig) + self.assertIn(f' distance {dist_global}', frrconfig) + self.assertIn(f' distance ospf6 intra-area {dist_intra_area} inter-area {dist_inter_area} external {dist_external}', frrconfig) + + + def test_ospfv3_03_redistribute(self): + route_map = 'foo-bar' + route_map_seq = '10' + redistribute = ['bgp', 'connected', 'kernel', 'ripng', 'static'] + + self.cli_set(['policy', 'route-map', route_map, 'rule', route_map_seq, 'action', 'permit']) + + for protocol in redistribute: + self.cli_set(base_path + ['redistribute', protocol, 'route-map', route_map]) + + # commit changes + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf6') + self.assertIn(f'router ospf6', frrconfig) + for protocol in redistribute: + self.assertIn(f' redistribute {protocol} route-map {route_map}', frrconfig) + + def test_ospfv3_04_interfaces(self): + + self.cli_set(base_path + ['parameters', 'router-id', router_id]) + self.cli_set(base_path + ['area', default_area]) + + cost = '100' + priority = '10' + interfaces = Section.interfaces('ethernet') + for interface in interfaces: + if_base = base_path + ['interface', interface] + self.cli_set(if_base + ['bfd']) + self.cli_set(if_base + ['cost', cost]) + self.cli_set(if_base + ['instance-id', '0']) + self.cli_set(if_base + ['mtu-ignore']) + self.cli_set(if_base + ['network', 'point-to-point']) + self.cli_set(if_base + ['passive']) + self.cli_set(if_base + ['priority', priority]) + cost = str(int(cost) + 10) + priority = str(int(priority) + 5) + + # commit changes + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf6') + self.assertIn(f'router ospf6', frrconfig) + + cost = '100' + priority = '10' + for interface in interfaces: + if_config = self.getFRRconfig(f'interface {interface}') + self.assertIn(f'interface {interface}', if_config) + self.assertIn(f' ipv6 ospf6 bfd', if_config) + self.assertIn(f' ipv6 ospf6 cost {cost}', if_config) + self.assertIn(f' ipv6 ospf6 mtu-ignore', if_config) + self.assertIn(f' ipv6 ospf6 network point-to-point', if_config) + self.assertIn(f' ipv6 ospf6 passive', if_config) + self.assertIn(f' ipv6 ospf6 priority {priority}', if_config) + cost = str(int(cost) + 10) + priority = str(int(priority) + 5) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_rip.py b/smoketest/scripts/cli/test_protocols_rip.py new file mode 100755 index 000000000..3406688c5 --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_rip.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 unittest + +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSession +from vyos.ifconfig import Section +from vyos.util import process_named_running + +PROCESS_NAME = 'ripd' +acl_in = '198' +acl_out = '199' +prefix_list_in = 'foo-prefix' +prefix_list_out = 'bar-prefix' +route_map = 'FooBar123' + +base_path = ['protocols', 'rip'] + +class TestProtocolsRIP(VyOSUnitTestSHIM.TestCase): + def setUp(self): + self.cli_set(['policy', 'access-list', acl_in, 'rule', '10', 'action', 'permit']) + self.cli_set(['policy', 'access-list', acl_in, 'rule', '10', 'source', 'any']) + self.cli_set(['policy', 'access-list', acl_in, 'rule', '10', 'destination', 'any']) + self.cli_set(['policy', 'access-list', acl_out, 'rule', '20', 'action', 'deny']) + self.cli_set(['policy', 'access-list', acl_out, 'rule', '20', 'source', 'any']) + self.cli_set(['policy', 'access-list', acl_out, 'rule', '20', 'destination', 'any']) + self.cli_set(['policy', 'prefix-list', prefix_list_in, 'rule', '100', 'action', 'permit']) + self.cli_set(['policy', 'prefix-list', prefix_list_in, 'rule', '100', 'prefix', '192.0.2.0/24']) + self.cli_set(['policy', 'prefix-list', prefix_list_out, 'rule', '200', 'action', 'deny']) + self.cli_set(['policy', 'prefix-list', prefix_list_out, 'rule', '200', 'prefix', '192.0.2.0/24']) + self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) + + def tearDown(self): + self.cli_delete(base_path) + self.cli_delete(['policy', 'access-list', acl_in]) + self.cli_delete(['policy', 'access-list', acl_out]) + self.cli_delete(['policy', 'prefix-list', prefix_list_in]) + self.cli_delete(['policy', 'prefix-list', prefix_list_out]) + self.cli_delete(['policy', 'route-map', route_map]) + self.cli_commit() + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + def test_rip(self): + distance = '40' + network_distance = '66' + metric = '8' + interfaces = Section.interfaces('ethernet') + neighbors = ['1.2.3.4', '1.2.3.5', '1.2.3.6'] + networks = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'] + redistribute = ['bgp', 'connected', 'isis', 'kernel', 'ospf', 'static'] + timer_garbage = '888' + timer_timeout = '1000' + timer_update = '90' + + self.cli_set(base_path + ['default-distance', distance]) + self.cli_set(base_path + ['default-information', 'originate']) + self.cli_set(base_path + ['default-metric', metric]) + self.cli_set(base_path + ['distribute-list', 'access-list', 'in', acl_in]) + self.cli_set(base_path + ['distribute-list', 'access-list', 'out', acl_out]) + self.cli_set(base_path + ['distribute-list', 'prefix-list', 'in', prefix_list_in]) + self.cli_set(base_path + ['distribute-list', 'prefix-list', 'out', prefix_list_out]) + self.cli_set(base_path + ['passive-interface', 'default']) + self.cli_set(base_path + ['timers', 'garbage-collection', timer_garbage]) + self.cli_set(base_path + ['timers', 'timeout', timer_timeout]) + self.cli_set(base_path + ['timers', 'update', timer_update]) + for interface in interfaces: + self.cli_set(base_path + ['interface', interface]) + self.cli_set(base_path + ['distribute-list', 'interface', interface, 'access-list', 'in', acl_in]) + self.cli_set(base_path + ['distribute-list', 'interface', interface, 'access-list', 'out', acl_out]) + self.cli_set(base_path + ['distribute-list', 'interface', interface, 'prefix-list', 'in', prefix_list_in]) + self.cli_set(base_path + ['distribute-list', 'interface', interface, 'prefix-list', 'out', prefix_list_out]) + for neighbor in neighbors: + self.cli_set(base_path + ['neighbor', neighbor]) + for network in networks: + self.cli_set(base_path + ['network', network]) + self.cli_set(base_path + ['network-distance', network, 'distance', network_distance]) + self.cli_set(base_path + ['route', network]) + for proto in redistribute: + self.cli_set(base_path + ['redistribute', proto, 'metric', metric]) + self.cli_set(base_path + ['redistribute', proto, 'route-map', route_map]) + + + # commit changes + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router rip') + self.assertIn(f'router rip', frrconfig) + self.assertIn(f' distance {distance}', frrconfig) + self.assertIn(f' default-information originate', frrconfig) + self.assertIn(f' default-metric {metric}', frrconfig) + self.assertIn(f' distribute-list {acl_in} in', frrconfig) + self.assertIn(f' distribute-list {acl_out} out', frrconfig) + self.assertIn(f' distribute-list prefix {prefix_list_in} in', frrconfig) + self.assertIn(f' distribute-list prefix {prefix_list_out} out', frrconfig) + self.assertIn(f' passive-interface default', frrconfig) + self.assertIn(f' timers basic {timer_update} {timer_timeout} {timer_garbage}', frrconfig) + for interface in interfaces: + self.assertIn(f' network {interface}', frrconfig) + self.assertIn(f' distribute-list {acl_in} in {interface}', frrconfig) + self.assertIn(f' distribute-list {acl_out} out {interface}', frrconfig) + self.assertIn(f' distribute-list prefix {prefix_list_in} in {interface}', frrconfig) + self.assertIn(f' distribute-list prefix {prefix_list_out} out {interface}', frrconfig) + for neighbor in neighbors: + self.assertIn(f' neighbor {neighbor}', frrconfig) + for network in networks: + self.assertIn(f' network {network}', frrconfig) + self.assertIn(f' distance {network_distance} {network}', frrconfig) + self.assertIn(f' route {network}', frrconfig) + for proto in redistribute: + self.assertIn(f' redistribute {proto} metric {metric} route-map {route_map}', frrconfig) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_ripng.py b/smoketest/scripts/cli/test_protocols_ripng.py new file mode 100755 index 000000000..add92b73d --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_ripng.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 unittest + +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSession +from vyos.ifconfig import Section +from vyos.util import process_named_running + +PROCESS_NAME = 'ripngd' +acl_in = '198' +acl_out = '199' +prefix_list_in = 'foo-prefix' +prefix_list_out = 'bar-prefix' +route_map = 'FooBar123' + +base_path = ['protocols', 'ripng'] + +class TestProtocolsRIPng(VyOSUnitTestSHIM.TestCase): + def setUp(self): + self.cli_set(['policy', 'access-list6', acl_in, 'rule', '10', 'action', 'permit']) + self.cli_set(['policy', 'access-list6', acl_in, 'rule', '10', 'source', 'any']) + self.cli_set(['policy', 'access-list6', acl_out, 'rule', '20', 'action', 'deny']) + self.cli_set(['policy', 'access-list6', acl_out, 'rule', '20', 'source', 'any']) + self.cli_set(['policy', 'prefix-list6', prefix_list_in, 'rule', '100', 'action', 'permit']) + self.cli_set(['policy', 'prefix-list6', prefix_list_in, 'rule', '100', 'prefix', '2001:db8::/32']) + self.cli_set(['policy', 'prefix-list6', prefix_list_out, 'rule', '200', 'action', 'deny']) + self.cli_set(['policy', 'prefix-list6', prefix_list_out, 'rule', '200', 'prefix', '2001:db8::/32']) + self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) + + def tearDown(self): + self.cli_delete(base_path) + self.cli_delete(['policy', 'access-list6', acl_in]) + self.cli_delete(['policy', 'access-list6', acl_out]) + self.cli_delete(['policy', 'prefix-list6', prefix_list_in]) + self.cli_delete(['policy', 'prefix-list6', prefix_list_out]) + self.cli_delete(['policy', 'route-map', route_map]) + self.cli_commit() + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + def test_ripng(self): + metric = '8' + interfaces = Section.interfaces('ethernet') + aggregates = ['2001:db8:1000::/48', '2001:db8:2000::/48', '2001:db8:3000::/48'] + networks = ['2001:db8:1000::/64', '2001:db8:1001::/64', '2001:db8:2000::/64', '2001:db8:2001::/64'] + redistribute = ['bgp', 'connected', 'kernel', 'ospfv3', 'static'] + timer_garbage = '888' + timer_timeout = '1000' + timer_update = '90' + + self.cli_set(base_path + ['default-information', 'originate']) + self.cli_set(base_path + ['default-metric', metric]) + self.cli_set(base_path + ['distribute-list', 'access-list', 'in', acl_in]) + self.cli_set(base_path + ['distribute-list', 'access-list', 'out', acl_out]) + self.cli_set(base_path + ['distribute-list', 'prefix-list', 'in', prefix_list_in]) + self.cli_set(base_path + ['distribute-list', 'prefix-list', 'out', prefix_list_out]) + self.cli_set(base_path + ['passive-interface', 'default']) + self.cli_set(base_path + ['timers', 'garbage-collection', timer_garbage]) + self.cli_set(base_path + ['timers', 'timeout', timer_timeout]) + self.cli_set(base_path + ['timers', 'update', timer_update]) + for aggregate in aggregates: + self.cli_set(base_path + ['aggregate-address', aggregate]) + + for interface in interfaces: + self.cli_set(base_path + ['interface', interface]) + self.cli_set(base_path + ['distribute-list', 'interface', interface, 'access-list', 'in', acl_in]) + self.cli_set(base_path + ['distribute-list', 'interface', interface, 'access-list', 'out', acl_out]) + self.cli_set(base_path + ['distribute-list', 'interface', interface, 'prefix-list', 'in', prefix_list_in]) + self.cli_set(base_path + ['distribute-list', 'interface', interface, 'prefix-list', 'out', prefix_list_out]) + for network in networks: + self.cli_set(base_path + ['network', network]) + self.cli_set(base_path + ['route', network]) + for proto in redistribute: + self.cli_set(base_path + ['redistribute', proto, 'metric', metric]) + self.cli_set(base_path + ['redistribute', proto, 'route-map', route_map]) + + + # commit changes + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ripng') + self.assertIn(f'router ripng', frrconfig) + self.assertIn(f' default-information originate', frrconfig) + self.assertIn(f' default-metric {metric}', frrconfig) + self.assertIn(f' ipv6 distribute-list {acl_in} in', frrconfig) + self.assertIn(f' ipv6 distribute-list {acl_out} out', frrconfig) + self.assertIn(f' ipv6 distribute-list prefix {prefix_list_in} in', frrconfig) + self.assertIn(f' ipv6 distribute-list prefix {prefix_list_out} out', frrconfig) + self.assertIn(f' passive-interface default', frrconfig) + self.assertIn(f' timers basic {timer_update} {timer_timeout} {timer_garbage}', frrconfig) + for aggregate in aggregates: + self.assertIn(f' aggregate-address {aggregate}', frrconfig) + for interface in interfaces: + self.assertIn(f' network {interface}', frrconfig) + self.assertIn(f' ipv6 distribute-list {acl_in} in {interface}', frrconfig) + self.assertIn(f' ipv6 distribute-list {acl_out} out {interface}', frrconfig) + self.assertIn(f' ipv6 distribute-list prefix {prefix_list_in} in {interface}', frrconfig) + self.assertIn(f' ipv6 distribute-list prefix {prefix_list_out} out {interface}', frrconfig) + for network in networks: + self.assertIn(f' network {network}', frrconfig) + self.assertIn(f' route {network}', frrconfig) + for proto in redistribute: + if proto == 'ospfv3': + proto = 'ospf6' + self.assertIn(f' redistribute {proto} metric {metric} route-map {route_map}', frrconfig) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_rpki.py b/smoketest/scripts/cli/test_protocols_rpki.py new file mode 100755 index 000000000..8212e9469 --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_rpki.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import unittest + +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.util import cmd +from vyos.util import process_named_running + +base_path = ['protocols', 'rpki'] +PROCESS_NAME = 'bgpd' + +rpki_known_hosts = '/config/auth/known_hosts' +rpki_ssh_key = '/config/auth/id_rsa_rpki' +rpki_ssh_pub = f'{rpki_ssh_key}.pub' + +class TestProtocolsRPKI(VyOSUnitTestSHIM.TestCase): + def tearDown(self): + self.cli_delete(base_path) + self.cli_commit() + + # Nothing RPKI specific should be left over in the config + # + # Disabled until T3266 is resolved + # frrconfig = self.getFRRconfig('rpki') + # self.assertNotIn('rpki', frrconfig) + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + def test_rpki(self): + polling = '7200' + cache = { + '192.0.2.1' : { + 'port' : '8080', + 'preference' : '1' + }, + '192.0.2.2' : { + 'port' : '9090', + 'preference' : '2' + }, + '2001:db8::1' : { + 'port' : '1234', + 'preference' : '3' + }, + '2001:db8::2' : { + 'port' : '5678', + 'preference' : '4' + }, + } + + self.cli_set(base_path + ['polling-period', polling]) + for peer, peer_config in cache.items(): + self.cli_set(base_path + ['cache', peer, 'port', peer_config['port']]) + self.cli_set(base_path + ['cache', peer, 'preference', peer_config['preference']]) + + # commit changes + self.cli_commit() + + # Verify FRR configuration + frrconfig = self.getFRRconfig('rpki') + self.assertIn(f'rpki polling_period {polling}', frrconfig) + + for peer, peer_config in cache.items(): + port = peer_config['port'] + preference = peer_config['preference'] + self.assertIn(f'rpki cache {peer} {port} preference {preference}', frrconfig) + + def test_rpki_ssh(self): + polling = '7200' + cache = { + '192.0.2.3' : { + 'port' : '1234', + 'username' : 'foo', + 'preference' : '10' + }, + '192.0.2.4' : { + 'port' : '5678', + 'username' : 'bar', + 'preference' : '20' + }, + } + + self.cli_set(base_path + ['polling-period', polling]) + + for peer, peer_config in cache.items(): + self.cli_set(base_path + ['cache', peer, 'port', peer_config['port']]) + self.cli_set(base_path + ['cache', peer, 'preference', peer_config['preference']]) + self.cli_set(base_path + ['cache', peer, 'ssh', 'username', peer_config['username']]) + self.cli_set(base_path + ['cache', peer, 'ssh', 'public-key-file', rpki_ssh_pub]) + self.cli_set(base_path + ['cache', peer, 'ssh', 'private-key-file', rpki_ssh_key]) + self.cli_set(base_path + ['cache', peer, 'ssh', 'known-hosts-file', rpki_known_hosts]) + + # commit changes + self.cli_commit() + + # Verify FRR configuration + frrconfig = self.getFRRconfig('rpki') + self.assertIn(f'rpki polling_period {polling}', frrconfig) + + for peer, peer_config in cache.items(): + port = peer_config['port'] + preference = peer_config['preference'] + username = peer_config['username'] + self.assertIn(f'rpki cache {peer} {port} {username} {rpki_ssh_key} {rpki_known_hosts} preference {preference}', frrconfig) + + + def test_rpki_verify_preference(self): + cache = { + '192.0.2.1' : { + 'port' : '8080', + 'preference' : '1' + }, + '192.0.2.2' : { + 'port' : '9090', + 'preference' : '1' + }, + } + + for peer, peer_config in cache.items(): + self.cli_set(base_path + ['cache', peer, 'port', peer_config['port']]) + self.cli_set(base_path + ['cache', peer, 'preference', peer_config['preference']]) + + # check validate() - preferences must be unique + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + +if __name__ == '__main__': + # Create OpenSSH keypair used in RPKI tests + if not os.path.isfile(rpki_ssh_key): + cmd(f'ssh-keygen -t rsa -f {rpki_ssh_key} -N ""') + + if not os.path.isfile(rpki_known_hosts): + cmd(f'touch {rpki_known_hosts}') + + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_static.py b/smoketest/scripts/cli/test_protocols_static.py new file mode 100755 index 000000000..75d3e6a42 --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_static.py @@ -0,0 +1,396 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 unittest + +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.template import is_ipv6 +from vyos.util import get_interface_config + +base_path = ['protocols', 'static'] +vrf_path = ['protocols', 'vrf'] + +routes = { + '10.0.0.0/8' : { + 'next_hop' : { + '192.0.2.100' : { 'distance' : '100' }, + '192.0.2.110' : { 'distance' : '110', 'interface' : 'eth0' }, + '192.0.2.120' : { 'distance' : '120', 'disable' : '' }, + }, + 'interface' : { + 'eth0' : { 'distance' : '130' }, + 'eth1' : { 'distance' : '140' }, + }, + 'blackhole' : { 'distance' : '250', 'tag' : '500' }, + }, + '172.16.0.0/12' : { + 'interface' : { + 'eth0' : { 'distance' : '50', 'vrf' : 'black' }, + 'eth1' : { 'distance' : '60', 'vrf' : 'black' }, + }, + 'blackhole' : { 'distance' : '90' }, + }, + '192.0.2.0/24' : { + 'interface' : { + 'eth0' : { 'distance' : '50', 'vrf' : 'black' }, + 'eth1' : { 'disable' : '' }, + }, + 'blackhole' : { 'distance' : '90' }, + }, + '100.64.0.0/10' : { + 'blackhole' : { }, + }, + '2001:db8:100::/40' : { + 'next_hop' : { + '2001:db8::1' : { 'distance' : '10' }, + '2001:db8::2' : { 'distance' : '20', 'interface' : 'eth0' }, + '2001:db8::3' : { 'distance' : '30', 'disable' : '' }, + }, + 'interface' : { + 'eth0' : { 'distance' : '40', 'vrf' : 'black' }, + 'eth1' : { 'distance' : '50', 'disable' : '' }, + }, + 'blackhole' : { 'distance' : '250', 'tag' : '500' }, + }, + '2001:db8:200::/40' : { + 'interface' : { + 'eth0' : { 'distance' : '40' }, + 'eth1' : { 'distance' : '50', 'disable' : '' }, + }, + 'blackhole' : { 'distance' : '250', 'tag' : '500' }, + }, + '2001:db8::/32' : { + 'blackhole' : { 'distance' : '200', 'tag' : '600' }, + }, +} + +tables = ['80', '81', '82'] + +class StaticRouteTest(VyOSUnitTestSHIM.TestCase): + def setUp(self): + # This is our "target" VRF when leaking routes: + self.cli_set(['vrf', 'name', 'black', 'table', '43210']) + + def tearDown(self): + for route, route_config in routes.items(): + route_type = 'route' + if is_ipv6(route): + route_type = 'route6' + self.cli_delete(base_path + [route_type, route]) + + for table in tables: + self.cli_delete(base_path + ['table', table]) + + tmp = self.getFRRconfig('', end='') + self.cli_commit() + + def test_protocols_static(self): + for route, route_config in routes.items(): + route_type = 'route' + if is_ipv6(route): + route_type = 'route6' + base = base_path + [route_type, route] + if 'next_hop' in route_config: + for next_hop, next_hop_config in route_config['next_hop'].items(): + self.cli_set(base + ['next-hop', next_hop]) + if 'disable' in next_hop_config: + self.cli_set(base + ['next-hop', next_hop, 'disable']) + if 'distance' in next_hop_config: + self.cli_set(base + ['next-hop', next_hop, 'distance', next_hop_config['distance']]) + if 'interface' in next_hop_config: + self.cli_set(base + ['next-hop', next_hop, 'interface', next_hop_config['interface']]) + if 'vrf' in next_hop_config: + self.cli_set(base + ['next-hop', next_hop, 'vrf', next_hop_config['vrf']]) + + + if 'interface' in route_config: + for interface, interface_config in route_config['interface'].items(): + self.cli_set(base + ['interface', interface]) + if 'disable' in interface_config: + self.cli_set(base + ['interface', interface, 'disable']) + if 'distance' in interface_config: + self.cli_set(base + ['interface', interface, 'distance', interface_config['distance']]) + if 'vrf' in interface_config: + self.cli_set(base + ['interface', interface, 'vrf', interface_config['vrf']]) + + if 'blackhole' in route_config: + self.cli_set(base + ['blackhole']) + if 'distance' in route_config['blackhole']: + self.cli_set(base + ['blackhole', 'distance', route_config['blackhole']['distance']]) + if 'tag' in route_config['blackhole']: + self.cli_set(base + ['blackhole', 'tag', route_config['blackhole']['tag']]) + + # commit changes + self.cli_commit() + + # Verify FRR bgpd configuration + frrconfig = self.getFRRconfig('ip route', end='') + + # Verify routes + for route, route_config in routes.items(): + ip_ipv6 = 'ip' + if is_ipv6(route): + ip_ipv6 = 'ipv6' + + if 'next_hop' in route_config: + for next_hop, next_hop_config in route_config['next_hop'].items(): + tmp = f'{ip_ipv6} route {route} {next_hop}' + if 'interface' in next_hop_config: + tmp += ' ' + next_hop_config['interface'] + if 'distance' in next_hop_config: + tmp += ' ' + next_hop_config['distance'] + if 'vrf' in next_hop_config: + tmp += ' nexthop-vrf ' + next_hop_config['vrf'] + + if 'disable' in next_hop_config: + self.assertNotIn(tmp, frrconfig) + else: + self.assertIn(tmp, frrconfig) + + if 'interface' in route_config: + for interface, interface_config in route_config['interface'].items(): + tmp = f'{ip_ipv6} route {route} {interface}' + if 'interface' in interface_config: + tmp += ' ' + interface_config['interface'] + if 'distance' in interface_config: + tmp += ' ' + interface_config['distance'] + if 'vrf' in interface_config: + tmp += ' nexthop-vrf ' + interface_config['vrf'] + + if 'disable' in interface_config: + self.assertNotIn(tmp, frrconfig) + else: + self.assertIn(tmp, frrconfig) + + if 'blackhole' in route_config: + tmp = f'{ip_ipv6} route {route} blackhole' + if 'tag' in route_config['blackhole']: + tmp += ' tag ' + route_config['blackhole']['tag'] + if 'distance' in route_config['blackhole']: + tmp += ' ' + route_config['blackhole']['distance'] + + self.assertIn(tmp, frrconfig) + + def test_protocols_static_table(self): + for table in tables: + for route, route_config in routes.items(): + route_type = 'route' + if is_ipv6(route): + route_type = 'route6' + base = base_path + ['table', table, route_type, route] + + if 'next_hop' in route_config: + for next_hop, next_hop_config in route_config['next_hop'].items(): + self.cli_set(base + ['next-hop', next_hop]) + if 'disable' in next_hop_config: + self.cli_set(base + ['next-hop', next_hop, 'disable']) + if 'distance' in next_hop_config: + self.cli_set(base + ['next-hop', next_hop, 'distance', next_hop_config['distance']]) + if 'interface' in next_hop_config: + self.cli_set(base + ['next-hop', next_hop, 'interface', next_hop_config['interface']]) + if 'vrf' in next_hop_config: + self.cli_set(base + ['next-hop', next_hop, 'vrf', next_hop_config['vrf']]) + + + if 'interface' in route_config: + for interface, interface_config in route_config['interface'].items(): + self.cli_set(base + ['interface', interface]) + if 'disable' in interface_config: + self.cli_set(base + ['interface', interface, 'disable']) + if 'distance' in interface_config: + self.cli_set(base + ['interface', interface, 'distance', interface_config['distance']]) + if 'vrf' in interface_config: + self.cli_set(base + ['interface', interface, 'vrf', interface_config['vrf']]) + + if 'blackhole' in route_config: + self.cli_set(base + ['blackhole']) + if 'distance' in route_config['blackhole']: + self.cli_set(base + ['blackhole', 'distance', route_config['blackhole']['distance']]) + if 'tag' in route_config['blackhole']: + self.cli_set(base + ['blackhole', 'tag', route_config['blackhole']['tag']]) + + # commit changes + self.cli_commit() + + # Verify FRR bgpd configuration + frrconfig = self.getFRRconfig('ip route', end='') + + for table in tables: + # Verify routes + for route, route_config in routes.items(): + ip_ipv6 = 'ip' + if is_ipv6(route): + ip_ipv6 = 'ipv6' + + if 'next_hop' in route_config: + for next_hop, next_hop_config in route_config['next_hop'].items(): + tmp = f'{ip_ipv6} route {route} {next_hop}' + if 'interface' in next_hop_config: + tmp += ' ' + next_hop_config['interface'] + if 'distance' in next_hop_config: + tmp += ' ' + next_hop_config['distance'] + if 'vrf' in next_hop_config: + tmp += ' nexthop-vrf ' + next_hop_config['vrf'] + + tmp += ' table ' + table + if 'disable' in next_hop_config: + self.assertNotIn(tmp, frrconfig) + else: + self.assertIn(tmp, frrconfig) + + if 'interface' in route_config: + for interface, interface_config in route_config['interface'].items(): + tmp = f'{ip_ipv6} route {route} {interface}' + if 'interface' in interface_config: + tmp += ' ' + interface_config['interface'] + if 'distance' in interface_config: + tmp += ' ' + interface_config['distance'] + if 'vrf' in interface_config: + tmp += ' nexthop-vrf ' + interface_config['vrf'] + + tmp += ' table ' + table + if 'disable' in interface_config: + self.assertNotIn(tmp, frrconfig) + else: + self.assertIn(tmp, frrconfig) + + if 'blackhole' in route_config: + tmp = f'{ip_ipv6} route {route} blackhole' + if 'tag' in route_config['blackhole']: + tmp += ' tag ' + route_config['blackhole']['tag'] + if 'distance' in route_config['blackhole']: + tmp += ' ' + route_config['blackhole']['distance'] + + tmp += ' table ' + table + self.assertIn(tmp, frrconfig) + + + def test_protocols_vrf_static(self): + # Create VRF instances and apply the static routes from above to FRR. + # Re-read the configured routes and match them if they are programmed + # properly. This also includes VRF leaking + vrfs = { + 'red' : { 'table' : '1000' }, + 'green' : { 'table' : '2000' }, + 'blue' : { 'table' : '3000' }, + } + + for vrf, vrf_config in vrfs.items(): + vrf_base_path = ['vrf', 'name', vrf] + self.cli_set(vrf_base_path + ['table', vrf_config['table']]) + + for route, route_config in routes.items(): + route_type = 'route' + if is_ipv6(route): + route_type = 'route6' + route_base_path = vrf_base_path + ['protocols', 'static', route_type, route] + + if 'next_hop' in route_config: + for next_hop, next_hop_config in route_config['next_hop'].items(): + self.cli_set(route_base_path + ['next-hop', next_hop]) + if 'disable' in next_hop_config: + self.cli_set(route_base_path + ['next-hop', next_hop, 'disable']) + if 'distance' in next_hop_config: + self.cli_set(route_base_path + ['next-hop', next_hop, 'distance', next_hop_config['distance']]) + if 'interface' in next_hop_config: + self.cli_set(route_base_path + ['next-hop', next_hop, 'interface', next_hop_config['interface']]) + if 'vrf' in next_hop_config: + self.cli_set(route_base_path + ['next-hop', next_hop, 'vrf', next_hop_config['vrf']]) + + + if 'interface' in route_config: + for interface, interface_config in route_config['interface'].items(): + self.cli_set(route_base_path + ['interface', interface]) + if 'disable' in interface_config: + self.cli_set(route_base_path + ['interface', interface, 'disable']) + if 'distance' in interface_config: + self.cli_set(route_base_path + ['interface', interface, 'distance', interface_config['distance']]) + if 'vrf' in interface_config: + self.cli_set(route_base_path + ['interface', interface, 'vrf', interface_config['vrf']]) + + if 'blackhole' in route_config: + self.cli_set(route_base_path + ['blackhole']) + if 'distance' in route_config['blackhole']: + self.cli_set(route_base_path + ['blackhole', 'distance', route_config['blackhole']['distance']]) + if 'tag' in route_config['blackhole']: + self.cli_set(route_base_path + ['blackhole', 'tag', route_config['blackhole']['tag']]) + + # commit changes + self.cli_commit() + + for vrf, vrf_config in vrfs.items(): + tmp = get_interface_config(vrf) + + # Compare VRF table ID + self.assertEqual(tmp['linkinfo']['info_data']['table'], int(vrf_config['table'])) + self.assertEqual(tmp['linkinfo']['info_kind'], 'vrf') + + # Verify FRR bgpd configuration + frrconfig = self.getFRRconfig(f'vrf {vrf}') + self.assertIn(f'vrf {vrf}', frrconfig) + + # Verify routes + for route, route_config in routes.items(): + ip_ipv6 = 'ip' + if is_ipv6(route): + ip_ipv6 = 'ipv6' + + if 'next_hop' in route_config: + for next_hop, next_hop_config in route_config['next_hop'].items(): + tmp = f'{ip_ipv6} route {route} {next_hop}' + if 'interface' in next_hop_config: + tmp += ' ' + next_hop_config['interface'] + if 'distance' in next_hop_config: + tmp += ' ' + next_hop_config['distance'] + if 'vrf' in next_hop_config: + tmp += ' nexthop-vrf ' + next_hop_config['vrf'] + + if 'disable' in next_hop_config: + self.assertNotIn(tmp, frrconfig) + else: + self.assertIn(tmp, frrconfig) + + if 'interface' in route_config: + for interface, interface_config in route_config['interface'].items(): + tmp = f'{ip_ipv6} route {route} {interface}' + if 'interface' in interface_config: + tmp += ' ' + interface_config['interface'] + if 'distance' in interface_config: + tmp += ' ' + interface_config['distance'] + if 'vrf' in interface_config: + tmp += ' nexthop-vrf ' + interface_config['vrf'] + + if 'disable' in interface_config: + self.assertNotIn(tmp, frrconfig) + else: + self.assertIn(tmp, frrconfig) + + if 'blackhole' in route_config: + tmp = f'{ip_ipv6} route {route} blackhole' + if 'tag' in route_config['blackhole']: + tmp += ' tag ' + route_config['blackhole']['tag'] + if 'distance' in route_config['blackhole']: + tmp += ' ' + route_config['blackhole']['distance'] + + self.assertIn(tmp, frrconfig) + + self.cli_delete(['vrf']) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_bcast-relay.py b/smoketest/scripts/cli/test_service_bcast-relay.py index c28509714..58b730ab4 100755 --- a/smoketest/scripts/cli/test_service_bcast-relay.py +++ b/smoketest/scripts/cli/test_service_bcast-relay.py @@ -14,47 +14,46 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import unittest +from base_vyostest_shim import VyOSUnitTestSHIM + from psutil import process_iter -from vyos.configsession import ConfigSession, ConfigSessionError +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError base_path = ['service', 'broadcast-relay'] -class TestServiceBroadcastRelay(unittest.TestCase): +class TestServiceBroadcastRelay(VyOSUnitTestSHIM.TestCase): _address1 = '192.0.2.1/24' _address2 = '192.0.2.1/24' def setUp(self): - self.session = ConfigSession(os.getpid()) - self.session.set(['interfaces', 'dummy', 'dum1001', 'address', self._address1]) - self.session.set(['interfaces', 'dummy', 'dum1002', 'address', self._address2]) - self.session.commit() + self.cli_set(['interfaces', 'dummy', 'dum1001', 'address', self._address1]) + self.cli_set(['interfaces', 'dummy', 'dum1002', 'address', self._address2]) def tearDown(self): - self.session.delete(['interfaces', 'dummy', 'dum1001']) - self.session.delete(['interfaces', 'dummy', 'dum1002']) - self.session.delete(base_path) - self.session.commit() - del self.session + self.cli_delete(['interfaces', 'dummy', 'dum1001']) + self.cli_delete(['interfaces', 'dummy', 'dum1002']) + self.cli_delete(base_path) + self.cli_commit() def test_broadcast_relay_service(self): ids = range(1, 5) for id in ids: base = base_path + ['id', str(id)] - self.session.set(base + ['description', 'vyos']) - self.session.set(base + ['port', str(10000 + id)]) + self.cli_set(base + ['description', 'vyos']) + self.cli_set(base + ['port', str(10000 + id)]) # check validate() - two interfaces must be present with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() - self.session.set(base + ['interface', 'dum1001']) - self.session.set(base + ['interface', 'dum1002']) - self.session.set(base + ['address', self._address1.split('/')[0]]) + self.cli_set(base + ['interface', 'dum1001']) + self.cli_set(base + ['interface', 'dum1002']) + self.cli_set(base + ['address', self._address1.split('/')[0]]) - self.session.commit() + self.cli_commit() for id in ids: # check if process is running diff --git a/smoketest/scripts/cli/test_service_dhcp-relay.py b/smoketest/scripts/cli/test_service_dhcp-relay.py index 676c4a481..db2edba54 100755 --- a/smoketest/scripts/cli/test_service_dhcp-relay.py +++ b/smoketest/scripts/cli/test_service_dhcp-relay.py @@ -14,14 +14,13 @@ # 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 base_vyostest_shim import VyOSUnitTestSHIM + from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError from vyos.ifconfig import Section -from vyos.util import cmd from vyos.util import process_named_running from vyos.util import read_file @@ -29,14 +28,10 @@ PROCESS_NAME = 'dhcrelay' RELAY_CONF = '/run/dhcp-relay/dhcrelay.conf' base_path = ['service', 'dhcp-relay'] -class TestServiceDHCPRelay(unittest.TestCase): - def setUp(self): - self.session = ConfigSession(os.getpid()) - +class TestServiceDHCPRelay(VyOSUnitTestSHIM.TestCase): def tearDown(self): - self.session.delete(base_path) - self.session.commit() - del self.session + self.cli_delete(base_path) + self.cli_commit() def test_relay_default(self): max_size = '800' @@ -44,28 +39,28 @@ class TestServiceDHCPRelay(unittest.TestCase): agents_packets = 'append' servers = ['192.0.2.1', '192.0.2.2'] - self.session.set(base_path + ['interface', 'lo']) + self.cli_set(base_path + ['interface', 'lo']) # check validate() - DHCP relay does not support the loopback interface with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(base_path + ['interface', 'lo']) + self.cli_commit() + self.cli_delete(base_path + ['interface', 'lo']) # activate DHCP relay on all ethernet interfaces for tmp in Section.interfaces("ethernet"): - self.session.set(base_path + ['interface', tmp]) + self.cli_set(base_path + ['interface', tmp]) # check validate() - No DHCP relay server(s) configured with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() for server in servers: - self.session.set(base_path + ['server', server]) + self.cli_set(base_path + ['server', server]) - self.session.set(base_path + ['relay-options', 'max-size', max_size]) - self.session.set(base_path + ['relay-options', 'hop-count', hop_count]) - self.session.set(base_path + ['relay-options', 'relay-agents-packets', agents_packets]) + self.cli_set(base_path + ['relay-options', 'max-size', max_size]) + self.cli_set(base_path + ['relay-options', 'hop-count', hop_count]) + self.cli_set(base_path + ['relay-options', 'relay-agents-packets', agents_packets]) # commit changes - self.session.commit() + self.cli_commit() # Check configured port config = read_file(RELAY_CONF) diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py index db7b2dda4..d3f6f21f1 100755 --- a/smoketest/scripts/cli/test_service_dhcp-server.py +++ b/smoketest/scripts/cli/test_service_dhcp-server.py @@ -14,13 +14,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import re -import os import unittest +from base_vyostest_shim import VyOSUnitTestSHIM + 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 address_from_cidr @@ -37,17 +36,15 @@ dns_1 = inc_ip(subnet, 2) dns_2 = inc_ip(subnet, 3) domain_name = 'vyos.net' -class TestServiceDHCPServer(unittest.TestCase): +class TestServiceDHCPServer(VyOSUnitTestSHIM.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}']) + self.cli_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 + self.cli_delete(['interfaces', 'dummy', 'dum8765']) + self.cli_delete(base_path) + self.cli_commit() def test_dhcp_single_pool_range(self): shared_net_name = 'SMOKE-1' @@ -57,25 +54,25 @@ class TestServiceDHCPServer(unittest.TestCase): range_1_start = inc_ip(subnet, 40) range_1_stop = inc_ip(subnet, 50) - self.session.set(base_path + ['dynamic-dns-update']) + self.cli_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]) + self.cli_set(pool + ['default-router', router]) + self.cli_set(pool + ['dns-server', dns_1]) + self.cli_set(pool + ['dns-server', dns_2]) + self.cli_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]) + self.cli_commit() + self.cli_set(pool + ['range', '0', 'start', range_0_start]) + self.cli_set(pool + ['range', '0', 'stop', range_0_stop]) + self.cli_set(pool + ['range', '1', 'start', range_1_start]) + self.cli_set(pool + ['range', '1', 'stop', range_1_stop]) # commit changes - self.session.commit() + self.cli_commit() config = read_file(DHCPD_CONF) network = address_from_cidr(subnet) @@ -110,42 +107,42 @@ class TestServiceDHCPServer(unittest.TestCase): 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.cli_set(pool + ['default-router', router]) + self.cli_set(pool + ['dns-server', dns_1]) + self.cli_set(pool + ['dns-server', dns_2]) + self.cli_set(pool + ['domain-name', domain_name]) + self.cli_set(pool + ['ip-forwarding']) + self.cli_set(pool + ['smtp-server', smtp_server]) + self.cli_set(pool + ['pop-server', smtp_server]) + self.cli_set(pool + ['time-server', time_server]) + self.cli_set(pool + ['tftp-server-name', tftp_server]) for search in search_domains: - self.session.set(pool + ['domain-search', search]) - 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.cli_set(pool + ['domain-search', search]) + self.cli_set(pool + ['bootfile-name', bootfile_name]) + self.cli_set(pool + ['bootfile-server', bootfile_server]) + self.cli_set(pool + ['wpad-url', wpad]) + self.cli_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']) + self.cli_set(pool + ['static-route', 'destination-subnet', '10.0.0.0/24']) + self.cli_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]) + self.cli_commit() + self.cli_set(pool + ['range', '0', 'start', range_0_start]) + self.cli_set(pool + ['range', '0', 'stop', range_0_stop]) # failover failover_local = router failover_remote = inc_ip(router, 1) - self.session.set(pool + ['failover', 'local-address', failover_local]) - self.session.set(pool + ['failover', 'name', shared_net_name]) - self.session.set(pool + ['failover', 'peer-address', failover_remote]) - self.session.set(pool + ['failover', 'status', 'primary']) + self.cli_set(pool + ['failover', 'local-address', failover_local]) + self.cli_set(pool + ['failover', 'name', shared_net_name]) + self.cli_set(pool + ['failover', 'peer-address', failover_remote]) + self.cli_set(pool + ['failover', 'status', 'primary']) # commit changes - self.session.commit() + self.cli_commit() config = read_file(DHCPD_CONF) @@ -200,27 +197,28 @@ class TestServiceDHCPServer(unittest.TestCase): def test_dhcp_single_pool_static_mapping(self): shared_net_name = 'SMOKE-2' + domain_name = 'private' 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.cli_set(pool + ['default-router', router]) + self.cli_set(pool + ['dns-server', dns_1]) + self.cli_set(pool + ['dns-server', dns_2]) + self.cli_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.cli_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)]) + self.cli_set(pool + ['static-mapping', client, 'mac-address', mac]) + self.cli_set(pool + ['static-mapping', client, 'ip-address', inc_ip(subnet, client_base)]) client_base += 1 # commit changes - self.session.commit() + self.cli_commit() config = read_file(DHCPD_CONF) network = address_from_cidr(subnet) @@ -263,25 +261,25 @@ class TestServiceDHCPServer(unittest.TestCase): 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.cli_set(pool + ['default-router', router]) + self.cli_set(pool + ['dns-server', dns_1]) + self.cli_set(pool + ['domain-name', domain_name]) + self.cli_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]) + self.cli_set(pool + ['range', '0', 'start', range_0_start]) + self.cli_set(pool + ['range', '0', 'stop', range_0_stop]) + self.cli_set(pool + ['range', '1', 'start', range_1_start]) + self.cli_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)]) + self.cli_set(pool + ['static-mapping', client, 'mac-address', mac]) + self.cli_set(pool + ['static-mapping', client, 'ip-address', inc_ip(subnet, client_base)]) client_base += 1 # commit changes - self.session.commit() + self.cli_commit() config = read_file(DHCPD_CONF) for network in ['0', '1', '2', '3']: @@ -328,13 +326,13 @@ class TestServiceDHCPServer(unittest.TestCase): range_0_stop = inc_ip(subnet, 20) pool = base_path + ['shared-network-name', 'EXCLUDE-TEST', 'subnet', subnet] - self.session.set(pool + ['default-router', router]) - self.session.set(pool + ['exclude', router]) - self.session.set(pool + ['range', '0', 'start', range_0_start]) - self.session.set(pool + ['range', '0', 'stop', range_0_stop]) + self.cli_set(pool + ['default-router', router]) + self.cli_set(pool + ['exclude', router]) + self.cli_set(pool + ['range', '0', 'start', range_0_start]) + self.cli_set(pool + ['range', '0', 'stop', range_0_stop]) # commit changes - self.session.commit() + self.cli_commit() # VErify config = read_file(DHCPD_CONF) @@ -361,13 +359,13 @@ class TestServiceDHCPServer(unittest.TestCase): range_0_start_excl = inc_ip(exclude_addr, 1) pool = base_path + ['shared-network-name', 'EXCLUDE-TEST-2', 'subnet', subnet] - self.session.set(pool + ['default-router', router]) - self.session.set(pool + ['exclude', exclude_addr]) - self.session.set(pool + ['range', '0', 'start', range_0_start]) - self.session.set(pool + ['range', '0', 'stop', range_0_stop]) + self.cli_set(pool + ['default-router', router]) + self.cli_set(pool + ['exclude', exclude_addr]) + self.cli_set(pool + ['range', '0', 'start', range_0_start]) + self.cli_set(pool + ['range', '0', 'stop', range_0_stop]) # commit changes - self.session.commit() + self.cli_commit() # Verify config = read_file(DHCPD_CONF) @@ -385,7 +383,7 @@ class TestServiceDHCPServer(unittest.TestCase): def test_dhcp_relay_server(self): # Listen on specific address and return DHCP leases from a non # directly connected pool - self.session.set(base_path + ['listen-address', router]) + self.cli_set(base_path + ['listen-address', router]) relay_subnet = '10.0.0.0/16' relay_router = inc_ip(relay_subnet, 1) @@ -394,12 +392,12 @@ class TestServiceDHCPServer(unittest.TestCase): range_0_stop = '10.0.250.255' pool = base_path + ['shared-network-name', 'RELAY', 'subnet', relay_subnet] - self.session.set(pool + ['default-router', relay_router]) - self.session.set(pool + ['range', '0', 'start', range_0_start]) - self.session.set(pool + ['range', '0', 'stop', range_0_stop]) + self.cli_set(pool + ['default-router', relay_router]) + self.cli_set(pool + ['range', '0', 'start', range_0_start]) + self.cli_set(pool + ['range', '0', 'stop', range_0_stop]) # commit changes - self.session.commit() + self.cli_commit() config = read_file(DHCPD_CONF) network = address_from_cidr(subnet) diff --git a/smoketest/scripts/cli/test_service_dhcpv6-relay.py b/smoketest/scripts/cli/test_service_dhcpv6-relay.py index e36c237bc..5a9dd1aa6 100755 --- a/smoketest/scripts/cli/test_service_dhcpv6-relay.py +++ b/smoketest/scripts/cli/test_service_dhcpv6-relay.py @@ -14,15 +14,14 @@ # 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 base_vyostest_shim import VyOSUnitTestSHIM + 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 @@ -35,52 +34,50 @@ upstream_if_addr = '2001:db8::1/64' listen_addr = '2001:db8:ffff::1/64' interfaces = [] -class TestServiceDHCPv6Relay(unittest.TestCase): +class TestServiceDHCPv6Relay(VyOSUnitTestSHIM.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]) + self.cli_set(['interfaces', 'ethernet', tmp, 'address', listen]) def tearDown(self): - self.session.delete(base_path) + self.cli_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.cli_delete(['interfaces', 'ethernet', tmp, 'address', listen]) - self.session.commit() - del self.session + self.cli_commit() 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]) + self.cli_set(base_path + ['use-interface-id-option']) + self.cli_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]) + self.cli_commit() + self.cli_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() + self.cli_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]]) + self.cli_set(base_path + ['listen-interface', tmp, 'address', listen_addr.split('/')[0]]) # commit changes - self.session.commit() + self.cli_commit() # Check configured port config = read_file(RELAY_CONF) diff --git a/smoketest/scripts/cli/test_service_dhcpv6-server.py b/smoketest/scripts/cli/test_service_dhcpv6-server.py index 319891a94..e85a055c7 100755 --- a/smoketest/scripts/cli/test_service_dhcpv6-server.py +++ b/smoketest/scripts/cli/test_service_dhcpv6-server.py @@ -14,14 +14,13 @@ # 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 base_vyostest_shim import VyOSUnitTestSHIM + from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError from vyos.template import inc_ip -from vyos.util import cmd from vyos.util import process_named_running from vyos.util import read_file @@ -37,16 +36,14 @@ nis_servers = ['2001:db8:ffff::1', '2001:db8:ffff::2'] interface = 'eth1' interface_addr = inc_ip(subnet, 1) + '/64' -class TestServiceDHCPServer(unittest.TestCase): +class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): def setUp(self): - self.session = ConfigSession(os.getpid()) - self.session.set(['interfaces', 'ethernet', interface, 'address', interface_addr]) + self.cli_set(['interfaces', 'ethernet', interface, 'address', interface_addr]) def tearDown(self): - self.session.delete(base_path) - self.session.delete(['interfaces', 'ethernet', interface, 'address', interface_addr]) - self.session.commit() - del self.session + self.cli_delete(base_path) + self.cli_delete(['interfaces', 'ethernet', interface, 'address', interface_addr]) + self.cli_commit() def test_single_pool(self): shared_net_name = 'SMOKE-1' @@ -62,37 +59,37 @@ class TestServiceDHCPServer(unittest.TestCase): pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet] - self.session.set(base_path + ['preference', preference]) + self.cli_set(base_path + ['preference', preference]) # we use the first subnet IP address as default gateway - self.session.set(pool + ['name-server', dns_1]) - self.session.set(pool + ['name-server', dns_2]) - self.session.set(pool + ['name-server', dns_2]) - self.session.set(pool + ['lease-time', 'default', lease_time]) - self.session.set(pool + ['lease-time', 'maximum', max_lease_time]) - self.session.set(pool + ['lease-time', 'minimum', min_lease_time]) - self.session.set(pool + ['nis-domain', domain]) - self.session.set(pool + ['nisplus-domain', domain]) - self.session.set(pool + ['sip-server', sip_server]) - self.session.set(pool + ['sntp-server', sntp_server]) - self.session.set(pool + ['address-range', 'start', range_start, 'stop', range_stop]) + self.cli_set(pool + ['name-server', dns_1]) + self.cli_set(pool + ['name-server', dns_2]) + self.cli_set(pool + ['name-server', dns_2]) + self.cli_set(pool + ['lease-time', 'default', lease_time]) + self.cli_set(pool + ['lease-time', 'maximum', max_lease_time]) + self.cli_set(pool + ['lease-time', 'minimum', min_lease_time]) + self.cli_set(pool + ['nis-domain', domain]) + self.cli_set(pool + ['nisplus-domain', domain]) + self.cli_set(pool + ['sip-server', sip_server]) + self.cli_set(pool + ['sntp-server', sntp_server]) + self.cli_set(pool + ['address-range', 'start', range_start, 'stop', range_stop]) for server in nis_servers: - self.session.set(pool + ['nis-server', server]) - self.session.set(pool + ['nisplus-server', server]) + self.cli_set(pool + ['nis-server', server]) + self.cli_set(pool + ['nisplus-server', server]) for search in search_domains: - self.session.set(pool + ['domain-search', search]) + self.cli_set(pool + ['domain-search', search]) client_base = 1 for client in ['client1', 'client2', 'client3']: cid = '00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:{}'.format(client_base) - self.session.set(pool + ['static-mapping', client, 'identifier', cid]) - self.session.set(pool + ['static-mapping', client, 'ipv6-address', inc_ip(subnet, client_base)]) + self.cli_set(pool + ['static-mapping', client, 'identifier', cid]) + self.cli_set(pool + ['static-mapping', client, 'ipv6-address', inc_ip(subnet, client_base)]) client_base += 1 # commit changes - self.session.commit() + self.cli_commit() config = read_file(DHCPD_CONF) self.assertIn(f'option dhcp6.preference {preference};', config) @@ -136,12 +133,12 @@ class TestServiceDHCPServer(unittest.TestCase): pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet] - self.session.set(pool + ['address-range', 'start', range_start, 'stop', range_stop]) - self.session.set(pool + ['prefix-delegation', 'start', delegate_start, 'stop', delegate_stop]) - self.session.set(pool + ['prefix-delegation', 'start', delegate_start, 'prefix-length', delegate_len]) + self.cli_set(pool + ['address-range', 'start', range_start, 'stop', range_stop]) + self.cli_set(pool + ['prefix-delegation', 'start', delegate_start, 'stop', delegate_stop]) + self.cli_set(pool + ['prefix-delegation', 'start', delegate_start, 'prefix-length', delegate_len]) # commit changes - self.session.commit() + self.cli_commit() config = read_file(DHCPD_CONF) self.assertIn(f'subnet6 {subnet}' + r' {', config) @@ -151,5 +148,26 @@ class TestServiceDHCPServer(unittest.TestCase): # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) + def test_global_nameserver(self): + shared_net_name = 'SMOKE-3' + ns_global_1 = '2001:db8::1111' + ns_global_2 = '2001:db8::2222' + + self.cli_set(base_path + ['global-parameters', 'name-server', ns_global_1]) + self.cli_set(base_path + ['global-parameters', 'name-server', ns_global_2]) + self.cli_set(base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]) + + # commit changes + self.cli_commit() + + config = read_file(DHCPD_CONF) + self.assertIn(f'option dhcp6.name-servers {ns_global_1};', config) + self.assertIn(f'option dhcp6.name-servers {ns_global_2};', config) + self.assertIn(f'subnet6 {subnet}' + r' {', config) + self.assertIn(f'set shared-networkname = "{shared_net_name}";', config) + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py index 83eede64a..d8a87ffd4 100755 --- a/smoketest/scripts/cli/test_service_dns_dynamic.py +++ b/smoketest/scripts/cli/test_service_dns_dynamic.py @@ -18,10 +18,11 @@ import re import os import unittest -from getpass import getuser +from base_vyostest_shim import VyOSUnitTestSHIM + from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError -from vyos.util import read_file +from vyos.util import cmd from vyos.util import process_named_running PROCESS_NAME = 'ddclient' @@ -29,21 +30,17 @@ DDCLIENT_CONF = '/run/ddclient/ddclient.conf' base_path = ['service', 'dns', 'dynamic'] def get_config_value(key): - tmp = read_file(DDCLIENT_CONF) + tmp = cmd(f'sudo cat {DDCLIENT_CONF}') tmp = re.findall(r'\n?{}=+(.*)'.format(key), tmp) tmp = tmp[0].rstrip(',') return tmp -class TestServiceDDNS(unittest.TestCase): - def setUp(self): - self.session = ConfigSession(os.getpid()) +class TestServiceDDNS(VyOSUnitTestSHIM.TestCase): def tearDown(self): # Delete DDNS configuration - self.session.delete(base_path) - self.session.commit() - - del self.session + self.cli_delete(base_path) + self.cli_commit() def test_dyndns_service(self): ddns = ['interface', 'eth0', 'service'] @@ -53,45 +50,44 @@ class TestServiceDDNS(unittest.TestCase): user = 'vyos_user' password = 'vyos_pass' zone = 'vyos.io' - self.session.delete(base_path) - self.session.set(base_path + ddns + [service, 'host-name', 'test.ddns.vyos.io']) - self.session.set(base_path + ddns + [service, 'login', user]) - self.session.set(base_path + ddns + [service, 'password', password]) - self.session.set(base_path + ddns + [service, 'zone', zone]) + self.cli_delete(base_path) + self.cli_set(base_path + ddns + [service, 'host-name', 'test.ddns.vyos.io']) + self.cli_set(base_path + ddns + [service, 'login', user]) + self.cli_set(base_path + ddns + [service, 'password', password]) + self.cli_set(base_path + ddns + [service, 'zone', zone]) # commit changes if service == 'cloudflare': - self.session.commit() + self.cli_commit() else: # zone option only works on cloudflare, an exception is raised # for all others with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.delete(base_path + ddns + [service, 'zone', 'vyos.io']) + self.cli_commit() + self.cli_delete(base_path + ddns + [service, 'zone', 'vyos.io']) # commit changes again - now it should work - self.session.commit() + self.cli_commit() # we can only read the configuration file when we operate as 'root' - if getuser() == 'root': - protocol = get_config_value('protocol') - login = get_config_value('login') - pwd = get_config_value('password') - - # some services need special treatment - protoname = service - if service == 'cloudflare': - tmp = get_config_value('zone') - self.assertTrue(tmp == zone) - elif service == 'afraid': - protoname = 'freedns' - elif service == 'dyndns': - protoname = 'dyndns2' - elif service == 'zoneedit': - protoname = 'zoneedit1' - - self.assertTrue(protocol == protoname) - self.assertTrue(login == user) - self.assertTrue(pwd == "'" + password + "'") + protocol = get_config_value('protocol') + login = get_config_value('login') + pwd = get_config_value('password') + + # some services need special treatment + protoname = service + if service == 'cloudflare': + tmp = get_config_value('zone') + self.assertTrue(tmp == zone) + elif service == 'afraid': + protoname = 'freedns' + elif service == 'dyndns': + protoname = 'dyndns2' + elif service == 'zoneedit': + protoname = 'zoneedit1' + + self.assertTrue(protocol == protoname) + self.assertTrue(login == user) + self.assertTrue(pwd == "'" + password + "'") # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) @@ -101,11 +97,11 @@ class TestServiceDDNS(unittest.TestCase): ddns = ['interface', 'eth0', 'rfc2136', 'vyos'] ddns_key_file = '/config/auth/my.key' - self.session.set(base_path + ddns + ['key', ddns_key_file]) - self.session.set(base_path + ddns + ['record', 'test.ddns.vyos.io']) - self.session.set(base_path + ddns + ['server', 'ns1.vyos.io']) - self.session.set(base_path + ddns + ['ttl', '300']) - self.session.set(base_path + ddns + ['zone', 'vyos.io']) + self.cli_set(base_path + ddns + ['key', ddns_key_file]) + self.cli_set(base_path + ddns + ['record', 'test.ddns.vyos.io']) + self.cli_set(base_path + ddns + ['server', 'ns1.vyos.io']) + self.cli_set(base_path + ddns + ['ttl', '300']) + self.cli_set(base_path + ddns + ['zone', 'vyos.io']) # ensure an exception will be raised as no key is present if os.path.exists(ddns_key_file): @@ -113,13 +109,13 @@ class TestServiceDDNS(unittest.TestCase): # check validate() - the key file does not exist yet with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() with open(ddns_key_file, 'w') as f: f.write('S3cretKey') # commit changes - self.session.commit() + self.cli_commit() # TODO: inspect generated configuration file diff --git a/smoketest/scripts/cli/test_service_dns_forwarding.py b/smoketest/scripts/cli/test_service_dns_forwarding.py index ada53e8dd..8005eb319 100755 --- a/smoketest/scripts/cli/test_service_dns_forwarding.py +++ b/smoketest/scripts/cli/test_service_dns_forwarding.py @@ -15,10 +15,12 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import re -import os import unittest -from vyos.configsession import ConfigSession, ConfigSessionError +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError from vyos.util import read_file from vyos.util import process_named_running @@ -37,44 +39,40 @@ def get_config_value(key, file=CONFIG_FILE): tmp = re.findall(r'\n{}=+(.*)'.format(key), tmp) return tmp[0] -class TestServicePowerDNS(unittest.TestCase): - def setUp(self): - self.session = ConfigSession(os.getpid()) - +class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase): def tearDown(self): # Delete DNS forwarding configuration - self.session.delete(base_path) - self.session.commit() - del self.session + self.cli_delete(base_path) + self.cli_commit() def test_basic_forwarding(self): # Check basic DNS forwarding settings cache_size = '20' negative_ttl = '120' - self.session.set(base_path + ['cache-size', cache_size]) - self.session.set(base_path + ['negative-ttl', negative_ttl]) + self.cli_set(base_path + ['cache-size', cache_size]) + self.cli_set(base_path + ['negative-ttl', negative_ttl]) # check validate() - allow from must be defined with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() for network in allow_from: - self.session.set(base_path + ['allow-from', network]) + self.cli_set(base_path + ['allow-from', network]) # check validate() - listen-address must be defined with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() for address in listen_adress: - self.session.set(base_path + ['listen-address', address]) + self.cli_set(base_path + ['listen-address', address]) # configure DNSSEC - self.session.set(base_path + ['dnssec', 'validate']) + self.cli_set(base_path + ['dnssec', 'validate']) # Do not use local /etc/hosts file in name resolution - self.session.set(base_path + ['ignore-hosts-file']) + self.cli_set(base_path + ['ignore-hosts-file']) # commit changes - self.session.commit() + self.cli_commit() # Check configured cache-size tmp = get_config_value('max-cache-entries') @@ -103,16 +101,16 @@ class TestServicePowerDNS(unittest.TestCase): # DNSSEC option testing for network in allow_from: - self.session.set(base_path + ['allow-from', network]) + self.cli_set(base_path + ['allow-from', network]) for address in listen_adress: - self.session.set(base_path + ['listen-address', address]) + self.cli_set(base_path + ['listen-address', address]) options = ['off', 'process-no-validate', 'process', 'log-fail', 'validate'] for option in options: - self.session.set(base_path + ['dnssec', option]) + self.cli_set(base_path + ['dnssec', option]) # commit changes - self.session.commit() + self.cli_commit() tmp = get_config_value('dnssec') self.assertEqual(tmp, option) @@ -124,16 +122,16 @@ class TestServicePowerDNS(unittest.TestCase): # Externe Domain Name Servers (DNS) addresses for network in allow_from: - self.session.set(base_path + ['allow-from', network]) + self.cli_set(base_path + ['allow-from', network]) for address in listen_adress: - self.session.set(base_path + ['listen-address', address]) + self.cli_set(base_path + ['listen-address', address]) nameservers = ['192.0.2.1', '192.0.2.2'] for nameserver in nameservers: - self.session.set(base_path + ['name-server', nameserver]) + self.cli_set(base_path + ['name-server', nameserver]) # commit changes - self.session.commit() + self.cli_commit() tmp = get_config_value(r'\+.', file=FORWARD_FILE) self.assertEqual(tmp, ', '.join(nameservers)) @@ -148,26 +146,26 @@ class TestServicePowerDNS(unittest.TestCase): def test_domain_forwarding(self): for network in allow_from: - self.session.set(base_path + ['allow-from', network]) + self.cli_set(base_path + ['allow-from', network]) for address in listen_adress: - self.session.set(base_path + ['listen-address', address]) + self.cli_set(base_path + ['listen-address', address]) domains = ['vyos.io', 'vyos.net', 'vyos.com'] nameservers = ['192.0.2.1', '192.0.2.2'] for domain in domains: for nameserver in nameservers: - self.session.set(base_path + ['domain', domain, 'server', nameserver]) + self.cli_set(base_path + ['domain', domain, 'server', nameserver]) # Test 'recursion-desired' flag for only one domain if domain == domains[0]: - self.session.set(base_path + ['domain', domain, 'recursion-desired']) + self.cli_set(base_path + ['domain', domain, 'recursion-desired']) # Test 'negative trust anchor' flag for the second domain only if domain == domains[1]: - self.session.set(base_path + ['domain', domain, 'addnta']) + self.cli_set(base_path + ['domain', domain, 'addnta']) # commit changes - self.session.commit() + self.cli_commit() # Test configured name-servers hosts_conf = read_file(HOSTSD_FILE) diff --git a/smoketest/scripts/cli/test_service_https.py b/smoketest/scripts/cli/test_service_https.py index fd0f6bfbd..3ed7655e9 100755 --- a/smoketest/scripts/cli/test_service_https.py +++ b/smoketest/scripts/cli/test_service_https.py @@ -14,28 +14,27 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import unittest +from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSession from vyos.util import run base_path = ['service', 'https'] -class TestHTTPSService(unittest.TestCase): +class TestHTTPSService(VyOSUnitTestSHIM.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) + self.cli_delete(base_path) def tearDown(self): - self.session.delete(base_path) - self.session.commit() + self.cli_delete(base_path) + self.cli_commit() def test_default(self): - self.session.set(base_path) - self.session.commit() + self.cli_set(base_path) + self.cli_commit() ret = run('sudo /usr/sbin/nginx -t') self.assertEqual(ret, 0) @@ -48,11 +47,11 @@ class TestHTTPSService(unittest.TestCase): test_path = base_path + ['virtual-host', vhost_id] - self.session.set(test_path + ['listen-address', address]) - self.session.set(test_path + ['listen-port', port]) - self.session.set(test_path + ['server-name', name]) + self.cli_set(test_path + ['listen-address', address]) + self.cli_set(test_path + ['listen-port', port]) + self.cli_set(test_path + ['server-name', name]) - self.session.commit() + self.cli_commit() ret = run('sudo /usr/sbin/nginx -t') self.assertEqual(ret, 0) diff --git a/smoketest/scripts/cli/test_service_mdns-repeater.py b/smoketest/scripts/cli/test_service_mdns-repeater.py index e6986b92a..b1092c3e5 100755 --- a/smoketest/scripts/cli/test_service_mdns-repeater.py +++ b/smoketest/scripts/cli/test_service_mdns-repeater.py @@ -14,35 +14,32 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import unittest +from base_vyostest_shim import VyOSUnitTestSHIM + from vyos.configsession import ConfigSession from vyos.util import process_named_running base_path = ['service', 'mdns', 'repeater'] intf_base = ['interfaces', 'dummy'] -class TestServiceMDNSrepeater(unittest.TestCase): - def setUp(self): - self.session = ConfigSession(os.getpid()) - +class TestServiceMDNSrepeater(VyOSUnitTestSHIM.TestCase): def tearDown(self): - self.session.delete(base_path) - self.session.delete(intf_base + ['dum10']) - self.session.delete(intf_base + ['dum20']) - self.session.commit() - del self.session + self.cli_delete(base_path) + self.cli_delete(intf_base + ['dum10']) + self.cli_delete(intf_base + ['dum20']) + self.cli_commit() def test_service(self): # Service required a configured IP address on the interface - self.session.set(intf_base + ['dum10', 'address', '192.0.2.1/30']) - self.session.set(intf_base + ['dum20', 'address', '192.0.2.5/30']) + self.cli_set(intf_base + ['dum10', 'address', '192.0.2.1/30']) + self.cli_set(intf_base + ['dum20', 'address', '192.0.2.5/30']) - self.session.set(base_path + ['interface', 'dum10']) - self.session.set(base_path + ['interface', 'dum20']) - self.session.commit() + self.cli_set(base_path + ['interface', 'dum10']) + self.cli_set(base_path + ['interface', 'dum20']) + self.cli_commit() # Check for running process self.assertTrue(process_named_running('mdns-repeater')) diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py index a4bb6e9f9..2b11ee362 100755 --- a/smoketest/scripts/cli/test_service_pppoe-server.py +++ b/smoketest/scripts/cli/test_service_pppoe-server.py @@ -27,7 +27,7 @@ local_if = ['interfaces', 'dummy', 'dum667'] ac_name = 'ACN' interface = 'eth0' -class TestServicePPPoEServer(BasicAccelPPPTest.BaseTest): +class TestServicePPPoEServer(BasicAccelPPPTest.TestCase): def setUp(self): self._base_path = ['service', 'pppoe-server'] self._process_name = 'accel-pppd' @@ -37,7 +37,7 @@ class TestServicePPPoEServer(BasicAccelPPPTest.BaseTest): super().setUp() def tearDown(self): - self.session.delete(local_if) + self.cli_delete(local_if) super().tearDown() def verify(self, conf): @@ -66,7 +66,7 @@ class TestServicePPPoEServer(BasicAccelPPPTest.BaseTest): super().verify(conf) def basic_config(self): - self.session.set(local_if + ['address', '192.0.2.1/32']) + self.cli_set(local_if + ['address', '192.0.2.1/32']) self.set(['access-concentrator', ac_name]) self.set(['interface', interface]) @@ -92,7 +92,7 @@ class TestServicePPPoEServer(BasicAccelPPPTest.BaseTest): self.set(['ppp-options', 'mru', mru]) # commit changes - self.session.commit() + self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters='=') @@ -124,7 +124,7 @@ class TestServicePPPoEServer(BasicAccelPPPTest.BaseTest): self.set( ['authentication', 'protocols', 'mschap-v2']) # commit changes - self.session.commit() + self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True) @@ -144,12 +144,13 @@ class TestServicePPPoEServer(BasicAccelPPPTest.BaseTest): start = '192.0.2.10' stop = '192.0.2.20' - start_stop = f'{start}-{stop}' + stop_octet = stop.split('.')[3] + start_stop = f'{start}-{stop_octet}' self.set(['client-ip-pool', 'start', start]) self.set(['client-ip-pool', 'stop', stop]) # commit changes - self.session.commit() + self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True) @@ -186,7 +187,7 @@ class TestServicePPPoEServer(BasicAccelPPPTest.BaseTest): self.set(['client-ipv6-pool', 'delegate', delegate_prefix, 'delegation-prefix', delegate_mask]) # commit changes - self.session.commit() + self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters='=') diff --git a/smoketest/scripts/cli/test_service_router-advert.py b/smoketest/scripts/cli/test_service_router-advert.py index b80eb3c43..b19c49c6e 100755 --- a/smoketest/scripts/cli/test_service_router-advert.py +++ b/smoketest/scripts/cli/test_service_router-advert.py @@ -15,9 +15,10 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import re -import os import unittest +from base_vyostest_shim import VyOSUnitTestSHIM + from vyos.configsession import ConfigSession from vyos.util import read_file from vyos.util import process_named_running @@ -33,26 +34,24 @@ def get_config_value(key): tmp = re.findall(r'\n?{}\s+(.*)'.format(key), tmp) return tmp[0].split()[0].replace(';','') -class TestServiceRADVD(unittest.TestCase): +class TestServiceRADVD(VyOSUnitTestSHIM.TestCase): def setUp(self): - self.session = ConfigSession(os.getpid()) - self.session.set(address_base + ['2001:db8::1/64']) + self.cli_set(address_base + ['2001:db8::1/64']) def tearDown(self): - self.session.delete(address_base) - self.session.delete(base_path) - self.session.commit() - del self.session + self.cli_delete(address_base) + self.cli_delete(base_path) + self.cli_commit() def test_single(self): - self.session.set(base_path + ['prefix', '::/64', 'no-on-link-flag']) - self.session.set(base_path + ['prefix', '::/64', 'no-autonomous-flag']) - self.session.set(base_path + ['prefix', '::/64', 'valid-lifetime', 'infinity']) - self.session.set(base_path + ['dnssl', '2001:db8::1234']) - self.session.set(base_path + ['other-config-flag']) + self.cli_set(base_path + ['prefix', '::/64', 'no-on-link-flag']) + self.cli_set(base_path + ['prefix', '::/64', 'no-autonomous-flag']) + self.cli_set(base_path + ['prefix', '::/64', 'valid-lifetime', 'infinity']) + self.cli_set(base_path + ['dnssl', '2001:db8::1234']) + self.cli_set(base_path + ['other-config-flag']) # commit changes - self.session.commit() + self.cli_commit() # verify values tmp = get_config_value('interface') diff --git a/smoketest/scripts/cli/test_service_snmp.py b/smoketest/scripts/cli/test_service_snmp.py index 81045d0b4..008271102 100755 --- a/smoketest/scripts/cli/test_service_snmp.py +++ b/smoketest/scripts/cli/test_service_snmp.py @@ -14,10 +14,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import re import unittest +from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError @@ -35,15 +35,11 @@ def get_config_value(key): tmp = re.findall(r'\n?{}\s+(.*)'.format(key), tmp) return tmp[0] -class TestSNMPService(unittest.TestCase): +class TestSNMPService(VyOSUnitTestSHIM.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): - del self.session + self.cli_delete(base_path) def test_snmp_basic(self): # Check if SNMP can be configured and service runs @@ -53,19 +49,19 @@ class TestSNMPService(unittest.TestCase): for auth in ['ro', 'rw']: community = 'VyOS' + auth - self.session.set(base_path + ['community', community, 'authorization', auth]) + self.cli_set(base_path + ['community', community, 'authorization', auth]) for client in clients: - self.session.set(base_path + ['community', community, 'client', client]) + self.cli_set(base_path + ['community', community, 'client', client]) for network in networks: - self.session.set(base_path + ['community', community, 'network', network]) + self.cli_set(base_path + ['community', community, 'network', network]) for addr in listen: - self.session.set(base_path + ['listen-address', addr]) + self.cli_set(base_path + ['listen-address', addr]) - self.session.set(base_path + ['contact', 'maintainers@vyos.io']) - self.session.set(base_path + ['location', 'qemu']) + self.cli_set(base_path + ['contact', 'maintainers@vyos.io']) + self.cli_set(base_path + ['location', 'qemu']) - self.session.commit() + self.cli_commit() # verify listen address, it will be returned as # ['unix:/run/snmpd.socket,udp:127.0.0.1:161,udp6:[::1]:161'] @@ -88,30 +84,30 @@ class TestSNMPService(unittest.TestCase): # Check if SNMPv3 can be configured with SHA authentication # and service runs - self.session.set(base_path + ['v3', 'engineid', '000000000000000000000002']) - self.session.set(base_path + ['v3', 'group', 'default', 'mode', 'ro']) + self.cli_set(base_path + ['v3', 'engineid', '000000000000000000000002']) + self.cli_set(base_path + ['v3', 'group', 'default', 'mode', 'ro']) # check validate() - a view must be created before this can be comitted with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() - self.session.set(base_path + ['v3', 'view', 'default', 'oid', '1']) - self.session.set(base_path + ['v3', 'group', 'default', 'view', 'default']) + self.cli_set(base_path + ['v3', 'view', 'default', 'oid', '1']) + self.cli_set(base_path + ['v3', 'group', 'default', 'view', 'default']) # create user - self.session.set(base_path + ['v3', 'user', 'vyos', 'auth', 'plaintext-password', 'vyos12345678']) - self.session.set(base_path + ['v3', 'user', 'vyos', 'auth', 'type', 'sha']) - self.session.set(base_path + ['v3', 'user', 'vyos', 'privacy', 'plaintext-password', 'vyos12345678']) - self.session.set(base_path + ['v3', 'user', 'vyos', 'privacy', 'type', 'aes']) - self.session.set(base_path + ['v3', 'user', 'vyos', 'group', 'default']) + self.cli_set(base_path + ['v3', 'user', 'vyos', 'auth', 'plaintext-password', 'vyos12345678']) + self.cli_set(base_path + ['v3', 'user', 'vyos', 'auth', 'type', 'sha']) + self.cli_set(base_path + ['v3', 'user', 'vyos', 'privacy', 'plaintext-password', 'vyos12345678']) + self.cli_set(base_path + ['v3', 'user', 'vyos', 'privacy', 'type', 'aes']) + self.cli_set(base_path + ['v3', 'user', 'vyos', 'group', 'default']) - self.session.commit() + self.cli_commit() # commit will alter the CLI values - check if they have been updated: hashed_password = '4e52fe55fd011c9c51ae2c65f4b78ca93dcafdfe' - tmp = self.session.show_config(base_path + ['v3', 'user', 'vyos', 'auth', 'encrypted-password']).split()[1] + tmp = self._session.show_config(base_path + ['v3', 'user', 'vyos', 'auth', 'encrypted-password']).split()[1] self.assertEqual(tmp, hashed_password) - tmp = self.session.show_config(base_path + ['v3', 'user', 'vyos', 'privacy', 'encrypted-password']).split()[1] + tmp = self._session.show_config(base_path + ['v3', 'user', 'vyos', 'privacy', 'encrypted-password']).split()[1] self.assertEqual(tmp, hashed_password) # TODO: read in config file and check values @@ -123,30 +119,30 @@ class TestSNMPService(unittest.TestCase): # Check if SNMPv3 can be configured with MD5 authentication # and service runs - self.session.set(base_path + ['v3', 'engineid', '000000000000000000000002']) - self.session.set(base_path + ['v3', 'group', 'default', 'mode', 'ro']) + self.cli_set(base_path + ['v3', 'engineid', '000000000000000000000002']) + self.cli_set(base_path + ['v3', 'group', 'default', 'mode', 'ro']) # check validate() - a view must be created before this can be comitted with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() - self.session.set(base_path + ['v3', 'view', 'default', 'oid', '1']) - self.session.set(base_path + ['v3', 'group', 'default', 'view', 'default']) + self.cli_set(base_path + ['v3', 'view', 'default', 'oid', '1']) + self.cli_set(base_path + ['v3', 'group', 'default', 'view', 'default']) # create user - self.session.set(base_path + ['v3', 'user', 'vyos', 'auth', 'plaintext-password', 'vyos12345678']) - self.session.set(base_path + ['v3', 'user', 'vyos', 'auth', 'type', 'md5']) - self.session.set(base_path + ['v3', 'user', 'vyos', 'privacy', 'plaintext-password', 'vyos12345678']) - self.session.set(base_path + ['v3', 'user', 'vyos', 'privacy', 'type', 'des']) - self.session.set(base_path + ['v3', 'user', 'vyos', 'group', 'default']) + self.cli_set(base_path + ['v3', 'user', 'vyos', 'auth', 'plaintext-password', 'vyos12345678']) + self.cli_set(base_path + ['v3', 'user', 'vyos', 'auth', 'type', 'md5']) + self.cli_set(base_path + ['v3', 'user', 'vyos', 'privacy', 'plaintext-password', 'vyos12345678']) + self.cli_set(base_path + ['v3', 'user', 'vyos', 'privacy', 'type', 'des']) + self.cli_set(base_path + ['v3', 'user', 'vyos', 'group', 'default']) - self.session.commit() + self.cli_commit() # commit will alter the CLI values - check if they have been updated: hashed_password = '4c67690d45d3dfcd33d0d7e308e370ad' - tmp = self.session.show_config(base_path + ['v3', 'user', 'vyos', 'auth', 'encrypted-password']).split()[1] + tmp = self._session.show_config(base_path + ['v3', 'user', 'vyos', 'auth', 'encrypted-password']).split()[1] self.assertEqual(tmp, hashed_password) - tmp = self.session.show_config(base_path + ['v3', 'user', 'vyos', 'privacy', 'encrypted-password']).split()[1] + tmp = self._session.show_config(base_path + ['v3', 'user', 'vyos', 'privacy', 'encrypted-password']).split()[1] self.assertEqual(tmp, hashed_password) # TODO: read in config file and check values diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py index 0bb907c3a..c76f709b1 100755 --- a/smoketest/scripts/cli/test_service_ssh.py +++ b/smoketest/scripts/cli/test_service_ssh.py @@ -14,10 +14,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import re import os +import re import unittest +from base_vyostest_shim import VyOSUnitTestSHIM + from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError from vyos.util import cmd @@ -25,40 +27,41 @@ from vyos.util import process_named_running from vyos.util import read_file PROCESS_NAME = 'sshd' -SSHD_CONF = '/run/ssh/sshd_config' +SSHD_CONF = '/run/sshd/sshd_config' base_path = ['service', 'ssh'] -vrf = 'ssh-test' +vrf = 'mgmt' + +key_rsa = '/etc/ssh/ssh_host_rsa_key' +key_dsa = '/etc/ssh/ssh_host_dsa_key' +key_ed25519 = '/etc/ssh/ssh_host_ed25519_key' def get_config_value(key): tmp = read_file(SSHD_CONF) tmp = re.findall(f'\n?{key}\s+(.*)', tmp) return tmp -class TestServiceSSH(unittest.TestCase): +class TestServiceSSH(VyOSUnitTestSHIM.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) + self.cli_delete(base_path) def tearDown(self): # delete testing SSH config - self.session.delete(base_path) - # restore "plain" SSH access - self.session.set(base_path) - # delete VRF - self.session.delete(['vrf', 'name', vrf]) + self.cli_delete(base_path) + self.cli_commit() - self.session.commit() - del self.session + self.assertTrue(os.path.isfile(key_rsa)) + self.assertTrue(os.path.isfile(key_dsa)) + self.assertTrue(os.path.isfile(key_ed25519)) def test_ssh_default(self): # Check if SSH service runs with default settings - used for checking # behavior of <defaultValue> in XML definition - self.session.set(base_path) + self.cli_set(base_path) # commit changes - self.session.commit() + self.cli_commit() # Check configured port port = get_config_value('Port')[0] @@ -69,15 +72,15 @@ class TestServiceSSH(unittest.TestCase): def test_ssh_single_listen_address(self): # Check if SSH service can be configured and runs - self.session.set(base_path + ['port', '1234']) - self.session.set(base_path + ['disable-host-validation']) - self.session.set(base_path + ['disable-password-authentication']) - self.session.set(base_path + ['loglevel', 'verbose']) - self.session.set(base_path + ['client-keepalive-interval', '100']) - self.session.set(base_path + ['listen-address', '127.0.0.1']) + self.cli_set(base_path + ['port', '1234']) + self.cli_set(base_path + ['disable-host-validation']) + self.cli_set(base_path + ['disable-password-authentication']) + self.cli_set(base_path + ['loglevel', 'verbose']) + self.cli_set(base_path + ['client-keepalive-interval', '100']) + self.cli_set(base_path + ['listen-address', '127.0.0.1']) # commit changes - self.session.commit() + self.cli_commit() # Check configured port port = get_config_value('Port')[0] @@ -109,16 +112,16 @@ class TestServiceSSH(unittest.TestCase): def test_ssh_multiple_listen_addresses(self): # Check if SSH service can be configured and runs with multiple # listen ports and listen-addresses - ports = ['22', '2222'] + ports = ['22', '2222', '2223', '2224'] for port in ports: - self.session.set(base_path + ['port', port]) + self.cli_set(base_path + ['port', port]) addresses = ['127.0.0.1', '::1'] for address in addresses: - self.session.set(base_path + ['listen-address', address]) + self.cli_set(base_path + ['listen-address', address]) # commit changes - self.session.commit() + self.cli_commit() # Check configured port tmp = get_config_value('Port') @@ -136,17 +139,17 @@ class TestServiceSSH(unittest.TestCase): def test_ssh_vrf(self): # Check if SSH service can be bound to given VRF port = '22' - self.session.set(base_path + ['port', port]) - self.session.set(base_path + ['vrf', vrf]) + self.cli_set(base_path + ['port', port]) + self.cli_set(base_path + ['vrf', vrf]) # VRF does yet not exist - an error must be thrown with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() - self.session.set(['vrf', 'name', vrf, 'table', '1001']) + self.cli_set(['vrf', 'name', vrf, 'table', '1338']) # commit changes - self.session.commit() + self.cli_commit() # Check configured port tmp = get_config_value('Port') @@ -159,5 +162,8 @@ class TestServiceSSH(unittest.TestCase): tmp = cmd(f'ip vrf pids {vrf}') self.assertIn(PROCESS_NAME, tmp) + # delete VRF + self.cli_delete(['vrf', 'name', vrf]) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_tftp-server.py b/smoketest/scripts/cli/test_service_tftp-server.py index 82e5811ff..aed4c6beb 100755 --- a/smoketest/scripts/cli/test_service_tftp-server.py +++ b/smoketest/scripts/cli/test_service_tftp-server.py @@ -14,11 +14,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import re -import os import unittest from psutil import process_iter +from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError @@ -32,28 +31,26 @@ dummy_if_path = ['interfaces', 'dummy', 'dum69'] address_ipv4 = '192.0.2.1' address_ipv6 = '2001:db8::1' -class TestServiceTFTPD(unittest.TestCase): +class TestServiceTFTPD(VyOSUnitTestSHIM.TestCase): def setUp(self): - self.session = ConfigSession(os.getpid()) - self.session.set(dummy_if_path + ['address', address_ipv4 + '/32']) - self.session.set(dummy_if_path + ['address', address_ipv6 + '/128']) + self.cli_set(dummy_if_path + ['address', address_ipv4 + '/32']) + self.cli_set(dummy_if_path + ['address', address_ipv6 + '/128']) def tearDown(self): - self.session.delete(base_path) - self.session.delete(dummy_if_path) - self.session.commit() - del self.session + self.cli_delete(base_path) + self.cli_delete(dummy_if_path) + self.cli_commit() def test_01_tftpd_single(self): directory = '/tmp' port = '69' # default port - self.session.set(base_path + ['allow-upload']) - self.session.set(base_path + ['directory', directory]) - self.session.set(base_path + ['listen-address', address_ipv4]) + self.cli_set(base_path + ['allow-upload']) + self.cli_set(base_path + ['directory', directory]) + self.cli_set(base_path + ['listen-address', address_ipv4]) # commit changes - self.session.commit() + self.cli_commit() config = read_file('/etc/default/tftpd0') # verify listen IP address @@ -71,13 +68,13 @@ class TestServiceTFTPD(unittest.TestCase): address = [address_ipv4, address_ipv6] port = '70' - self.session.set(base_path + ['directory', directory]) + self.cli_set(base_path + ['directory', directory]) for addr in address: - self.session.set(base_path + ['listen-address', addr]) - self.session.set(base_path + ['port', port]) + self.cli_set(base_path + ['listen-address', addr]) + self.cli_set(base_path + ['port', port]) # commit changes - self.session.commit() + self.cli_commit() for idx in range(0, len(address)): config = read_file(f'/etc/default/tftpd{idx}') diff --git a/smoketest/scripts/cli/test_service_webproxy.py b/smoketest/scripts/cli/test_service_webproxy.py index 3db2daa8f..d47bd452d 100755 --- a/smoketest/scripts/cli/test_service_webproxy.py +++ b/smoketest/scripts/cli/test_service_webproxy.py @@ -14,9 +14,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import unittest +from base_vyostest_shim import VyOSUnitTestSHIM + from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError from vyos.util import cmd @@ -29,23 +30,21 @@ base_path = ['service', 'webproxy'] listen_if = 'dum3632' listen_ip = '192.0.2.1' -class TestServiceWebProxy(unittest.TestCase): +class TestServiceWebProxy(VyOSUnitTestSHIM.TestCase): def setUp(self): - self.session = ConfigSession(os.getpid()) - self.session.set(['interfaces', 'dummy', listen_if, 'address', listen_ip + '/32']) + self.cli_set(['interfaces', 'dummy', listen_if, 'address', listen_ip + '/32']) def tearDown(self): - self.session.delete(['interfaces', 'dummy', listen_if]) - self.session.delete(base_path) - self.session.commit() - del self.session + self.cli_delete(['interfaces', 'dummy', listen_if]) + self.cli_delete(base_path) + self.cli_commit() def test_01_basic_proxy(self): default_cache = '100' - self.session.set(base_path + ['listen-address', listen_ip]) + self.cli_set(base_path + ['listen-address', listen_ip]) # commit changes - self.session.commit() + self.cli_commit() config = read_file(PROXY_CONF) self.assertIn(f'http_port {listen_ip}:3128 intercept', config) @@ -84,24 +83,24 @@ class TestServiceWebProxy(unittest.TestCase): block_mine = ['application/pdf', 'application/x-sh'] body_max_size = '4096' - self.session.set(base_path + ['listen-address', listen_ip]) - self.session.set(base_path + ['append-domain', domain]) - self.session.set(base_path + ['default-port', port]) - self.session.set(base_path + ['cache-size', cache_size]) - self.session.set(base_path + ['disable-access-log']) + self.cli_set(base_path + ['listen-address', listen_ip]) + self.cli_set(base_path + ['append-domain', domain]) + self.cli_set(base_path + ['default-port', port]) + self.cli_set(base_path + ['cache-size', cache_size]) + self.cli_set(base_path + ['disable-access-log']) - self.session.set(base_path + ['minimum-object-size', min_obj_size]) - self.session.set(base_path + ['maximum-object-size', max_obj_size]) + self.cli_set(base_path + ['minimum-object-size', min_obj_size]) + self.cli_set(base_path + ['maximum-object-size', max_obj_size]) - self.session.set(base_path + ['outgoing-address', listen_ip]) + self.cli_set(base_path + ['outgoing-address', listen_ip]) for mime in block_mine: - self.session.set(base_path + ['reply-block-mime', mime]) + self.cli_set(base_path + ['reply-block-mime', mime]) - self.session.set(base_path + ['reply-body-max-size', body_max_size]) + self.cli_set(base_path + ['reply-body-max-size', body_max_size]) # commit changes - self.session.commit() + self.cli_commit() config = read_file(PROXY_CONF) self.assertIn(f'http_port {listen_ip}:{port} intercept', config) @@ -132,34 +131,34 @@ class TestServiceWebProxy(unittest.TestCase): ldap_attr = 'cn' ldap_filter = '(cn=%s)' - self.session.set(base_path + ['listen-address', listen_ip, 'disable-transparent']) - self.session.set(base_path + ['authentication', 'children', auth_children]) - self.session.set(base_path + ['authentication', 'credentials-ttl', cred_ttl]) + self.cli_set(base_path + ['listen-address', listen_ip, 'disable-transparent']) + self.cli_set(base_path + ['authentication', 'children', auth_children]) + self.cli_set(base_path + ['authentication', 'credentials-ttl', cred_ttl]) - self.session.set(base_path + ['authentication', 'realm', realm]) - self.session.set(base_path + ['authentication', 'method', 'ldap']) + self.cli_set(base_path + ['authentication', 'realm', realm]) + self.cli_set(base_path + ['authentication', 'method', 'ldap']) # check validate() - LDAP authentication is enabled, but server not set with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(base_path + ['authentication', 'ldap', 'server', ldap_server]) + self.cli_commit() + self.cli_set(base_path + ['authentication', 'ldap', 'server', ldap_server]) # check validate() - LDAP password can not be set when bind-dn is not define - self.session.set(base_path + ['authentication', 'ldap', 'password', ldap_password]) + self.cli_set(base_path + ['authentication', 'ldap', 'password', ldap_password]) with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(base_path + ['authentication', 'ldap', 'bind-dn', ldap_bind_dn]) + self.cli_commit() + self.cli_set(base_path + ['authentication', 'ldap', 'bind-dn', ldap_bind_dn]) # check validate() - LDAP base-dn must be set with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(base_path + ['authentication', 'ldap', 'base-dn', ldap_base_dn]) + self.cli_commit() + self.cli_set(base_path + ['authentication', 'ldap', 'base-dn', ldap_base_dn]) - self.session.set(base_path + ['authentication', 'ldap', 'username-attribute', ldap_attr]) - self.session.set(base_path + ['authentication', 'ldap', 'filter-expression', ldap_filter]) - self.session.set(base_path + ['authentication', 'ldap', 'use-ssl']) + self.cli_set(base_path + ['authentication', 'ldap', 'username-attribute', ldap_attr]) + self.cli_set(base_path + ['authentication', 'ldap', 'filter-expression', ldap_filter]) + self.cli_set(base_path + ['authentication', 'ldap', 'use-ssl']) # commit changes - self.session.commit() + self.cli_commit() config = read_file(PROXY_CONF) self.assertIn(f'http_port {listen_ip}:3128', config) # disable-transparent @@ -175,7 +174,7 @@ class TestServiceWebProxy(unittest.TestCase): self.assertTrue(process_named_running(PROCESS_NAME)) def test_04_cache_peer(self): - self.session.set(base_path + ['listen-address', listen_ip]) + self.cli_set(base_path + ['listen-address', listen_ip]) cache_peers = { 'foo' : '192.0.2.1', @@ -183,12 +182,12 @@ class TestServiceWebProxy(unittest.TestCase): 'baz' : '192.0.2.3', } for peer in cache_peers: - self.session.set(base_path + ['cache-peer', peer, 'address', cache_peers[peer]]) + self.cli_set(base_path + ['cache-peer', peer, 'address', cache_peers[peer]]) if peer == 'baz': - self.session.set(base_path + ['cache-peer', peer, 'type', 'sibling']) + self.cli_set(base_path + ['cache-peer', peer, 'type', 'sibling']) # commit changes - self.session.commit() + self.cli_commit() config = read_file(PROXY_CONF) self.assertIn('never_direct allow all', config) @@ -214,22 +213,22 @@ class TestServiceWebProxy(unittest.TestCase): local_ok = ['10.0.0.0', 'vyos.net'] local_ok_url = ['vyos.net', 'vyos.io'] - self.session.set(base_path + ['listen-address', listen_ip]) - self.session.set(base_path + ['url-filtering', 'squidguard', 'log', 'all']) + self.cli_set(base_path + ['listen-address', listen_ip]) + self.cli_set(base_path + ['url-filtering', 'squidguard', 'log', 'all']) for block in local_block: - self.session.set(base_path + ['url-filtering', 'squidguard', 'local-block', block]) + self.cli_set(base_path + ['url-filtering', 'squidguard', 'local-block', block]) for ok in local_ok: - self.session.set(base_path + ['url-filtering', 'squidguard', 'local-ok', ok]) + self.cli_set(base_path + ['url-filtering', 'squidguard', 'local-ok', ok]) for url in local_block_url: - self.session.set(base_path + ['url-filtering', 'squidguard', 'local-block-url', url]) + self.cli_set(base_path + ['url-filtering', 'squidguard', 'local-block-url', url]) for url in local_ok_url: - self.session.set(base_path + ['url-filtering', 'squidguard', 'local-ok-url', url]) + self.cli_set(base_path + ['url-filtering', 'squidguard', 'local-ok-url', url]) for pattern in local_block_pattern: - self.session.set(base_path + ['url-filtering', 'squidguard', 'local-block-keyword', pattern]) + self.cli_set(base_path + ['url-filtering', 'squidguard', 'local-block-keyword', pattern]) # commit changes - self.session.commit() + self.cli_commit() # Check regular Squid config config = read_file(PROXY_CONF) diff --git a/smoketest/scripts/cli/test_system_acceleration_qat.py b/smoketest/scripts/cli/test_system_acceleration_qat.py index cadb263f5..9584888d6 100755 --- a/smoketest/scripts/cli/test_system_acceleration_qat.py +++ b/smoketest/scripts/cli/test_system_acceleration_qat.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 Francois Mertz fireboxled@gmail.com +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -14,34 +14,31 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import unittest +from base_vyostest_shim import VyOSUnitTestSHIM + from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError base_path = ['system', 'acceleration', 'qat'] -class TestSystemLCD(unittest.TestCase): - def setUp(self): - self.session = ConfigSession(os.getpid()) - +class TestIntelQAT(VyOSUnitTestSHIM.TestCase): def tearDown(self): - self.session.delete(base_path) - self.session.commit() - del self.session + self.cli_delete(base_path) + self.cli_commit() - def test_basic(self): - """ Check if configuration script is in place and that the config - script throws an error as QAT device is not present in Qemu. This *must* - be extended with QAT autodetection once run on a QAT enabled device """ + def test_simple_unsupported(self): + # Check if configuration script is in place and that the config script + # throws an error as QAT device is not present in Qemu. This *must* be + # extended with QAT autodetection once run on a QAT enabled device # configure some system display - self.session.set(base_path) + self.cli_set(base_path) # An error must be thrown if QAT device could not be found with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_system_ip.py b/smoketest/scripts/cli/test_system_ip.py index 8fc18ba88..e98a4e234 100755 --- a/smoketest/scripts/cli/test_system_ip.py +++ b/smoketest/scripts/cli/test_system_ip.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 Francois Mertz fireboxled@gmail.com +# 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 @@ -14,22 +14,18 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import unittest +from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSession from vyos.util import read_file base_path = ['system', 'ip'] -class TestSystemIP(unittest.TestCase): - def setUp(self): - self.session = ConfigSession(os.getpid()) - +class TestSystemIP(VyOSUnitTestSHIM.TestCase): def tearDown(self): - self.session.delete(base_path) - self.session.commit() - del self.session + self.cli_delete(base_path) + self.cli_commit() def test_system_ip_forwarding(self): # Test if IPv4 forwarding can be disabled globally, default is '1' @@ -37,8 +33,8 @@ class TestSystemIP(unittest.TestCase): all_forwarding = '/proc/sys/net/ipv4/conf/all/forwarding' self.assertEqual(read_file(all_forwarding), '1') - self.session.set(base_path + ['disable-forwarding']) - self.session.commit() + self.cli_set(base_path + ['disable-forwarding']) + self.cli_commit() self.assertEqual(read_file(all_forwarding), '0') @@ -50,9 +46,9 @@ class TestSystemIP(unittest.TestCase): self.assertEqual(read_file(use_neigh), '0') self.assertEqual(read_file(hash_policy), '0') - self.session.set(base_path + ['multipath', 'ignore-unreachable-nexthops']) - self.session.set(base_path + ['multipath', 'layer4-hashing']) - self.session.commit() + self.cli_set(base_path + ['multipath', 'ignore-unreachable-nexthops']) + self.cli_set(base_path + ['multipath', 'layer4-hashing']) + self.cli_commit() self.assertEqual(read_file(use_neigh), '1') self.assertEqual(read_file(hash_policy), '1') @@ -69,8 +65,8 @@ class TestSystemIP(unittest.TestCase): self.assertEqual(read_file(gc_thresh1), '1024') for size in [1024, 2048, 4096, 8192, 16384, 32768]: - self.session.set(base_path + ['arp', 'table-size', str(size)]) - self.session.commit() + self.cli_set(base_path + ['arp', 'table-size', str(size)]) + self.cli_commit() self.assertEqual(read_file(gc_thresh3), str(size)) self.assertEqual(read_file(gc_thresh2), str(size // 2)) diff --git a/smoketest/scripts/cli/test_system_ipv6.py b/smoketest/scripts/cli/test_system_ipv6.py new file mode 100755 index 000000000..c9c9e833d --- /dev/null +++ b/smoketest/scripts/cli/test_system_ipv6.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 unittest + +from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.configsession import ConfigSession +from vyos.util import read_file + +base_path = ['system', 'ipv6'] + +file_forwarding = '/proc/sys/net/ipv6/conf/all/forwarding' +file_disable = '/etc/modprobe.d/vyos_disable_ipv6.conf' +file_dad = '/proc/sys/net/ipv6/conf/all/accept_dad' +file_multipath = '/proc/sys/net/ipv6/fib_multipath_hash_policy' + +class TestSystemIPv6(VyOSUnitTestSHIM.TestCase): + def tearDown(self): + self.cli_delete(base_path) + self.cli_commit() + + def test_system_ipv6_forwarding(self): + # Test if IPv6 forwarding can be disabled globally, default is '1' + # which means forwearding enabled + self.assertEqual(read_file(file_forwarding), '1') + + self.cli_set(base_path + ['disable-forwarding']) + self.cli_commit() + + self.assertEqual(read_file(file_forwarding), '0') + + def test_system_ipv6_disable(self): + # Do not assign any IPv6 address on interfaces, this requires a reboot + # which can not be tested, but we can read the config file :) + self.cli_set(base_path + ['disable']) + self.cli_commit() + + # Verify configuration file + self.assertEqual(read_file(file_disable), 'options ipv6 disable_ipv6=1') + + def test_system_ipv6_strict_dad(self): + # This defaults to 1 + self.assertEqual(read_file(file_dad), '1') + + # Do not assign any IPv6 address on interfaces, this requires a reboot + # which can not be tested, but we can read the config file :) + self.cli_set(base_path + ['strict-dad']) + self.cli_commit() + + # Verify configuration file + self.assertEqual(read_file(file_dad), '2') + + def test_system_ipv6_multipath(self): + # This defaults to 0 + self.assertEqual(read_file(file_multipath), '0') + + # Do not assign any IPv6 address on interfaces, this requires a reboot + # which can not be tested, but we can read the config file :) + self.cli_set(base_path + ['multipath', 'layer4-hashing']) + self.cli_commit() + + # Verify configuration file + self.assertEqual(read_file(file_multipath), '1') + + def test_system_ipv6_neighbor_table_size(self): + # Maximum number of entries to keep in the ARP cache, the + # default is 8192 + + gc_thresh3 = '/proc/sys/net/ipv6/neigh/default/gc_thresh3' + gc_thresh2 = '/proc/sys/net/ipv6/neigh/default/gc_thresh2' + gc_thresh1 = '/proc/sys/net/ipv6/neigh/default/gc_thresh1' + self.assertEqual(read_file(gc_thresh3), '8192') + self.assertEqual(read_file(gc_thresh2), '4096') + self.assertEqual(read_file(gc_thresh1), '1024') + + for size in [1024, 2048, 4096, 8192, 16384, 32768]: + self.cli_set(base_path + ['neighbor', 'table-size', str(size)]) + self.cli_commit() + + self.assertEqual(read_file(gc_thresh3), str(size)) + self.assertEqual(read_file(gc_thresh2), str(size // 2)) + self.assertEqual(read_file(gc_thresh1), str(size // 8)) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_system_lcd.py b/smoketest/scripts/cli/test_system_lcd.py index 2bf601e3b..7a39e2986 100755 --- a/smoketest/scripts/cli/test_system_lcd.py +++ b/smoketest/scripts/cli/test_system_lcd.py @@ -14,32 +14,29 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import unittest +from base_vyostest_shim import VyOSUnitTestSHIM from configparser import ConfigParser + from vyos.configsession import ConfigSession from vyos.util import process_named_running config_file = '/run/LCDd/LCDd.conf' base_path = ['system', 'lcd'] -class TestSystemLCD(unittest.TestCase): - def setUp(self): - self.session = ConfigSession(os.getpid()) - +class TestSystemLCD(VyOSUnitTestSHIM.TestCase): def tearDown(self): - self.session.delete(base_path) - self.session.commit() - del self.session + self.cli_delete(base_path) + self.cli_commit() def test_system_display(self): # configure some system display - self.session.set(base_path + ['device', 'ttyS1']) - self.session.set(base_path + ['model', 'cfa-533']) + self.cli_set(base_path + ['device', 'ttyS1']) + self.cli_set(base_path + ['model', 'cfa-533']) # commit changes - self.session.commit() + self.cli_commit() # load up ini-styled LCDd.conf conf = ConfigParser() diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py index 6188cf38b..aa97511e0 100755 --- a/smoketest/scripts/cli/test_system_login.py +++ b/smoketest/scripts/cli/test_system_login.py @@ -14,47 +14,46 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import re import platform import unittest +from base_vyostest_shim import VyOSUnitTestSHIM + from distutils.version import LooseVersion from platform import release as kernel_version from subprocess import Popen, PIPE from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError from vyos.util import cmd from vyos.util import read_file +from vyos.template import inc_ip base_path = ['system', 'login'] users = ['vyos1', 'vyos2'] -class TestSystemLogin(unittest.TestCase): - def setUp(self): - self.session = ConfigSession(os.getpid()) - +class TestSystemLogin(VyOSUnitTestSHIM.TestCase): def tearDown(self): # Delete individual users from configuration for user in users: - self.session.delete(base_path + ['user', user]) + self.cli_delete(base_path + ['user', user]) - self.session.commit() - del self.session + self.cli_commit() - def test_local_user(self): + def test_system_login_user(self): # Check if user can be created and we can SSH to localhost - self.session.set(['service', 'ssh', 'port', '22']) + self.cli_set(['service', 'ssh', 'port', '22']) for user in users: name = "VyOS Roxx " + user home_dir = "/tmp/" + user - self.session.set(base_path + ['user', user, 'authentication', 'plaintext-password', user]) - self.session.set(base_path + ['user', user, 'full-name', 'VyOS Roxx']) - self.session.set(base_path + ['user', user, 'home-directory', home_dir]) + self.cli_set(base_path + ['user', user, 'authentication', 'plaintext-password', user]) + self.cli_set(base_path + ['user', user, 'full-name', 'VyOS Roxx']) + self.cli_set(base_path + ['user', user, 'home-directory', home_dir]) - self.session.commit() + self.cli_commit() for user in users: cmd = ['su','-', user] @@ -82,7 +81,7 @@ class TestSystemLogin(unittest.TestCase): for option in options: self.assertIn(f'{option}=y', kernel_config) - def test_radius_config(self): + def test_system_login_radius_ipv4(self): # Verify generated RADIUS configuration files radius_key = 'VyOSsecretVyOS' @@ -91,12 +90,18 @@ class TestSystemLogin(unittest.TestCase): radius_port = '2000' radius_timeout = '1' - self.session.set(base_path + ['radius', 'server', radius_server, 'key', radius_key]) - self.session.set(base_path + ['radius', 'server', radius_server, 'port', radius_port]) - self.session.set(base_path + ['radius', 'server', radius_server, 'timeout', radius_timeout]) - self.session.set(base_path + ['radius', 'source-address', radius_source]) + self.cli_set(base_path + ['radius', 'server', radius_server, 'key', radius_key]) + self.cli_set(base_path + ['radius', 'server', radius_server, 'port', radius_port]) + self.cli_set(base_path + ['radius', 'server', radius_server, 'timeout', radius_timeout]) + self.cli_set(base_path + ['radius', 'source-address', radius_source]) + self.cli_set(base_path + ['radius', 'source-address', inc_ip(radius_source, 1)]) + + # check validate() - Only one IPv4 source-address supported + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['radius', 'source-address', inc_ip(radius_source, 1)]) - self.session.commit() + self.cli_commit() # this file must be read with higher permissions pam_radius_auth_conf = cmd('sudo cat /etc/pam_radius_auth.conf') @@ -130,5 +135,59 @@ class TestSystemLogin(unittest.TestCase): tmp = re.findall(r'group:\s+mapname\s+files', nsswitch_conf) self.assertTrue(tmp) + def test_system_login_radius_ipv6(self): + # Verify generated RADIUS configuration files + + radius_key = 'VyOS-VyOS' + radius_server = '2001:db8::1' + radius_source = '::1' + radius_port = '4000' + radius_timeout = '4' + + self.cli_set(base_path + ['radius', 'server', radius_server, 'key', radius_key]) + self.cli_set(base_path + ['radius', 'server', radius_server, 'port', radius_port]) + self.cli_set(base_path + ['radius', 'server', radius_server, 'timeout', radius_timeout]) + self.cli_set(base_path + ['radius', 'source-address', radius_source]) + self.cli_set(base_path + ['radius', 'source-address', inc_ip(radius_source, 1)]) + + # check validate() - Only one IPv4 source-address supported + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['radius', 'source-address', inc_ip(radius_source, 1)]) + + self.cli_commit() + + # this file must be read with higher permissions + pam_radius_auth_conf = cmd('sudo cat /etc/pam_radius_auth.conf') + tmp = re.findall(r'\n?\[{}\]:{}\s+{}\s+{}\s+\[{}\]'.format(radius_server, + radius_port, radius_key, radius_timeout, + radius_source), pam_radius_auth_conf) + self.assertTrue(tmp) + + # required, static options + self.assertIn('priv-lvl 15', pam_radius_auth_conf) + self.assertIn('mapped_priv_user radius_priv_user', pam_radius_auth_conf) + + # PAM + pam_common_account = read_file('/etc/pam.d/common-account') + self.assertIn('pam_radius_auth.so', pam_common_account) + + pam_common_auth = read_file('/etc/pam.d/common-auth') + self.assertIn('pam_radius_auth.so', pam_common_auth) + + pam_common_session = read_file('/etc/pam.d/common-session') + self.assertIn('pam_radius_auth.so', pam_common_session) + + pam_common_session_noninteractive = read_file('/etc/pam.d/common-session-noninteractive') + self.assertIn('pam_radius_auth.so', pam_common_session_noninteractive) + + # NSS + nsswitch_conf = read_file('/etc/nsswitch.conf') + tmp = re.findall(r'passwd:\s+mapuid\s+files\s+mapname', nsswitch_conf) + self.assertTrue(tmp) + + tmp = re.findall(r'group:\s+mapname\s+files', nsswitch_conf) + self.assertTrue(tmp) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_system_nameserver.py b/smoketest/scripts/cli/test_system_nameserver.py index 5610c90c7..50dc466c2 100755 --- a/smoketest/scripts/cli/test_system_nameserver.py +++ b/smoketest/scripts/cli/test_system_nameserver.py @@ -14,12 +14,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import re import unittest -from vyos.configsession import ConfigSession, ConfigSessionError -import vyos.util as util +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError + +from vyos.util import read_file RESOLV_CONF = '/etc/resolv.conf' @@ -27,25 +30,20 @@ test_servers = ['192.0.2.10', '2001:db8:1::100'] base_path = ['system', 'name-server'] def get_name_servers(): - resolv_conf = util.read_file(RESOLV_CONF) + resolv_conf = read_file(RESOLV_CONF) return re.findall(r'\n?nameserver\s+(.*)', resolv_conf) -class TestSystemNameServer(unittest.TestCase): - def setUp(self): - self.session = ConfigSession(os.getpid()) - +class TestSystemNameServer(VyOSUnitTestSHIM.TestCase): def tearDown(self): # Delete existing name servers - self.session.delete(base_path) - self.session.commit() - - del self.session + self.cli_delete(base_path) + self.cli_commit() def test_nameserver_add(self): # Check if server is added to resolv.conf for s in test_servers: - self.session.set(base_path + [s]) - self.session.commit() + self.cli_set(base_path + [s]) + self.cli_commit() servers = get_name_servers() for s in servers: @@ -54,8 +52,8 @@ class TestSystemNameServer(unittest.TestCase): def test_nameserver_delete(self): # Test if a deleted server disappears from resolv.conf for s in test_servers: - self.session.delete(base_path + [s]) - self.session.commit() + self.cli_delete(base_path + [s]) + self.cli_commit() servers = get_name_servers() for s in servers: diff --git a/smoketest/scripts/cli/test_system_ntp.py b/smoketest/scripts/cli/test_system_ntp.py index 7d1bc144f..2b86ebd7c 100755 --- a/smoketest/scripts/cli/test_system_ntp.py +++ b/smoketest/scripts/cli/test_system_ntp.py @@ -15,9 +15,10 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import re -import os import unittest +from base_vyostest_shim import VyOSUnitTestSHIM + from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError from vyos.template import address_from_cidr @@ -26,7 +27,7 @@ from vyos.util import read_file from vyos.util import process_named_running PROCESS_NAME = 'ntpd' -NTP_CONF = '/etc/ntp.conf' +NTP_CONF = '/run/ntpd/ntpd.conf' base_path = ['system', 'ntp'] def get_config_value(key): @@ -35,17 +36,17 @@ def get_config_value(key): # remove possible trailing whitespaces return [item.strip() for item in tmp] -class TestSystemNTP(unittest.TestCase): +class TestSystemNTP(VyOSUnitTestSHIM.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) + self.cli_delete(base_path) def tearDown(self): - self.session.delete(base_path) - self.session.commit() - del self.session + self.cli_delete(base_path) + self.cli_commit() + + self.assertFalse(process_named_running(PROCESS_NAME)) def test_ntp_options(self): # Test basic NTP support with multiple servers and their options @@ -55,13 +56,13 @@ class TestSystemNTP(unittest.TestCase): for server in servers: for option in options: - self.session.set(base_path + ['server', server, option]) + self.cli_set(base_path + ['server', server, option]) # Test NTP pool - self.session.set(base_path + ['server', ntp_pool, 'pool']) + self.cli_set(base_path + ['server', ntp_pool, 'pool']) # commit changes - self.session.commit() + self.cli_commit() # Check generated configuration tmp = get_config_value('server') @@ -77,19 +78,23 @@ class TestSystemNTP(unittest.TestCase): def test_ntp_clients(self): # Test the allowed-networks statement + listen_address = ['127.0.0.1', '::1'] + for listen in listen_address: + self.cli_set(base_path + ['listen-address', listen]) + networks = ['192.0.2.0/24', '2001:db8:1000::/64'] for network in networks: - self.session.set(base_path + ['allow-clients', 'address', network]) + self.cli_set(base_path + ['allow-clients', 'address', network]) # Verify "NTP server not configured" verify() statement with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() servers = ['192.0.2.1', '192.0.2.2'] for server in servers: - self.session.set(base_path + ['server', server]) + self.cli_set(base_path + ['server', server]) - self.session.commit() + self.cli_commit() # Check generated client address configuration for network in networks: @@ -102,7 +107,9 @@ class TestSystemNTP(unittest.TestCase): # Check listen address tmp = get_config_value('interface') - test = ['ignore wildcard', 'listen 127.0.0.1', 'listen ::1'] + test = ['ignore wildcard'] + for listen in listen_address: + test.append(f'listen {listen}') self.assertEqual(tmp, test) # Check for running process diff --git a/smoketest/scripts/cli/test_vpn_openconnect.py b/smoketest/scripts/cli/test_vpn_openconnect.py index e27216e09..bf528c8b7 100755 --- a/smoketest/scripts/cli/test_vpn_openconnect.py +++ b/smoketest/scripts/cli/test_vpn_openconnect.py @@ -14,9 +14,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import unittest +from base_vyostest_shim import VyOSUnitTestSHIM + from vyos.configsession import ConfigSession from vyos.util import process_named_running @@ -25,29 +26,24 @@ base_path = ['vpn', 'openconnect'] cert = '/etc/ssl/certs/ssl-cert-snakeoil.pem' cert_key = '/etc/ssl/private/ssl-cert-snakeoil.key' -class TestVpnOpenconnect(unittest.TestCase): - def setUp(self): - self.session = ConfigSession(os.getpid()) - +class TestVpnOpenconnect(VyOSUnitTestSHIM.TestCase): def tearDown(self): # Delete vpn openconnect configuration - self.session.delete(base_path) - self.session.commit() - - del self.session + self.cli_delete(base_path) + self.cli_commit() def test_vpn(self): user = 'vyos_user' password = 'vyos_pass' - self.session.delete(base_path) - self.session.set(base_path + ["authentication", "local-users", "username", user, "password", password]) - self.session.set(base_path + ["authentication", "mode", "local"]) - self.session.set(base_path + ["network-settings", "client-ip-settings", "subnet", "192.0.2.0/24"]) - self.session.set(base_path + ["ssl", "ca-cert-file", cert]) - self.session.set(base_path + ["ssl", "cert-file", cert]) - self.session.set(base_path + ["ssl", "key-file", cert_key]) - - self.session.commit() + self.cli_delete(base_path) + self.cli_set(base_path + ["authentication", "local-users", "username", user, "password", password]) + self.cli_set(base_path + ["authentication", "mode", "local"]) + self.cli_set(base_path + ["network-settings", "client-ip-settings", "subnet", "192.0.2.0/24"]) + self.cli_set(base_path + ["ssl", "ca-cert-file", cert]) + self.cli_set(base_path + ["ssl", "cert-file", cert]) + self.cli_set(base_path + ["ssl", "key-file", cert_key]) + + self.cli_commit() # Check for running process self.assertTrue(process_named_running('ocserv-main')) diff --git a/smoketest/scripts/cli/test_vpn_sstp.py b/smoketest/scripts/cli/test_vpn_sstp.py index 95fe38dd9..033338685 100755 --- a/smoketest/scripts/cli/test_vpn_sstp.py +++ b/smoketest/scripts/cli/test_vpn_sstp.py @@ -23,18 +23,14 @@ ca_cert = '/tmp/ca.crt' ssl_cert = '/tmp/server.crt' ssl_key = '/tmp/server.key' -class TestVPNSSTPServer(BasicAccelPPPTest.BaseTest): +class TestVPNSSTPServer(BasicAccelPPPTest.TestCase): def setUp(self): self._base_path = ['vpn', 'sstp'] self._process_name = 'accel-pppd' self._config_file = '/run/accel-pppd/sstp.conf' self._chap_secrets = '/run/accel-pppd/sstp.chap-secrets' - super().setUp() - def tearDown(self): - super().tearDown() - def basic_config(self): # SSL is mandatory self.set(['ssl', 'ca-cert-file', ca_cert]) diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py index 7bcfea861..591630c46 100755 --- a/smoketest/scripts/cli/test_vrf.py +++ b/smoketest/scripts/cli/test_vrf.py @@ -14,53 +14,151 @@ # 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 json import unittest -from vyos.configsession import ConfigSession, ConfigSessionError +from netifaces import interfaces +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.ifconfig import Interface +from vyos.ifconfig import Section +from vyos.template import is_ipv6 +from vyos.util import cmd from vyos.util import read_file from vyos.validate import is_intf_addr_assigned -class VRFTest(unittest.TestCase): - def setUp(self): - self.session = ConfigSession(os.getpid()) - self._vrfs = ['red', 'green', 'blue'] +base_path = ['vrf'] +vrfs = ['red', 'green', 'blue', 'foo-bar', 'baz_foo'] + +class VRFTest(VyOSUnitTestSHIM.TestCase): + _interfaces = [] + + @classmethod + def setUpClass(cls): + # we need to filter out VLAN interfaces identified by a dot (.) + # in their name - just in case! + if 'TEST_ETH' in os.environ: + tmp = os.environ['TEST_ETH'].split() + cls._interfaces = tmp + else: + for tmp in Section.interfaces('ethernet'): + if not '.' in tmp: + cls._interfaces.append(tmp) + # call base-classes classmethod + super(cls, cls).setUpClass() def tearDown(self): # delete all VRFs - self.session.delete(['vrf']) - self.session.commit() - del self.session + self.cli_delete(base_path) + self.cli_commit() + for vrf in vrfs: + self.assertNotIn(vrf, interfaces()) def test_vrf_table_id(self): - table = 1000 - for vrf in self._vrfs: - base = ['vrf', 'name', vrf] - description = "VyOS-VRF-" + vrf - self.session.set(base + ['description', description]) + table = '1000' + for vrf in vrfs: + base = base_path + ['name', vrf] + description = f'VyOS-VRF-{vrf}' + self.cli_set(base + ['description', description]) # check validate() - a table ID is mandatory with self.assertRaises(ConfigSessionError): - self.session.commit() + self.cli_commit() - self.session.set(base + ['table', str(table)]) - table += 1 + self.cli_set(base + ['table', table]) + if vrf == 'green': + self.cli_set(base + ['disable']) + + table = str(int(table) + 1) # commit changes - self.session.commit() + self.cli_commit() + + # Verify VRF configuration + table = '1000' + iproute2_config = read_file('/etc/iproute2/rt_tables.d/vyos-vrf.conf') + for vrf in vrfs: + description = f'VyOS-VRF-{vrf}' + self.assertTrue(vrf in interfaces()) + vrf_if = Interface(vrf) + # validate proper interface description + self.assertEqual(vrf_if.get_alias(), description) + # validate admin up/down state of VRF + state = 'up' + if vrf == 'green': + state = 'down' + self.assertEqual(vrf_if.get_admin_state(), state) + + # Test the iproute2 lookup file, syntax is as follows: + # + # # id vrf name comment + # 1000 red # VyOS-VRF-red + # 1001 green # VyOS-VRF-green + # ... + regex = f'{table}\s+{vrf}\s+#\s+{description}' + self.assertTrue(re.findall(regex, iproute2_config)) + table = str(int(table) + 1) def test_vrf_loopback_ips(self): - table = 1000 - for vrf in self._vrfs: - base = ['vrf', 'name', vrf] - self.session.set(base + ['table', str(table)]) - table += 1 + table = '2000' + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_set(base + ['table', str(table)]) + table = str(int(table) + 1) # commit changes - self.session.commit() - for vrf in self._vrfs: + self.cli_commit() + + # Verify VRF configuration + for vrf in vrfs: + self.assertTrue(vrf in interfaces()) self.assertTrue(is_intf_addr_assigned(vrf, '127.0.0.1')) self.assertTrue(is_intf_addr_assigned(vrf, '::1')) + def test_vrf_table_id_is_unalterable(self): + # Linux Kernel prohibits the change of a VRF table on the fly. + # VRF must be deleted and recreated! + table = '1000' + vrf = vrfs[0] + base = base_path + ['name', vrf] + self.cli_set(base + ['table', table]) + + # commit changes + self.cli_commit() + + # Check if VRF has been created + self.assertTrue(vrf in interfaces()) + + table = str(int(table) + 1) + self.cli_set(base + ['table', table]) + # check validate() - table ID can not be altered! + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + def test_vrf_assign_interface(self): + vrf = vrfs[0] + table = '5000' + self.cli_set(['vrf', 'name', vrf, 'table', table]) + + for interface in self._interfaces: + section = Section.section(interface) + self.cli_set(['interfaces', section, interface, 'vrf', vrf]) + + # commit changes + self.cli_commit() + + # Verify & cleanup + for interface in self._interfaces: + # os.readlink resolves to: '../../../../../virtual/net/foovrf' + tmp = os.readlink(f'/sys/class/net/{interface}/master').split('/')[-1] + self.assertEqual(tmp, vrf) + # cleanup + section = Section.section(interface) + self.cli_delete(['interfaces', section, interface, 'vrf']) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/system/test_iproute2.py b/smoketest/scripts/system/test_iproute2.py new file mode 100755 index 000000000..2d2fe195b --- /dev/null +++ b/smoketest/scripts/system/test_iproute2.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import unittest + +class TestIproute2(unittest.TestCase): + def test_ip_is_symlink(self): + # For an unknown reason VyOS 1.3.0-rc2 did not have a symlink from + # /usr/sbin/ip -> /bin/ip - verify this now and forever + real_file = '/bin/ip' + symlink = '/usr/sbin/ip' + self.assertTrue(os.path.islink(symlink)) + self.assertFalse(os.path.islink(real_file)) + self.assertEqual(os.readlink(symlink), real_file) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/sphinx/source/.gitignore b/sphinx/source/.gitignore new file mode 100644 index 000000000..30d85567b --- /dev/null +++ b/sphinx/source/.gitignore @@ -0,0 +1 @@ +*.rst diff --git a/src/completion/list_bgp_peer_groups.sh b/src/completion/list_bgp_peer_groups.sh new file mode 100755 index 000000000..4503d608f --- /dev/null +++ b/src/completion/list_bgp_peer_groups.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# Copyright (C) 2021 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/>. + +# Return BGP peer-groups from CLI + +declare -a vals +eval "bgp_as=$(cli-shell-api listNodes protocols bgp)" +eval "vals=($(cli-shell-api listNodes protocols bgp $bgp_as peer-group))" + +echo -n ${vals[@]} +exit 0 diff --git a/src/conf_mode/dynamic_dns.py b/src/conf_mode/dynamic_dns.py index 6d39c6644..c979feca7 100755 --- a/src/conf_mode/dynamic_dns.py +++ b/src/conf_mode/dynamic_dns.py @@ -114,7 +114,7 @@ def verify(dyndns): raise ConfigError(f'"password" {error_msg}') if 'zone' in config: - if service != 'cloudflare': + if service != 'cloudflare' and ('protocol' not in config or config['protocol'] != 'cloudflare'): raise ConfigError(f'"zone" option only supported with CloudFlare') if 'custom' in config: diff --git a/src/conf_mode/http-api.py b/src/conf_mode/http-api.py index 472eb77e4..7e4b117c8 100755 --- a/src/conf_mode/http-api.py +++ b/src/conf_mode/http-api.py @@ -19,6 +19,7 @@ import sys import os import json +import time from copy import deepcopy import vyos.defaults @@ -34,11 +35,6 @@ config_file = '/etc/vyos/http-api.conf' vyos_conf_scripts_dir=vyos.defaults.directories['conf_mode'] -# XXX: this model will need to be extended for tag nodes -dependencies = [ - 'https.py', -] - def get_config(config=None): http_api = deepcopy(vyos.defaults.api_data) x = http_api.get('api_keys') @@ -103,8 +99,10 @@ def apply(http_api): else: call('systemctl stop vyos-http-api.service') - for dep in dependencies: - cmd(f'{vyos_conf_scripts_dir}/{dep}', raising=ConfigError) + # Let uvicorn settle before restarting Nginx + time.sleep(2) + + cmd(f'{vyos_conf_scripts_dir}/https.py', raising=ConfigError) if __name__ == '__main__': try: diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py index 7af3e3d7c..fd4ffed9a 100755 --- a/src/conf_mode/interfaces-bridge.py +++ b/src/conf_mode/interfaces-bridge.py @@ -41,26 +41,6 @@ from vyos import ConfigError from vyos import airbag airbag.enable() -def helper_check_removed_vlan(conf,bridge,key,key_mangling): - key_update = re.sub(key_mangling[0], key_mangling[1], key) - if dict_search('member.interface', bridge): - for interface in bridge['member']['interface']: - tmp = leaf_node_changed(conf, ['member', 'interface',interface,key]) - if tmp: - if 'member' in bridge: - if 'interface' in bridge['member']: - if interface in bridge['member']['interface']: - bridge['member']['interface'][interface].update({f'{key_update}_removed': tmp }) - else: - bridge['member']['interface'].update({interface: {f'{key_update}_removed': tmp }}) - else: - bridge['member'].update({ 'interface': {interface: {f'{key_update}_removed': tmp }}}) - else: - bridge.update({'member': { 'interface': {interface: {f'{key_update}_removed': tmp }}}}) - - return bridge - - def get_config(config=None): """ Retrive CLI config as dictionary. Dictionary can never be empty, as at least the @@ -74,18 +54,12 @@ def get_config(config=None): bridge = get_interface_dict(conf, base) # determine which members have been removed - tmp = node_changed(conf, ['member', 'interface']) + tmp = node_changed(conf, ['member', 'interface'], key_mangling=('-', '_')) if tmp: if 'member' in bridge: bridge['member'].update({'interface_remove': tmp }) else: bridge.update({'member': {'interface_remove': tmp }}) - - - # determine which members vlan have been removed - - bridge = helper_check_removed_vlan(conf,bridge,'native-vlan',('-', '_')) - bridge = helper_check_removed_vlan(conf,bridge,'allowed-vlan',('-', '_')) if dict_search('member.interface', bridge): # XXX: T2665: we need a copy of the dict keys for iteration, else we will get: @@ -99,7 +73,6 @@ def get_config(config=None): # the default dictionary is not properly paged into the dict (see T2665) # thus we will ammend it ourself default_member_values = defaults(base + ['member', 'interface']) - vlan_aware = False for interface,interface_config in bridge['member']['interface'].items(): bridge['member']['interface'][interface] = dict_merge( default_member_values, bridge['member']['interface'][interface]) @@ -120,19 +93,11 @@ def get_config(config=None): # Bridge members must not have an assigned address tmp = has_address_configured(conf, interface) if tmp: bridge['member']['interface'][interface].update({'has_address' : ''}) - + # VLAN-aware bridge members must not have VLAN interface configuration - if 'native_vlan' in interface_config: - vlan_aware = True - - if 'allowed_vlan' in interface_config: - vlan_aware = True - - - if vlan_aware: - tmp = has_vlan_subinterface_configured(conf,interface) - if tmp: - if tmp: bridge['member']['interface'][interface].update({'has_vlan' : ''}) + tmp = has_vlan_subinterface_configured(conf,interface) + if 'enable_vlan' in bridge and tmp: + bridge['member']['interface'][interface].update({'has_vlan' : ''}) return bridge @@ -142,8 +107,8 @@ def verify(bridge): verify_dhcpv6(bridge) verify_vrf(bridge) - - vlan_aware = False + + ifname = bridge['ifname'] if dict_search('member.interface', bridge): for interface, interface_config in bridge['member']['interface'].items(): @@ -166,31 +131,24 @@ def verify(bridge): if 'has_address' in interface_config: raise ConfigError(error_msg + 'it has an address assigned!') - - if 'has_vlan' in interface_config: - raise ConfigError(error_msg + 'it has an VLAN subinterface assigned!') - - # VLAN-aware bridge members must not have VLAN interface configuration - if 'native_vlan' in interface_config: - vlan_aware = True - - if 'allowed_vlan' in interface_config: - vlan_aware = True - - if vlan_aware and 'wlan' in interface: - raise ConfigError(error_msg + 'VLAN aware cannot be set!') - - if 'allowed_vlan' in interface_config: - for vlan in interface_config['allowed_vlan']: - if re.search('[0-9]{1,4}-[0-9]{1,4}', vlan): - vlan_range = vlan.split('-') - if int(vlan_range[0]) <1 and int(vlan_range[0])>4094: - raise ConfigError('VLAN ID must be between 1 and 4094') - if int(vlan_range[1]) <1 and int(vlan_range[1])>4094: - raise ConfigError('VLAN ID must be between 1 and 4094') - else: - if int(vlan) <1 and int(vlan)>4094: - raise ConfigError('VLAN ID must be between 1 and 4094') + + if 'enable_vlan' in bridge: + if 'has_vlan' in interface_config: + raise ConfigError(error_msg + 'it has an VLAN subinterface assigned!') + + if 'wlan' in interface: + raise ConfigError(error_msg + 'VLAN aware cannot be set!') + else: + for option in ['allowed_vlan', 'native_vlan']: + if option in interface_config: + raise ConfigError('Can not use VLAN options on non VLAN aware bridge') + + if 'enable_vlan' in bridge: + if dict_search('vif.1', bridge): + raise ConfigError(f'VLAN 1 sub interface cannot be set for VLAN aware bridge {ifname}, and VLAN 1 is always the parent interface') + else: + if dict_search('vif', bridge): + raise ConfigError(f'You must first activate "enable-vlan" of {ifname} bridge to use "vif"') return None diff --git a/src/conf_mode/interfaces-erspan.py b/src/conf_mode/interfaces-erspan.py new file mode 100755 index 000000000..97ae3cf55 --- /dev/null +++ b/src/conf_mode/interfaces-erspan.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2018-2020 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# 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 copy import deepcopy +from netifaces import interfaces + +from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.configdict import get_interface_dict +from vyos.configdict import node_changed +from vyos.configdict import leaf_node_changed +from vyos.configverify import verify_mtu_ipv6 +from vyos.configverify import verify_tunnel +from vyos.ifconfig import Interface +from vyos.ifconfig import ERSpanIf +from vyos.ifconfig import ER6SpanIf +from vyos.template import is_ipv4 +from vyos.template import is_ipv6 +from vyos.util import dict_search +from vyos import ConfigError +from vyos import airbag +airbag.enable() + +def get_config(config=None): + """ + Retrive CLI config as dictionary. Dictionary can never be empty, as at least + the interface name will be added or a deleted flag + """ + if config: + conf = config + else: + conf = Config() + base = ['interfaces', 'erspan'] + erspan = get_interface_dict(conf, base) + + tmp = leaf_node_changed(conf, ['encapsulation']) + if tmp: + erspan.update({'encapsulation_changed': {}}) + + return erspan + +def verify(erspan): + if 'deleted' in erspan: + return None + + if 'encapsulation' not in erspan: + raise ConfigError('Unable to detect the following ERSPAN tunnel encapsulation'\ + '{ifname}!'.format(**erspan)) + + verify_mtu_ipv6(erspan) + verify_tunnel(erspan) + + key = dict_search('parameters.ip.key',erspan) + if key == None: + raise ConfigError('parameters.ip.key is mandatory for ERSPAN tunnel') + + +def generate(erspan): + return None + +def apply(erspan): + if 'deleted' in erspan or 'encapsulation_changed' in erspan: + if erspan['ifname'] in interfaces(): + tmp = Interface(erspan['ifname']) + tmp.remove() + if 'deleted' in erspan: + return None + + dispatch = { + 'erspan': ERSpanIf, + 'ip6erspan': ER6SpanIf + } + + # We need to re-map the tunnel encapsulation proto to a valid interface class + encap = erspan['encapsulation'] + klass = dispatch[encap] + + erspan_tunnel = klass(**erspan) + erspan_tunnel.change_options() + erspan_tunnel.update(erspan) + + return None + +if __name__ == '__main__': + try: + c = get_config() + generate(c) + verify(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index bc102826f..378f400b8 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -23,13 +23,14 @@ from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configverify import verify_address from vyos.configverify import verify_dhcpv6 +from vyos.configverify import verify_eapol from vyos.configverify import verify_interface_exists +from vyos.configverify import verify_mirror from vyos.configverify import verify_mtu from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_vlan_config from vyos.configverify import verify_vrf -from vyos.configverify import verify_eapol -from vyos.configverify import verify_mirror +from vyos.ethtool import Ethtool from vyos.ifconfig import EthernetIf from vyos.template import render from vyos.util import call @@ -59,15 +60,13 @@ def verify(ethernet): if 'deleted' in ethernet: return None - verify_interface_exists(ethernet) - - if ethernet.get('speed', None) == 'auto': - if ethernet.get('duplex', None) != 'auto': - raise ConfigError('If speed is hardcoded, duplex must be hardcoded, too') + ifname = ethernet['ifname'] + verify_interface_exists(ifname) - if ethernet.get('duplex', None) == 'auto': - if ethernet.get('speed', None) != 'auto': - raise ConfigError('If duplex is hardcoded, speed must be hardcoded, too') + # No need to check speed and duplex keys as both have default values. + if ((ethernet['speed'] == 'auto' and ethernet['duplex'] != 'auto') or + (ethernet['speed'] != 'auto' and ethernet['duplex'] == 'auto')): + raise ConfigError('Speed/Duplex missmatch. Must be both auto or manually configured') verify_mtu(ethernet) verify_mtu_ipv6(ethernet) @@ -77,12 +76,38 @@ def verify(ethernet): verify_eapol(ethernet) verify_mirror(ethernet) - ifname = ethernet['ifname'] # verify offloading capabilities - if 'offload' in ethernet and 'rps' in ethernet['offload']: + if dict_search('offload.rps', ethernet) != None: if not os.path.exists(f'/sys/class/net/{ifname}/queues/rx-0/rps_cpus'): raise ConfigError('Interface does not suport RPS!') + driver = EthernetIf(ifname).get_driver_name() + # T3342 - Xen driver requires special treatment + if driver == 'vif': + if int(ethernet['mtu']) > 1500 and dict_search('offload.sg', ethernet) == None: + raise ConfigError('Xen netback drivers requires scatter-gatter offloading '\ + 'for MTU size larger then 1500 bytes') + + ethtool = Ethtool(ifname) + if 'ring_buffer' in ethernet: + max_rx = ethtool.get_rx_buffer() + if not max_rx: + raise ConfigError('Driver does not support RX ring-buffer configuration!') + + max_tx = ethtool.get_tx_buffer() + if not max_tx: + raise ConfigError('Driver does not support TX ring-buffer configuration!') + + rx = dict_search('ring_buffer.rx', ethernet) + if rx and int(rx) > int(max_rx): + raise ConfigError(f'Driver only supports a maximum RX ring-buffer '\ + f'size of "{max_rx}" bytes!') + + tx = dict_search('ring_buffer.tx', ethernet) + if tx and int(tx) > int(max_tx): + raise ConfigError(f'Driver only supports a maximum TX ring-buffer '\ + f'size of "{max_tx}" bytes!') + # XDP requires multiple TX queues if 'xdp' in ethernet: queues = glob(f'/sys/class/net/{ifname}/queues/tx-*') diff --git a/src/conf_mode/interfaces-geneve.py b/src/conf_mode/interfaces-geneve.py index 979a5612e..2a63b60aa 100755 --- a/src/conf_mode/interfaces-geneve.py +++ b/src/conf_mode/interfaces-geneve.py @@ -72,18 +72,8 @@ def apply(geneve): g.remove() if 'deleted' not in geneve: - # This is a special type of interface which needs additional parameters - # when created using iproute2. Instead of passing a ton of arguments, - # use a dictionary provided by the interface class which holds all the - # options necessary. - conf = GeneveIf.get_config() - - # Assign GENEVE instance configuration parameters to config dict - conf['vni'] = geneve['vni'] - conf['remote'] = geneve['remote'] - # Finally create the new interface - g = GeneveIf(geneve['ifname'], **conf) + g = GeneveIf(**geneve) g.update(geneve) return None diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py index 1118143e4..9b6ddd5aa 100755 --- a/src/conf_mode/interfaces-l2tpv3.py +++ b/src/conf_mode/interfaces-l2tpv3.py @@ -34,7 +34,6 @@ airbag.enable() k_mod = ['l2tp_eth', 'l2tp_netlink', 'l2tp_ip', 'l2tp_ip6'] - def get_config(config=None): """ Retrive CLI config as dictionary. Dictionary can never be empty, as at least the @@ -47,12 +46,6 @@ def get_config(config=None): base = ['interfaces', 'l2tpv3'] l2tpv3 = get_interface_dict(conf, base) - # L2TPv3 is "special" the default MTU is 1488 - update accordingly - # as the config_level is already st in get_interface_dict() - we can use [] - tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) - if 'mtu' not in tmp: - l2tpv3['mtu'] = '1488' - # To delete an l2tpv3 interface we need the current tunnel and session-id if 'deleted' in l2tpv3: tmp = leaf_node_changed(conf, ['tunnel-id']) @@ -71,15 +64,15 @@ def verify(l2tpv3): interface = l2tpv3['ifname'] - for key in ['local_ip', 'remote_ip', 'tunnel_id', 'peer_tunnel_id', + for key in ['source_address', 'remote', 'tunnel_id', 'peer_tunnel_id', 'session_id', 'peer_session_id']: if key not in l2tpv3: tmp = key.replace('_', '-') - raise ConfigError(f'L2TPv3 {tmp} must be configured!') + raise ConfigError(f'Missing mandatory L2TPv3 option: "{tmp}"!') - if not is_addr_assigned(l2tpv3['local_ip']): - raise ConfigError('L2TPv3 local-ip address ' - '"{local_ip}" is not configured!'.format(**l2tpv3)) + if not is_addr_assigned(l2tpv3['source_address']): + raise ConfigError('L2TPv3 source-address address "{source_address}" ' + 'not configured on any interface!'.format(**l2tpv3)) verify_mtu_ipv6(l2tpv3) verify_address(l2tpv3) @@ -89,34 +82,16 @@ def generate(l2tpv3): return None def apply(l2tpv3): - # This is a special type of interface which needs additional parameters - # when created using iproute2. Instead of passing a ton of arguments, - # use a dictionary provided by the interface class which holds all the - # options necessary. - conf = L2TPv3If.get_config() - # Check if L2TPv3 interface already exists if l2tpv3['ifname'] in interfaces(): # L2TPv3 is picky when changing tunnels/sessions, thus we can simply # always delete it first. - conf['session_id'] = l2tpv3['session_id'] - conf['tunnel_id'] = l2tpv3['tunnel_id'] - l = L2TPv3If(l2tpv3['ifname'], **conf) + l = L2TPv3If(**l2tpv3) l.remove() if 'deleted' not in l2tpv3: - conf['peer_tunnel_id'] = l2tpv3['peer_tunnel_id'] - conf['local_port'] = l2tpv3['source_port'] - conf['remote_port'] = l2tpv3['destination_port'] - conf['encapsulation'] = l2tpv3['encapsulation'] - conf['local_address'] = l2tpv3['local_ip'] - conf['remote_address'] = l2tpv3['remote_ip'] - conf['session_id'] = l2tpv3['session_id'] - conf['tunnel_id'] = l2tpv3['tunnel_id'] - conf['peer_session_id'] = l2tpv3['peer_session_id'] - # Finally create the new interface - l = L2TPv3If(l2tpv3['ifname'], **conf) + l = L2TPv3If(**l2tpv3) l.update(l2tpv3) return None diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index 2c8367ff3..eab69f36e 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -49,14 +49,6 @@ def get_config(config=None): base = ['interfaces', 'macsec'] macsec = get_interface_dict(conf, base) - # MACsec is "special" the default MTU is 1460 - update accordingly - # as the config_level is already st in get_interface_dict() - we can use [] - tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) - if 'mtu' not in tmp: - # base MTU for MACsec is 1468 bytes, but we leave room for 802.1ad and - # 802.1q VLAN tags, thus the limit is 1460 bytes. - macsec['mtu'] = '1460' - # Check if interface has been removed if 'deleted' in macsec: source_interface = conf.return_effective_value(['source-interface']) @@ -123,17 +115,9 @@ def apply(macsec): os.unlink(wpa_suppl_conf.format(**macsec)) else: - # This is a special type of interface which needs additional parameters - # when created using iproute2. Instead of passing a ton of arguments, - # use a dictionary provided by the interface class which holds all the - # options necessary. - conf = MACsecIf.get_config() - conf['source_interface'] = macsec['source_interface'] - conf['security_cipher'] = macsec['security']['cipher'] - # It is safe to "re-create" the interface always, there is a sanity # check that the interface will only be create if its non existent - i = MACsecIf(macsec['ifname'], **conf) + i = MACsecIf(**macsec) i.update(macsec) call('systemctl restart wpa_supplicant-macsec@{source_interface}' diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index e4a6a5ec1..4afb85526 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -17,6 +17,7 @@ import os import re +from glob import glob from sys import exit from ipaddress import IPv4Address from ipaddress import IPv4Network @@ -488,14 +489,9 @@ def apply(openvpn): # Do some cleanup when OpenVPN is disabled/deleted if 'deleted' in openvpn or 'disable' in openvpn: - # cleanup old configuration files - cleanup = [] - cleanup.append(cfg_file.format(**openvpn)) - cleanup.append(openvpn['auth_user_pass_file']) - - for file in cleanup: - if os.path.isfile(file): - os.unlink(file) + for cleanup_file in glob(f'/run/openvpn/{interface}.*'): + if os.path.isfile(cleanup_file): + os.unlink(cleanup_file) if interface in interfaces(): VTunIf(interface).remove() @@ -506,10 +502,7 @@ def apply(openvpn): # existed - nevertheless, spawn new OpenVPN process call(f'systemctl start openvpn@{interface}.service') - conf = VTunIf.get_config() - conf['device_type'] = openvpn['device_type'] - - o = VTunIf(interface, **conf) + o = VTunIf(**openvpn) o.update(openvpn) return None diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py index c31e49574..3675db73b 100755 --- a/src/conf_mode/interfaces-pppoe.py +++ b/src/conf_mode/interfaces-pppoe.py @@ -43,12 +43,6 @@ def get_config(config=None): base = ['interfaces', 'pppoe'] pppoe = get_interface_dict(conf, base) - # PPPoE is "special" the default MTU is 1492 - update accordingly - # as the config_level is already st in get_interface_dict() - we can use [] - tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) - if 'mtu' not in tmp: - pppoe['mtu'] = '1492' - return pppoe def verify(pppoe): @@ -79,7 +73,7 @@ def generate(pppoe): config_files = [config_pppoe, script_pppoe_pre_up, script_pppoe_ip_up, script_pppoe_ip_down, script_pppoe_ipv6_up, config_wide_dhcp6c] - if 'deleted' in pppoe: + if 'deleted' in pppoe or 'disable' in pppoe: # stop DHCPv6-PD client call(f'systemctl stop dhcp6c@{ifname}.service') # Hang-up PPPoE connection @@ -116,13 +110,11 @@ def generate(pppoe): return None def apply(pppoe): - if 'deleted' in pppoe: - # bail out early + if 'deleted' in pppoe or 'disable' in pppoe: + call('systemctl stop ppp@{ifname}.service'.format(**pppoe)) return None - if 'disable' not in pppoe: - # Dial PPPoE connection - call('systemctl restart ppp@{ifname}.service'.format(**pppoe)) + call('systemctl restart ppp@{ifname}.service'.format(**pppoe)) return None diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py index ddbef56ac..34a054837 100755 --- a/src/conf_mode/interfaces-pseudo-ethernet.py +++ b/src/conf_mode/interfaces-pseudo-ethernet.py @@ -75,19 +75,9 @@ def apply(peth): if 'mode_old' in peth: MACVLANIf(peth['ifname']).remove() - # This is a special type of interface which needs additional parameters - # when created using iproute2. Instead of passing a ton of arguments, - # use a dictionary provided by the interface class which holds all the - # options necessary. - conf = MACVLANIf.get_config() - - # Assign MACVLAN instance configuration parameters to config dict - conf['source_interface'] = peth['source_interface'] - conf['mode'] = peth['mode'] - # It is safe to "re-create" the interface always, there is a sanity check # that the interface will only be create if its non existent - p = MACVLANIf(peth['ifname'], **conf) + p = MACVLANIf(**peth) p.update(peth) return None diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py index 1a7e9a96d..cab94a5b0 100755 --- a/src/conf_mode/interfaces-tunnel.py +++ b/src/conf_mode/interfaces-tunnel.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-2021 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 @@ -24,21 +24,17 @@ from vyos.configdict import dict_merge from vyos.configdict import get_interface_dict from vyos.configdict import node_changed from vyos.configdict import leaf_node_changed -from vyos.configverify import verify_vrf from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete +from vyos.configverify import verify_interface_exists from vyos.configverify import verify_mtu_ipv6 +from vyos.configverify import verify_vrf +from vyos.configverify import verify_tunnel from vyos.ifconfig import Interface -from vyos.ifconfig import GREIf -from vyos.ifconfig import GRETapIf -from vyos.ifconfig import IPIPIf -from vyos.ifconfig import IP6GREIf -from vyos.ifconfig import IPIP6If -from vyos.ifconfig import IP6IP6If -from vyos.ifconfig import SitIf -from vyos.ifconfig import Sit6RDIf +from vyos.ifconfig import TunnelIf from vyos.template import is_ipv4 from vyos.template import is_ipv6 +from vyos.util import get_interface_config from vyos.util import dict_search from vyos import ConfigError from vyos import airbag @@ -56,12 +52,6 @@ def get_config(config=None): base = ['interfaces', 'tunnel'] tunnel = get_interface_dict(conf, base) - # Wireguard is "special" the default MTU is 1420 - update accordingly - # as the config_level is already st in get_interface_dict() - we can use [] - tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) - if 'mtu' not in tmp: - tunnel['mtu'] = '1476' - tmp = leaf_node_changed(conf, ['encapsulation']) if tmp: tunnel.update({'encapsulation_changed': {}}) @@ -83,103 +73,50 @@ def verify(tunnel): return None if 'encapsulation' not in tunnel: - raise ConfigError('Must configure the tunnel encapsulation for '\ - '{ifname}!'.format(**tunnel)) + error = 'Must configure encapsulation for "{ifname}"!' + raise ConfigError(error.format(**tunnel)) verify_mtu_ipv6(tunnel) verify_address(tunnel) verify_vrf(tunnel) + verify_tunnel(tunnel) - if 'local_ip' not in tunnel and 'dhcp_interface' not in tunnel: - raise ConfigError('local-ip is mandatory for tunnel') - - if 'remote_ip' not in tunnel and tunnel['encapsulation'] != 'gre': - raise ConfigError('remote-ip is mandatory for tunnel') - - if {'local_ip', 'dhcp_interface'} <= set(tunnel): - raise ConfigError('Can not use both local-ip and dhcp-interface') - - if tunnel['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre']: - error_ipv6 = 'Encapsulation mode requires IPv6' - if 'local_ip' in tunnel and not is_ipv6(tunnel['local_ip']): - raise ConfigError(f'{error_ipv6} local-ip') - - if 'remote_ip' in tunnel and not is_ipv6(tunnel['remote_ip']): - raise ConfigError(f'{error_ipv6} remote-ip') - else: - error_ipv4 = 'Encapsulation mode requires IPv4' - if 'local_ip' in tunnel and not is_ipv4(tunnel['local_ip']): - raise ConfigError(f'{error_ipv4} local-ip') + if 'source_interface' in tunnel: + verify_interface_exists(tunnel['source_interface']) - if 'remote_ip' in tunnel and not is_ipv4(tunnel['remote_ip']): - raise ConfigError(f'{error_ipv4} remote-ip') + # TTL != 0 and nopmtudisc are incompatible, parameters and ip use default + # values, thus the keys are always present. + if dict_search('parameters.ip.no_pmtu_discovery', tunnel) != None: + if dict_search('parameters.ip.ttl', tunnel) != '0': + raise ConfigError('Disabled PMTU requires TTL set to "0"!') + if tunnel['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre']: + raise ConfigError('Can not disable PMTU discovery for given encapsulation') - if tunnel['encapsulation'] in ['sit', 'gre-bridge']: - if 'source_interface' in tunnel: - raise ConfigError('Option source-interface can not be used with ' \ - 'encapsulation "sit" or "gre-bridge"') - elif tunnel['encapsulation'] == 'gre': - if 'local_ip' in tunnel and is_ipv6(tunnel['local_ip']): - raise ConfigError('Can not use local IPv6 address is for mGRE tunnels') def generate(tunnel): return None def apply(tunnel): - if 'deleted' in tunnel or 'encapsulation_changed' in tunnel: - if tunnel['ifname'] in interfaces(): - tmp = Interface(tunnel['ifname']) + interface = tunnel['ifname'] + # If a gretap tunnel is already existing we can not "simply" change local or + # remote addresses. This returns "Operation not supported" by the Kernel. + # There is no other solution to destroy and recreate the tunnel. + encap = '' + remote = '' + tmp = get_interface_config(interface) + if tmp: + encap = dict_search('linkinfo.info_kind', tmp) + remote = dict_search('linkinfo.info_data.remote', tmp) + + if ('deleted' in tunnel or 'encapsulation_changed' in tunnel or + encap in ['gretap', 'ip6gretap'] or remote in ['any']): + if interface in interfaces(): + tmp = Interface(interface) tmp.remove() if 'deleted' in tunnel: return None - dispatch = { - 'gre': GREIf, - 'gre-bridge': GRETapIf, - 'ipip': IPIPIf, - 'ipip6': IPIP6If, - 'ip6ip6': IP6IP6If, - 'ip6gre': IP6GREIf, - 'sit': SitIf, - } - - # We need to re-map the tunnel encapsulation proto to a valid interface class - encap = tunnel['encapsulation'] - klass = dispatch[encap] - - # This is a special type of interface which needs additional parameters - # when created using iproute2. Instead of passing a ton of arguments, - # use a dictionary provided by the interface class which holds all the - # options necessary. - conf = klass.get_config() - - # Copy/re-assign our dictionary values to values understood by the - # derived _Tunnel classes - mapping = { - # this : get_config() - 'local_ip' : 'local', - 'remote_ip' : 'remote', - 'source_interface' : 'dev', - 'parameters.ip.ttl' : 'ttl', - 'parameters.ip.tos' : 'tos', - 'parameters.ip.key' : 'key', - 'parameters.ipv6.encaplimit' : 'encaplimit' - } - - # Add additional IPv6 options if tunnel is IPv6 aware - if tunnel['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre']: - mappingv6 = { - # this : get_config() - 'parameters.ipv6.encaplimit' : 'encaplimit' - } - mapping.update(mappingv6) - - for our_key, their_key in mapping.items(): - if dict_search(our_key, tunnel) and their_key in conf: - conf[their_key] = dict_search(our_key, tunnel) - - tun = klass(tunnel['ifname'], **conf) - tun.change_options() + tun = TunnelIf(**tunnel) tun.update(tunnel) return None diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py index 04e258fcf..8e6247a30 100755 --- a/src/conf_mode/interfaces-vxlan.py +++ b/src/conf_mode/interfaces-vxlan.py @@ -42,12 +42,6 @@ def get_config(config=None): base = ['interfaces', 'vxlan'] vxlan = get_interface_dict(conf, base) - # VXLAN is "special" the default MTU is 1492 - update accordingly - # as the config_level is already st in get_interface_dict() - we can use [] - tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) - if 'mtu' not in tmp: - vxlan['mtu'] = '1450' - return vxlan def verify(vxlan): @@ -96,19 +90,8 @@ def apply(vxlan): v.remove() if 'deleted' not in vxlan: - # This is a special type of interface which needs additional parameters - # when created using iproute2. Instead of passing a ton of arguments, - # use a dictionary provided by the interface class which holds all the - # options necessary. - conf = VXLANIf.get_config() - - # Assign VXLAN instance configuration parameters to config dict - for tmp in ['vni', 'group', 'source_address', 'source_interface', 'remote', 'port']: - if tmp in vxlan: - conf[tmp] = vxlan[tmp] - # Finally create the new interface - v = VXLANIf(vxlan['ifname'], **conf) + v = VXLANIf(**vxlan) v.update(vxlan) return None diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py index 7cfc76aa0..024ab8f59 100755 --- a/src/conf_mode/interfaces-wireguard.py +++ b/src/conf_mode/interfaces-wireguard.py @@ -46,19 +46,13 @@ def get_config(config=None): base = ['interfaces', 'wireguard'] wireguard = get_interface_dict(conf, base) - # Wireguard is "special" the default MTU is 1420 - update accordingly - # as the config_level is already st in get_interface_dict() - we can use [] - tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) - if 'mtu' not in tmp: - wireguard['mtu'] = '1420' - # Mangle private key - it has a default so its always valid wireguard['private_key'] = '/config/auth/wireguard/{private_key}/private.key'.format(**wireguard) # Determine which Wireguard peer has been removed. # Peers can only be removed with their public key! dict = {} - tmp = node_changed(conf, ['peer']) + tmp = node_changed(conf, ['peer'], key_mangling=('-', '_')) for peer in (tmp or []): pubkey = leaf_node_changed(conf, ['peer', peer, 'pubkey']) if pubkey: diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py index b25fcd4e0..7b3de6e8a 100755 --- a/src/conf_mode/interfaces-wireless.py +++ b/src/conf_mode/interfaces-wireless.py @@ -255,17 +255,8 @@ def apply(wifi): if 'deleted' in wifi: WiFiIf(interface).remove() else: - # This is a special type of interface which needs additional parameters - # when created using iproute2. Instead of passing a ton of arguments, - # use a dictionary provided by the interface class which holds all the - # options necessary. - conf = WiFiIf.get_config() - - # Assign WiFi instance configuration parameters to config dict - conf['phy'] = wifi['physical_device'] - # Finally create the new interface - w = WiFiIf(interface, **conf) + w = WiFiIf(**wifi) w.update(wifi) # Enable/Disable interface - interface is always placed in diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py index 6b645857a..082c3e128 100755 --- a/src/conf_mode/lldp.py +++ b/src/conf_mode/lldp.py @@ -21,7 +21,8 @@ from copy import deepcopy from sys import exit from vyos.config import Config -from vyos.validate import is_addr_assigned,is_loopback_addr +from vyos.validate import is_addr_assigned +from vyos.validate import is_loopback_addr from vyos.version import get_version_data from vyos import ConfigError from vyos.util import call @@ -237,8 +238,10 @@ def apply(lldp): else: # LLDP service has been terminated call('systemctl stop lldpd.service') - os.unlink(config_file) - os.unlink(vyos_config_file) + if os.path.isfile(config_file): + os.unlink(config_file) + if os.path.isfile(vyos_config_file): + os.unlink(vyos_config_file) if __name__ == '__main__': try: diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index 1ccec3d2e..dae958774 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 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 @@ -26,6 +26,7 @@ from netifaces import interfaces from vyos.config import Config from vyos.configdict import dict_merge from vyos.template import render +from vyos.template import is_ip_network from vyos.util import cmd from vyos.util import check_kmod from vyos.util import dict_search @@ -68,9 +69,9 @@ def verify_rule(config, err_msg): 'ports can only be specified when protocol is '\ 'either tcp, udp or tcp_udp!') - if '/' in (dict_search('translation.address', config) or []): + if is_ip_network(dict_search('translation.address', config)): raise ConfigError(f'{err_msg}\n' \ - 'Cannot use ports with an IPv4net type translation address as it\n' \ + 'Cannot use ports with an IPv4 network as translation address as it\n' \ 'statically maps a whole network of addresses onto another\n' \ 'network of addresses') @@ -88,7 +89,7 @@ def get_config(config=None): for direction in ['source', 'destination']: if direction in nat: default_values = defaults(base + [direction, 'rule']) - for rule in nat[direction]['rule']: + for rule in dict_search(f'{direction}.rule', nat) or []: nat[direction]['rule'][rule] = dict_merge(default_values, nat[direction]['rule'][rule]) @@ -147,7 +148,7 @@ def verify(nat): addr = dict_search('translation.address', config) if addr != None: - if addr != 'masquerade': + if addr != 'masquerade' and not is_ip_network(addr): for ip in addr.split('-'): if not is_addr_assigned(ip): print(f'WARNING: IP address {ip} does not exist on the system!') diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py new file mode 100755 index 000000000..e2bd6417d --- /dev/null +++ b/src/conf_mode/nat66.py @@ -0,0 +1,175 @@ +#!/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 jmespath +import json +import os + +from sys import exit +from netifaces import interfaces + +from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.template import render +from vyos.util import cmd +from vyos.util import check_kmod +from vyos.util import dict_search +from vyos.template import is_ipv6 +from vyos.xml import defaults +from vyos import ConfigError +from vyos import airbag +airbag.enable() + +k_mod = ['nft_nat', 'nft_chain_nat'] + +iptables_nat_config = '/tmp/vyos-nat66-rules.nft' +ndppd_config = '/run/ndppd/ndppd.conf' + +def get_handler(json, chain, target): + """ Get nftable rule handler number of given chain/target combination. + Handler is required when adding NAT66/Conntrack helper targets """ + for x in json: + if x['chain'] != chain: + continue + if x['target'] != target: + continue + return x['handle'] + + return None + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + + base = ['nat66'] + nat = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + + # T2665: we must add the tagNode defaults individually until this is + # moved to the base class + for direction in ['source', 'destination']: + if direction in nat: + default_values = defaults(base + [direction, 'rule']) + if 'rule' in nat[direction]: + for rule in nat[direction]['rule']: + nat[direction]['rule'][rule] = dict_merge(default_values, + nat[direction]['rule'][rule]) + + # read in current nftable (once) for further processing + tmp = cmd('nft -j list table ip6 raw') + nftable_json = json.loads(tmp) + + # condense the full JSON table into a list with only relevand informations + pattern = 'nftables[?rule].rule[?expr[].jump].{chain: chain, handle: handle, target: expr[].jump.target | [0]}' + condensed_json = jmespath.search(pattern, nftable_json) + + if not conf.exists(base): + nat['helper_functions'] = 'remove' + nat['pre_ct_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYATTA_CT_HELPER') + nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK') + nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYATTA_CT_HELPER') + nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'NAT_CONNTRACK') + nat['deleted'] = '' + return nat + + # check if NAT66 connection tracking helpers need to be set up - this has to + # be done only once + if not get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK'): + nat['helper_functions'] = 'add' + + # Retrieve current table handler positions + nat['pre_ct_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYATTA_CT_IGNORE') + nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'VYATTA_CT_PREROUTING_HOOK') + nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYATTA_CT_IGNORE') + nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'VYATTA_CT_OUTPUT_HOOK') + else: + nat['helper_functions'] = 'has' + + return nat + +def verify(nat): + if not nat or 'deleted' in nat: + # no need to verify the CLI as NAT66 is going to be deactivated + return None + + if 'helper_functions' in nat and nat['helper_functions'] != 'has': + if not (nat['pre_ct_conntrack'] or nat['out_ct_conntrack']): + raise Exception('could not determine nftable ruleset handlers') + + if dict_search('source.rule', nat): + for rule, config in dict_search('source.rule', nat).items(): + err_msg = f'Source NAT66 configuration error in rule {rule}:' + if 'outbound_interface' not in config: + raise ConfigError(f'{err_msg}\n' \ + 'outbound-interface not specified') + else: + if config['outbound_interface'] not in interfaces(): + print(f'WARNING: rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system') + + addr = dict_search('translation.address', config) + if addr != None: + if addr != 'masquerade' and not is_ipv6(addr): + raise ConfigError(f'Warning: IPv6 address {addr} is not a valid address') + + prefix = dict_search('source.prefix', config) + if prefix != None: + if not is_ipv6(prefix): + raise ConfigError(f'{err_msg} source-prefix not specified') + + if dict_search('destination.rule', nat): + for rule, config in dict_search('destination.rule', nat).items(): + err_msg = f'Destination NAT66 configuration error in rule {rule}:' + + if 'inbound_interface' not in config: + raise ConfigError(f'{err_msg}\n' \ + 'inbound-interface not specified') + else: + if config['inbound_interface'] not in 'any' and config['inbound_interface'] not in interfaces(): + print(f'WARNING: rule "{rule}" interface "{config["inbound_interface"]}" does not exist on this system') + + return None + +def generate(nat): + render(iptables_nat_config, 'firewall/nftables-nat66.tmpl', nat, permission=0o755) + render(ndppd_config, 'proxy-ndp/ndppd.conf.tmpl', nat, permission=0o755) + return None + +def apply(nat): + if not nat: + return None + cmd(f'{iptables_nat_config}') + if 'deleted' in nat or not dict_search('source.rule', nat): + cmd('systemctl stop ndppd') + if os.path.isfile(ndppd_config): + os.unlink(ndppd_config) + else: + cmd('systemctl restart ndppd') + if os.path.isfile(iptables_nat_config): + os.unlink(iptables_nat_config) + + return None + +if __name__ == '__main__': + try: + check_kmod(k_mod) + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/conf_mode/ntp.py b/src/conf_mode/ntp.py index b102b3e9e..52070aabc 100755 --- a/src/conf_mode/ntp.py +++ b/src/conf_mode/ntp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-2021 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 @@ -24,7 +24,7 @@ from vyos.template import render from vyos import airbag airbag.enable() -config_file = r'/etc/ntp.conf' +config_file = r'/run/ntpd/ntpd.conf' systemd_override = r'/etc/systemd/system/ntp.service.d/override.conf' def get_config(config=None): @@ -33,8 +33,11 @@ def get_config(config=None): else: conf = Config() base = ['system', 'ntp'] + if not conf.exists(base): + return None ntp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + ntp['config_file'] = config_file return ntp def verify(ntp): @@ -42,7 +45,7 @@ def verify(ntp): if not ntp: return None - if len(ntp.get('allow_clients', {})) and not (len(ntp.get('server', {})) > 0): + if 'allow_clients' in ntp and 'server' not in ntp: raise ConfigError('NTP server not configured') verify_vrf(ntp) @@ -53,7 +56,7 @@ def generate(ntp): if not ntp: return None - render(config_file, 'ntp/ntp.conf.tmpl', ntp) + render(config_file, 'ntp/ntpd.conf.tmpl', ntp) render(systemd_override, 'ntp/override.conf.tmpl', ntp) return None diff --git a/src/conf_mode/policy-lists.py b/src/conf_mode/policy-lists.py new file mode 100755 index 000000000..94a020e7b --- /dev/null +++ b/src/conf_mode/policy-lists.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os + +from sys import exit + +from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.template import render +from vyos.template import render_to_string +from vyos.util import call +from vyos.util import dict_search +from vyos import ConfigError +from vyos import frr +from vyos import airbag +from pprint import pprint +airbag.enable() + +config_file = r'/tmp/policy.frr' +frr_daemon = 'zebra' + +DEBUG = os.path.exists('/tmp/policy.debug') +if DEBUG: + import logging + lg = logging.getLogger("vyos.frr") + lg.setLevel(logging.DEBUG) + ch = logging.StreamHandler() + lg.addHandler(ch) + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + base = ['npolicy'] + policy = conf.get_config_dict(base, key_mangling=('-', '_')) + + # Bail out early if configuration tree does not exist + if not conf.exists(base): + return policy + + pprint(policy) + exit(1) + return policy + +def verify(policy): + if not policy: + return None + + return None + +def generate(policy): + if not policy: + policy['new_frr_config'] = '' + return None + + # render(config) not needed, its only for debug + # render(config_file, 'frr/policy.frr.tmpl', policy) + # policy['new_frr_config'] = render_to_string('frr/policy.frr.tmpl') + + return None + +def apply(policy): + # Save original configuration prior to starting any commit actions + # frr_cfg = frr.FRRConfig() + # frr_cfg.load_configuration(frr_daemon) + # frr_cfg.modify_section(f'ip', '') + # frr_cfg.add_before(r'(line vty)', policy['new_frr_config']) + + # Debugging + if DEBUG: + from pprint import pprint + print('') + print('--------- DEBUGGING ----------') + pprint(dir(frr_cfg)) + print('Existing config:\n') + for line in frr_cfg.original_config: + print(line) + print(f'Replacement config:\n') + print(f'{policy["new_frr_config"]}') + print(f'Modified config:\n') + print(f'{frr_cfg}') + + # frr_cfg.commit_configuration(frr_daemon) + + # 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 policy['new_frr_config'] == '': + # for a in range(5): + # frr_cfg.commit_configuration(frr_daemon) + + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/conf_mode/policy-local-route.py b/src/conf_mode/policy-local-route.py index c4024dce4..013f22665 100755 --- a/src/conf_mode/policy-local-route.py +++ b/src/conf_mode/policy-local-route.py @@ -40,7 +40,7 @@ def get_config(config=None): # delete policy local-route dict = {} - tmp = node_changed(conf, ['policy', 'local-route', 'rule']) + tmp = node_changed(conf, ['policy', 'local-route', 'rule'], key_mangling=('-', '_')) if tmp: for rule in (tmp or []): src = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'source']) diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py index d1e551cad..a43eed504 100755 --- a/src/conf_mode/protocols_bfd.py +++ b/src/conf_mode/protocols_bfd.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2020 VyOS maintainers and contributors +# Copyright (C) 2019-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -17,191 +17,97 @@ import os from sys import exit -from copy import deepcopy from vyos.config import Config +from vyos.configdict import dict_merge from vyos.template import is_ipv6 -from vyos.template import render +from vyos.template import render_to_string from vyos.util import call from vyos.validate import is_ipv6_link_local +from vyos.xml import defaults from vyos import ConfigError +from vyos import frr from vyos import airbag airbag.enable() -config_file = r'/tmp/bfd.frr' - -default_config_data = { - 'new_peers': [], - 'old_peers' : [] -} - -# get configuration for BFD peer from proposed or effective configuration -def get_bfd_peer_config(peer, conf_mode="proposed"): - conf = Config() - conf.set_level('protocols bfd peer {0}'.format(peer)) - - bfd_peer = { - 'remote': peer, - 'shutdown': False, - 'src_if': '', - 'src_addr': '', - 'multiplier': '3', - 'rx_interval': '300', - 'tx_interval': '300', - 'multihop': False, - 'echo_interval': '', - 'echo_mode': False, - } - - # Check if individual peer is disabled - if conf_mode == "effective" and conf.exists_effective('shutdown'): - bfd_peer['shutdown'] = True - if conf_mode == "proposed" and conf.exists('shutdown'): - bfd_peer['shutdown'] = True - - # Check if peer has a local source interface configured - if conf_mode == "effective" and conf.exists_effective('source interface'): - bfd_peer['src_if'] = conf.return_effective_value('source interface') - if conf_mode == "proposed" and conf.exists('source interface'): - bfd_peer['src_if'] = conf.return_value('source interface') - - # Check if peer has a local source address configured - this is mandatory for IPv6 - if conf_mode == "effective" and conf.exists_effective('source address'): - bfd_peer['src_addr'] = conf.return_effective_value('source address') - if conf_mode == "proposed" and conf.exists('source address'): - bfd_peer['src_addr'] = conf.return_value('source address') - - # Tell BFD daemon that we should expect packets with TTL less than 254 - # (because it will take more than one hop) and to listen on the multihop - # port (4784) - if conf_mode == "effective" and conf.exists_effective('multihop'): - bfd_peer['multihop'] = True - if conf_mode == "proposed" and conf.exists('multihop'): - bfd_peer['multihop'] = True - - # Configures the minimum interval that this system is capable of receiving - # control packets. The default value is 300 milliseconds. - if conf_mode == "effective" and conf.exists_effective('interval receive'): - bfd_peer['rx_interval'] = conf.return_effective_value('interval receive') - if conf_mode == "proposed" and conf.exists('interval receive'): - bfd_peer['rx_interval'] = conf.return_value('interval receive') - - # The minimum transmission interval (less jitter) that this system wants - # to use to send BFD control packets. - if conf_mode == "effective" and conf.exists_effective('interval transmit'): - bfd_peer['tx_interval'] = conf.return_effective_value('interval transmit') - if conf_mode == "proposed" and conf.exists('interval transmit'): - bfd_peer['tx_interval'] = conf.return_value('interval transmit') - - # Configures the detection multiplier to determine packet loss. The remote - # transmission interval will be multiplied by this value to determine the - # connection loss detection timer. The default value is 3. - if conf_mode == "effective" and conf.exists_effective('interval multiplier'): - bfd_peer['multiplier'] = conf.return_effective_value('interval multiplier') - if conf_mode == "proposed" and conf.exists('interval multiplier'): - bfd_peer['multiplier'] = conf.return_value('interval multiplier') - - # Configures the minimal echo receive transmission interval that this system is capable of handling - if conf_mode == "effective" and conf.exists_effective('interval echo-interval'): - bfd_peer['echo_interval'] = conf.return_effective_value('interval echo-interval') - if conf_mode == "proposed" and conf.exists('interval echo-interval'): - bfd_peer['echo_interval'] = conf.return_value('interval echo-interval') - - # Enables or disables the echo transmission mode - if conf_mode == "effective" and conf.exists_effective('echo-mode'): - bfd_peer['echo_mode'] = True - if conf_mode == "proposed" and conf.exists('echo-mode'): - bfd_peer['echo_mode'] = True - - return bfd_peer - -def get_config(): - bfd = deepcopy(default_config_data) - conf = Config() - if not (conf.exists('protocols bfd') or conf.exists_effective('protocols bfd')): - return None +def get_config(config=None): + if config: + conf = config else: - conf.set_level('protocols bfd') - - # as we have to use vtysh to talk to FRR we also need to know - # which peers are gone due to a config removal - thus we read in - # all peers (active or to delete) - for peer in conf.list_effective_nodes('peer'): - bfd['old_peers'].append(get_bfd_peer_config(peer, "effective")) - - for peer in conf.list_nodes('peer'): - bfd['new_peers'].append(get_bfd_peer_config(peer)) - - # find deleted peers - set_new_peers = set(conf.list_nodes('peer')) - set_old_peers = set(conf.list_effective_nodes('peer')) - bfd['deleted_peers'] = set_old_peers - set_new_peers + conf = Config() + base = ['protocols', 'bfd'] + bfd = conf.get_config_dict(base, get_first_key=True) + + # Bail out early if configuration tree does not exist + if not conf.exists(base): + return bfd + + # We have gathered the dict representation of the CLI, but there are + # default options which we need to update into the dictionary retrived. + # XXX: T2665: we currently have no nice way for defaults under tag + # nodes, thus we load the defaults "by hand" + default_values = defaults(base + ['peer']) + if 'peer' in bfd: + for peer in bfd['peer']: + bfd['peer'][peer] = dict_merge(default_values, bfd['peer'][peer]) + + if 'profile' in bfd: + for profile in bfd['profile']: + bfd['profile'][profile] = dict_merge(default_values, bfd['profile'][profile]) return bfd def verify(bfd): - if bfd is None: + if not bfd: return None - # some variables to use later - conf = Config() - - for peer in bfd['new_peers']: - # IPv6 link local peers require an explicit local address/interface - if is_ipv6_link_local(peer['remote']): - if not (peer['src_if'] and peer['src_addr']): - raise ConfigError('BFD IPv6 link-local peers require explicit local address and interface setting') - - # IPv6 peers require an explicit local address - if is_ipv6(peer['remote']): - if not peer['src_addr']: - raise ConfigError('BFD IPv6 peers require explicit local address setting') - - # multihop require source address - if peer['multihop'] and not peer['src_addr']: - raise ConfigError('Multihop require source address') - - # multihop and echo-mode cannot be used together - if peer['multihop'] and peer['echo_mode']: - raise ConfigError('Multihop and echo-mode cannot be used together') - - # multihop doesn't accept interface names - if peer['multihop'] and peer['src_if']: - raise ConfigError('Multihop and source interface cannot be used together') - - # echo interval can be configured only with enabled echo-mode - if peer['echo_interval'] != '' and not peer['echo_mode']: - raise ConfigError('echo-interval can be configured only with enabled echo-mode') - - # check if we deleted peers are not used in configuration - if conf.exists('protocols bgp'): - bgp_as = conf.list_nodes('protocols bgp')[0] - - # check BGP neighbors - for peer in bfd['deleted_peers']: - if conf.exists('protocols bgp {0} neighbor {1} bfd'.format(bgp_as, peer)): - raise ConfigError('Cannot delete BFD peer {0}: it is used in BGP configuration'.format(peer)) - if conf.exists('protocols bgp {0} neighbor {1} peer-group'.format(bgp_as, peer)): - peer_group = conf.return_value('protocols bgp {0} neighbor {1} peer-group'.format(bgp_as, peer)) - if conf.exists('protocols bgp {0} peer-group {1} bfd'.format(bgp_as, peer_group)): - raise ConfigError('Cannot delete BFD peer {0}: it belongs to BGP peer-group {1} with enabled BFD'.format(peer, peer_group)) + if 'peer' in bfd: + for peer, peer_config in bfd['peer'].items(): + # IPv6 link local peers require an explicit local address/interface + if is_ipv6_link_local(peer): + if 'source' not in peer_config or len(peer_config['source'] < 2): + raise ConfigError('BFD IPv6 link-local peers require explicit local address and interface setting') + + # IPv6 peers require an explicit local address + if is_ipv6(peer): + if 'source' not in peer_config or 'address' not in peer_config['source']: + raise ConfigError('BFD IPv6 peers require explicit local address setting') + + if 'multihop' in peer_config: + # multihop require source address + if 'source' not in peer_config or 'address' not in peer_config['source']: + raise ConfigError('BFD multihop require source address') + + # multihop and echo-mode cannot be used together + if 'echo_mode' in peer_config: + raise ConfigError('Multihop and echo-mode cannot be used together') + + # multihop doesn't accept interface names + if 'source' in peer_config and 'interface' in peer_config['source']: + raise ConfigError('Multihop and source interface cannot be used together') return None def generate(bfd): - if bfd is None: + if not bfd: + bfd['new_frr_config'] = '' return None - render(config_file, 'frr/bfd.frr.tmpl', bfd) - return None + bfd['new_frr_config'] = render_to_string('frr/bfd.frr.tmpl', bfd) def apply(bfd): - if bfd is None: - return None - - call("vtysh -d bfdd -f " + config_file) - if os.path.exists(config_file): - os.remove(config_file) + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + frr_cfg.load_configuration() + frr_cfg.modify_section('^bfd', '') + frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bfd['new_frr_config']) + frr_cfg.commit_configuration() + + # 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 bfd['new_frr_config'] == '': + for a in range(5): + frr_cfg.commit_configuration() return None diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index d0dfb55ec..6770865ff 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -14,114 +14,207 @@ # 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 sys import argv from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.template import is_ip +from vyos.template import render_to_string from vyos.util import call from vyos.util import dict_search -from vyos.template import render -from vyos.template import render_to_string +from vyos.validate import is_addr_assigned from vyos import ConfigError from vyos import frr from vyos import airbag airbag.enable() -config_file = r'/tmp/bgp.frr' +frr_daemon = 'bgpd' + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + + vrf = None + if len(argv) > 1: + vrf = argv[1] -def get_config(): - conf = Config() - base = ['protocols', 'nbgp'] + base_path = ['protocols', 'bgp'] + + # eqivalent of the C foo ? 'a' : 'b' statement + base = vrf and ['vrf', 'name', vrf, 'protocols', 'bgp'] or base_path bgp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) - # XXX: any reason we can not move this into the FRR template? - # we shall not call vtysh directly, especially not in get_config() + # Assign the name of our VRF context. This MUST be done before the return + # statement below, else on deletion we will delete the default instance + # instead of the VRF instance. + if vrf: bgp.update({'vrf' : vrf}) + if not conf.exists(base): - bgp = {} - call('vtysh -c \"conf t\" -c \"no ip protocol bgp\" ') + bgp.update({'deleted' : ''}) + return bgp - if not conf.exists(base + ['route-map']): - call('vtysh -c \"conf t\" -c \"no ip protocol bgp\" ') + # We also need some additional information from the config, + # prefix-lists and route-maps for instance. + base = ['policy'] + tmp = conf.get_config_dict(base, key_mangling=('-', '_')) + # Merge policy dict into bgp dict + bgp = dict_merge(tmp, bgp) return bgp +def verify_remote_as(peer_config, bgp_config): + if 'remote_as' in peer_config: + return peer_config['remote_as'] + + if 'peer_group' in peer_config: + peer_group_name = peer_config['peer_group'] + tmp = dict_search(f'peer_group.{peer_group_name}.remote_as', bgp_config) + if tmp: return tmp + + if 'interface' in peer_config: + if 'remote_as' in peer_config['interface']: + return peer_config['interface']['remote_as'] + + if 'peer_group' in peer_config['interface']: + peer_group_name = peer_config['interface']['peer_group'] + tmp = dict_search(f'peer_group.{peer_group_name}.remote_as', bgp_config) + if tmp: return tmp + + return None + def verify(bgp): - if not bgp: + if not bgp or 'deleted' in bgp: return None - # Check if declared more than one ASN - if len(bgp) > 1: - raise ConfigError('Only one BGP AS number can be defined!') - - for asn, asn_config in bgp.items(): - import pprint - pprint.pprint(asn_config) - - # Common verification for both peer-group and neighbor statements - for neighbor in ['neighbor', 'peer_group']: - # bail out early if there is no neighbor or peer-group statement - # this also saves one indention level - if neighbor not in asn_config: - print(f'no {neighbor} found in config') - continue - - for peer, peer_config in asn_config[neighbor].items(): - # Only regular "neighbor" statement can have a peer-group set - # Check if the configure peer-group exists - if 'peer_group' in peer_config: - peer_group = peer_config['peer_group'] - if peer_group not in asn_config['peer_group']: - raise ConfigError(f'Specified peer-group "{peer_group}" for '\ - f'neighbor "{neighbor}" does not exist!') - - # Some checks can/must only be done on a neighbor and nor a peer-group - if neighbor == 'neighbor': - # remote-as must be either set explicitly for the neighbor - # or for the entire peer-group - if 'remote_as' not in peer_config: - peer_group = peer_config['peer_group'] - if 'remote_as' not in asn_config['peer_group'][peer_group]: - raise ConfigError('Remote AS must be set for neighbor or peer-group!') + if 'local_as' not in bgp: + raise ConfigError('BGP local-as number must be defined!') + + # Common verification for both peer-group and neighbor statements + for neighbor in ['neighbor', 'peer_group']: + # bail out early if there is no neighbor or peer-group statement + # this also saves one indention level + if neighbor not in bgp: + continue + + for peer, peer_config in bgp[neighbor].items(): + # Only regular "neighbor" statement can have a peer-group set + # Check if the configure peer-group exists + if 'peer_group' in peer_config: + peer_group = peer_config['peer_group'] + if 'peer_group' not in bgp or peer_group not in bgp['peer_group']: + raise ConfigError(f'Specified peer-group "{peer_group}" for '\ + f'neighbor "{neighbor}" does not exist!') + + # ttl-security and ebgp-multihop can't be used in the same configration + if 'ebgp_multihop' in peer_config and 'ttl_security' in peer_config: + raise ConfigError('You can\'t set both ebgp-multihop and ttl-security hops') + + # Check spaces in the password + if 'password' in peer_config and ' ' in peer_config['password']: + raise ConfigError('You can\'t use spaces in the password') + + # Some checks can/must only be done on a neighbor and not a peer-group + if neighbor == 'neighbor': + # remote-as must be either set explicitly for the neighbor + # or for the entire peer-group + if not verify_remote_as(peer_config, bgp): + raise ConfigError(f'Neighbor "{peer}" remote-as must be set!') + + # Only checks for ipv4 and ipv6 neighbors + # Check if neighbor address is assigned as system interface address + if is_ip(peer) and is_addr_assigned(peer): + raise ConfigError(f'Can\'t configure local address as neighbor "{peer}"') + + for afi in ['ipv4_unicast', 'ipv6_unicast', 'l2vpn_evpn']: + # Bail out early if address family is not configured + if 'address_family' not in peer_config or afi not in peer_config['address_family']: + continue + + afi_config = peer_config['address_family'][afi] + # Validate if configured Prefix list exists + if 'prefix_list' in afi_config: + for tmp in ['import', 'export']: + if tmp not in afi_config['prefix_list']: + # bail out early + continue + # get_config_dict() mangles all '-' characters to '_' this is legitimate, thus all our + # compares will run on '_' as also '_' is a valid name for a prefix-list + prefix_list = afi_config['prefix_list'][tmp].replace('-', '_') + if afi == 'ipv4_unicast': + if dict_search(f'policy.prefix_list.{prefix_list}', bgp) == None: + raise ConfigError(f'prefix-list "{prefix_list}" used for "{tmp}" does not exist!') + elif afi == 'ipv6_unicast': + if dict_search(f'policy.prefix_list6.{prefix_list}', bgp) == None: + raise ConfigError(f'prefix-list6 "{prefix_list}" used for "{tmp}" does not exist!') + + if 'route_map' in afi_config: + for tmp in ['import', 'export']: + if tmp in afi_config['route_map']: + # get_config_dict() mangles all '-' characters to '_' this is legitim, thus all our + # compares will run on '_' as also '_' is a valid name for a route-map + route_map = afi_config['route_map'][tmp].replace('-', '_') + if dict_search(f'policy.route_map.{route_map}', bgp) == None: + raise ConfigError(f'route-map "{route_map}" used for "{tmp}" does not exist!') + + if 'route_reflector_client' in afi_config: + if 'remote_as' in peer_config and bgp['local_as'] != peer_config['remote_as']: + raise ConfigError('route-reflector-client only supported for iBGP peers') + else: + if 'peer_group' in peer_config: + peer_group_as = dict_search(f'peer_group.{peer_group}.remote_as', bgp) + if peer_group_as != None and peer_group_as != bgp['local_as']: + raise ConfigError('route-reflector-client only supported for iBGP peers') + + # Throw an error if a peer group is not configured for allow range + for prefix in dict_search('listen.range', bgp) or []: + # we can not use dict_search() here as prefix contains dots ... + if 'peer_group' not in bgp['listen']['range'][prefix]: + raise ConfigError(f'Listen range for prefix "{prefix}" has no peer group configured.') + + peer_group = bgp['listen']['range'][prefix]['peer_group'] + if 'peer_group' not in bgp or peer_group not in bgp['peer_group']: + raise ConfigError(f'Peer-group "{peer_group}" for listen range "{prefix}" does not exist!') + + if not verify_remote_as(bgp['listen']['range'][prefix], bgp): + raise ConfigError(f'Peer-group "{peer_group}" requires remote-as to be set!') return None def generate(bgp): - if not bgp: + if not bgp or 'deleted' in bgp: bgp['new_frr_config'] = '' return None - # only one BGP AS is supported, so we can directly send the first key - # of the config dict - asn = list(bgp.keys())[0] - bgp[asn]['asn'] = asn - - # render(config) not needed, its only for debug - render(config_file, 'frr/bgp.frr.tmpl', bgp[asn]) - bgp['new_frr_config'] = render_to_string('frr/bgp.frr.tmpl', bgp[asn]) - + bgp['new_frr_config'] = render_to_string('frr/bgp.frr.tmpl', bgp) return None def apply(bgp): # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() - frr_cfg.load_configuration(daemon='bgpd') - frr_cfg.modify_section(f'router bgp \S+', '') + frr_cfg.load_configuration(frr_daemon) + + if 'vrf' in bgp: + vrf = bgp['vrf'] + frr_cfg.modify_section(f'^router bgp \d+ vrf {vrf}$', '') + else: + frr_cfg.modify_section('^router bgp \d+$', '') + frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bgp['new_frr_config']) - frr_cfg.commit_configuration(daemon='bgpd') + frr_cfg.commit_configuration(frr_daemon) # 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 bgp['new_frr_config'] == '': for a in range(5): - frr_cfg.commit_configuration(daemon='bgpd') - - # Debugging - ''' - print('') - print('--------- DEBUGGING ----------') - print(f'Existing config:\n{frr_cfg["original_config"]}\n\n') - print(f'Replacement config:\n{bgp["new_frr_config"]}\n\n') - print(f'Modified config:\n{frr_cfg["modified_config"]}\n\n') - ''' + frr_cfg.commit_configuration(frr_daemon) + + # Save configuration to /run/frr/{daemon}.conf + frr.save_configuration(frr_daemon) return None diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py index b7afad473..02cf9970c 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) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -17,143 +17,200 @@ import os from sys import exit +from sys import argv from vyos.config import Config +from vyos.configdict import dict_merge from vyos.configdict import node_changed -from vyos import ConfigError +from vyos.configverify import verify_interface_exists from vyos.util import call from vyos.util import dict_search -from vyos.template import render +from vyos.util import get_interface_config from vyos.template import render_to_string +from vyos import ConfigError from vyos import frr from vyos import airbag airbag.enable() +frr_daemon = 'isisd' + def get_config(config=None): if config: conf = config else: conf = Config() - base = ['protocols', 'isis'] - isis = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + vrf = None + if len(argv) > 1: + vrf = argv[1] + + base_path = ['protocols', 'isis'] + + # eqivalent of the C foo ? 'a' : 'b' statement + base = vrf and ['vrf', 'name', vrf, 'protocols', 'isis'] or base_path + isis = conf.get_config_dict(base, key_mangling=('-', '_'), + get_first_key=True) + + # Assign the name of our VRF context. This MUST be done before the return + # statement below, else on deletion we will delete the default instance + # instead of the VRF instance. + if vrf: isis['vrf'] = vrf + + # As we no re-use this Python handler for both VRF and non VRF instances for + # IS-IS we need to find out if any interfaces changed so properly adjust + # the FRR configuration and not by acctident change interfaces from a + # different VRF. + interfaces_removed = node_changed(conf, base + ['interface']) + if interfaces_removed: + isis['interface_removed'] = list(interfaces_removed) + + # Bail out early if configuration tree does not exist + if not conf.exists(base): + isis.update({'deleted' : ''}) + return isis + + # We also need some additional information from the config, prefix-lists + # and route-maps for instance. They will be used in verify() + base = ['policy'] + tmp = conf.get_config_dict(base, key_mangling=('-', '_')) + # Merge policy dict into OSPF dict + isis = dict_merge(tmp, isis) return isis def verify(isis): # bail out early - looks like removal from running config - if not isis: + if not isis or 'deleted' in isis: return None - for process, isis_config in isis.items(): - # If more then one isis process is defined (Frr only supports one) - # http://docs.frrouting.org/en/latest/isisd.html#isis-router - if len(isis) > 1: - raise ConfigError('Only one isis process can be defined') - - # If network entity title (net) not defined - if 'net' not in isis_config: - raise ConfigError('ISIS net format iso is mandatory!') - - # If interface not set - if 'interface' not in isis_config: - raise ConfigError('ISIS interface is mandatory!') - - # If md5 and plaintext-password set at the same time - if 'area_password' in isis_config: - if {'md5', 'plaintext_password'} <= set(isis_config['encryption']): - raise ConfigError('Can not use both md5 and plaintext-password for ISIS area-password!') - - # If one param from delay set, but not set others - if 'spf_delay_ietf' in isis_config: - required_timers = ['holddown', 'init_delay', 'long_delay', 'short_delay', 'time_to_learn'] - exist_timers = [] - for elm_timer in required_timers: - if elm_timer in isis_config['spf_delay_ietf']: - exist_timers.append(elm_timer) - - exist_timers = set(required_timers).difference(set(exist_timers)) - if len(exist_timers) > 0: - raise ConfigError('All types of delay must be specified: ' + ', '.join(exist_timers).replace('_', '-')) - - # If Redistribute set, but level don't set - if 'redistribute' in isis_config: - proc_level = isis_config.get('level','').replace('-','_') - for proto, proto_config in isis_config.get('redistribute', {}).get('ipv4', {}).items(): + if 'net' not in isis: + raise ConfigError('Network entity is mandatory!') + + # last byte in IS-IS area address must be 0 + tmp = isis['net'].split('.') + if int(tmp[-1]) != 0: + raise ConfigError('Last byte of IS-IS network entity title must always be 0!') + + # If interface not set + if 'interface' not in isis: + raise ConfigError('Interface used for routing updates is mandatory!') + + for interface in isis['interface']: + verify_interface_exists(interface) + if 'vrf' in isis: + # If interface specific options are set, we must ensure that the + # interface is bound to our requesting VRF. Due to the VyOS + # priorities the interface is bound to the VRF after creation of + # the VRF itself, and before any routing protocol is configured. + vrf = isis['vrf'] + tmp = get_interface_config(interface) + if 'master' not in tmp or tmp['master'] != vrf: + raise ConfigError(f'Interface {interface} is not a member of VRF {vrf}!') + + # If md5 and plaintext-password set at the same time + if 'area_password' in isis: + if {'md5', 'plaintext_password'} <= set(isis['encryption']): + raise ConfigError('Can not use both md5 and plaintext-password for ISIS area-password!') + + # If one param from delay set, but not set others + if 'spf_delay_ietf' in isis: + required_timers = ['holddown', 'init_delay', 'long_delay', 'short_delay', 'time_to_learn'] + exist_timers = [] + for elm_timer in required_timers: + if elm_timer in isis['spf_delay_ietf']: + exist_timers.append(elm_timer) + + exist_timers = set(required_timers).difference(set(exist_timers)) + if len(exist_timers) > 0: + raise ConfigError('All types of delay must be specified: ' + ', '.join(exist_timers).replace('_', '-')) + + # If Redistribute set, but level don't set + if 'redistribute' in isis: + proc_level = isis.get('level','').replace('-','_') + for afi in ['ipv4']: + if afi not in isis['redistribute']: + continue + + for proto, proto_config in isis['redistribute'][afi].items(): if 'level_1' not in proto_config and 'level_2' not in proto_config: - raise ConfigError('Redistribute level-1 or level-2 should be specified in \"protocols isis {} redistribute ipv4 {}\"'.format(process, proto)) - for redistribute_level in proto_config.keys(): - if proc_level and proc_level != 'level_1_2' and proc_level != redistribute_level: - raise ConfigError('\"protocols isis {0} redistribute ipv4 {2} {3}\" cannot be used with \"protocols isis {0} level {1}\"'.format(process, proc_level, proto, redistribute_level)) - - # Segment routing checks - if dict_search('segment_routing', isis_config): - if dict_search('segment_routing.global_block', isis_config): - high_label_value = dict_search('segment_routing.global_block.high_label_value', isis_config) - low_label_value = dict_search('segment_routing.global_block.low_label_value', isis_config) - # If segment routing global block high value is blank, throw error - if low_label_value and not high_label_value: - raise ConfigError('Segment routing global block high value must not be left blank') - # If segment routing global block low value is blank, throw error - if high_label_value and not low_label_value: - raise ConfigError('Segment routing global block low value must not be left blank') - # If segment routing global block low value is higher than the high value, throw error - if int(low_label_value) > int(high_label_value): - raise ConfigError('Segment routing global block low value must be lower than high value') - - if dict_search('segment_routing.local_block', isis_config): - high_label_value = dict_search('segment_routing.local_block.high_label_value', isis_config) - low_label_value = dict_search('segment_routing.local_block.low_label_value', isis_config) - # If segment routing local block high value is blank, throw error - if low_label_value and not high_label_value: - raise ConfigError('Segment routing local block high value must not be left blank') - # If segment routing local block low value is blank, throw error - if high_label_value and not low_label_value: - raise ConfigError('Segment routing local block low value must not be left blank') - # If segment routing local block low value is higher than the high value, throw error - if int(low_label_value) > int(high_label_value): - raise ConfigError('Segment routing local block low value must be lower than high value') + raise ConfigError(f'Redistribute level-1 or level-2 should be specified in ' \ + f'"protocols isis {process} redistribute {afi} {proto}"!') + + for redistr_level, redistr_config in proto_config.items(): + if proc_level and proc_level != 'level_1_2' and proc_level != redistr_level: + raise ConfigError(f'"protocols isis {process} redistribute {afi} {proto} {redistr_level}" ' \ + f'can not be used with \"protocols isis {process} level {proc_level}\"') + + if 'route_map' in redistr_config: + name = redistr_config['route_map'] + tmp = name.replace('-', '_') + if dict_search(f'policy.route_map.{tmp}', isis) == None: + raise ConfigError(f'Route-map {name} does not exist!') + + # Segment routing checks + if dict_search('segment_routing.global_block', isis): + high_label_value = dict_search('segment_routing.global_block.high_label_value', isis) + low_label_value = dict_search('segment_routing.global_block.low_label_value', isis) + + # If segment routing global block high value is blank, throw error + if (low_label_value and not high_label_value) or (high_label_value and not low_label_value): + raise ConfigError('Segment routing global block requires both low and high value!') + + # If segment routing global block low value is higher than the high value, throw error + if int(low_label_value) > int(high_label_value): + raise ConfigError('Segment routing global block low value must be lower than high value') + + if dict_search('segment_routing.local_block', isis): + high_label_value = dict_search('segment_routing.local_block.high_label_value', isis) + low_label_value = dict_search('segment_routing.local_block.low_label_value', isis) + + # If segment routing local block high value is blank, throw error + if (low_label_value and not high_label_value) or (high_label_value and not low_label_value): + raise ConfigError('Segment routing local block requires both high and low value!') + + # If segment routing local block low value is higher than the high value, throw error + if int(low_label_value) > int(high_label_value): + raise ConfigError('Segment routing local block low value must be lower than high value') return None def generate(isis): - if not isis: + if not isis or 'deleted' in isis: isis['new_frr_config'] = '' return None - # only one ISIS process is supported, so we can directly send the first key - # of the config dict - process = list(isis.keys())[0] - isis[process]['process'] = process - - isis['new_frr_config'] = render_to_string('frr/isis.frr.tmpl', - isis[process]) - + isis['new_frr_config'] = render_to_string('frr/isis.frr.tmpl', isis) return None def apply(isis): # Save original configuration prior to starting any commit actions 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.load_configuration(frr_daemon) + + # Generate empty helper string which can be ammended to FRR commands, + # it will be either empty (default VRF) or contain the "vrf <name" statement + vrf = '' + if 'vrf' in isis: + vrf = ' vrf ' + isis['vrf'] + + frr_cfg.modify_section(f'^router isis VyOS{vrf}$', '') + for key in ['interface', 'interface_removed']: + if key not in isis: + continue + for interface in isis[key]: + frr_cfg.modify_section(f'^interface {interface}{vrf}$', '') + frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', isis['new_frr_config']) - frr_cfg.commit_configuration(daemon='isisd') + frr_cfg.commit_configuration(frr_daemon) # 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_cfg.commit_configuration(frr_daemon) + + # Save configuration to /run/frr/{daemon}.conf + frr.save_configuration(frr_daemon) return None diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py new file mode 100755 index 000000000..b4ee8659a --- /dev/null +++ b/src/conf_mode/protocols_ospf.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 sys import argv + +from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.configdict import node_changed +from vyos.configverify import verify_route_maps +from vyos.configverify import verify_interface_exists +from vyos.template import render_to_string +from vyos.util import call +from vyos.util import dict_search +from vyos.util import get_interface_config +from vyos.xml import defaults +from vyos import ConfigError +from vyos import frr +from vyos import airbag +airbag.enable() + +frr_daemon = 'ospfd' + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + + vrf = None + if len(argv) > 1: + vrf = argv[1] + + base_path = ['protocols', 'ospf'] + + # eqivalent of the C foo ? 'a' : 'b' statement + base = vrf and ['vrf', 'name', vrf, 'protocols', 'ospf'] or base_path + ospf = conf.get_config_dict(base, key_mangling=('-', '_'), + get_first_key=True) + + # Assign the name of our VRF context. This MUST be done before the return + # statement below, else on deletion we will delete the default instance + # instead of the VRF instance. + if vrf: ospf['vrf'] = vrf + + # As we no re-use this Python handler for both VRF and non VRF instances for + # OSPF we need to find out if any interfaces changed so properly adjust + # the FRR configuration and not by acctident change interfaces from a + # different VRF. + interfaces_removed = node_changed(conf, base + ['interface']) + if interfaces_removed: + ospf['interface_removed'] = list(interfaces_removed) + + # Bail out early if configuration tree does not exist + if not conf.exists(base): + ospf.update({'deleted' : ''}) + return ospf + + # We have gathered the dict representation of the CLI, but there are default + # options which we need to update into the dictionary retrived. + # XXX: Note that we can not call defaults(base), as defaults does not work + # on an instance of a tag node. As we use the exact same CLI definition for + # both the non-vrf and vrf version this is absolutely safe! + default_values = defaults(base_path) + + # We have to cleanup the default dict, as default values could enable features + # which are not explicitly enabled on the CLI. Example: default-information + # originate comes with a default metric-type of 2, which will enable the + # entire default-information originate tree, even when not set via CLI so we + # need to check this first and probably drop that key. + if dict_search('default_information.originate', ospf) is None: + del default_values['default_information'] + if dict_search('area.area_type.nssa', ospf) is None: + del default_values['area']['area_type']['nssa'] + if 'mpls_te' not in ospf: + del default_values['mpls_te'] + for protocol in ['bgp', 'connected', 'isis', 'kernel', 'rip', 'static']: + if dict_search(f'redistribute.{protocol}', ospf) is None: + del default_values['redistribute'][protocol] + + # XXX: T2665: we currently have no nice way for defaults under tag nodes, + # clean them out and add them manually :( + del default_values['neighbor'] + del default_values['area']['virtual_link'] + del default_values['interface'] + + # merge in remaining default values + ospf = dict_merge(default_values, ospf) + + if 'neighbor' in ospf: + default_values = defaults(base + ['neighbor']) + for neighbor in ospf['neighbor']: + ospf['neighbor'][neighbor] = dict_merge(default_values, ospf['neighbor'][neighbor]) + + if 'area' in ospf: + default_values = defaults(base + ['area', 'virtual-link']) + for area, area_config in ospf['area'].items(): + if 'virtual_link' in area_config: + print(default_values) + for virtual_link in area_config['virtual_link']: + ospf['area'][area]['virtual_link'][virtual_link] = dict_merge( + default_values, ospf['area'][area]['virtual_link'][virtual_link]) + + if 'interface' in ospf: + for interface in ospf['interface']: + # We need to reload the defaults on every pass b/c of + # hello-multiplier dependency on dead-interval + default_values = defaults(base + ['interface']) + # If hello-multiplier is set, we need to remove the default from + # dead-interval. + if 'hello_multiplier' in ospf['interface'][interface]: + del default_values['dead_interval'] + + ospf['interface'][interface] = dict_merge(default_values, + ospf['interface'][interface]) + + # We also need some additional information from the config, prefix-lists + # and route-maps for instance. They will be used in verify() + base = ['policy'] + tmp = conf.get_config_dict(base, key_mangling=('-', '_')) + # Merge policy dict into OSPF dict + ospf = dict_merge(tmp, ospf) + + return ospf + +def verify(ospf): + if not ospf: + return None + + verify_route_maps(ospf) + + if 'interface' in ospf: + for interface in ospf['interface']: + verify_interface_exists(interface) + # One can not use dead-interval and hello-multiplier at the same + # time. FRR will only activate the last option set via CLI. + if {'hello_multiplier', 'dead_interval'} <= set(ospf['interface'][interface]): + raise ConfigError(f'Can not use hello-multiplier and dead-interval ' \ + f'concurrently for {interface}!') + + if 'vrf' in ospf: + # If interface specific options are set, we must ensure that the + # interface is bound to our requesting VRF. Due to the VyOS + # priorities the interface is bound to the VRF after creation of + # the VRF itself, and before any routing protocol is configured. + vrf = ospf['vrf'] + tmp = get_interface_config(interface) + if 'master' not in tmp or tmp['master'] != vrf: + raise ConfigError(f'Interface {interface} is not a member of VRF {vrf}!') + + return None + +def generate(ospf): + if not ospf or 'deleted' in ospf: + ospf['new_frr_config'] = '' + return None + + ospf['new_frr_config'] = render_to_string('frr/ospf.frr.tmpl', ospf) + return None + +def apply(ospf): + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + frr_cfg.load_configuration(frr_daemon) + + # Generate empty helper string which can be ammended to FRR commands, + # it will be either empty (default VRF) or contain the "vrf <name" statement + vrf = '' + if 'vrf' in ospf: + vrf = ' vrf ' + ospf['vrf'] + + frr_cfg.modify_section(f'^router ospf{vrf}$', '') + for key in ['interface', 'interface_removed']: + if key not in ospf: + continue + for interface in ospf[key]: + frr_cfg.modify_section(f'^interface {interface}{vrf}$', '') + + frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospf['new_frr_config']) + frr_cfg.commit_configuration(frr_daemon) + + # 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 ospf['new_frr_config'] == '': + for a in range(5): + frr_cfg.commit_configuration(frr_daemon) + + # Save configuration to /run/frr/{daemon}.conf + frr.save_configuration(frr_daemon) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py new file mode 100755 index 000000000..f3beab204 --- /dev/null +++ b/src/conf_mode/protocols_ospfv3.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os + +from sys import exit + +from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.configverify import verify_route_maps +from vyos.template import render_to_string +from vyos.util import call +from vyos.ifconfig import Interface +from vyos.xml import defaults +from vyos import ConfigError +from vyos import frr +from vyos import airbag +airbag.enable() + +frr_daemon = 'ospf6d' + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + base = ['protocols', 'ospfv3'] + ospfv3 = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + + # Bail out early if configuration tree does not exist + if not conf.exists(base): + return ospfv3 + + # We also need some additional information from the config, prefix-lists + # and route-maps for instance. They will be used in verify() + base = ['policy'] + tmp = conf.get_config_dict(base, key_mangling=('-', '_')) + # Merge policy dict into OSPF dict + ospfv3 = dict_merge(tmp, ospfv3) + + return ospfv3 + +def verify(ospfv3): + if not ospfv3: + return None + + verify_route_maps(ospfv3) + + if 'interface' in ospfv3: + for ifname, if_config in ospfv3['interface'].items(): + if 'ifmtu' in if_config: + mtu = Interface(ifname).get_mtu() + if int(if_config['ifmtu']) > int(mtu): + raise ConfigError(f'OSPFv3 ifmtu cannot go beyond physical MTU of "{mtu}"') + + return None + +def generate(ospfv3): + if not ospfv3: + ospfv3['new_frr_config'] = '' + return None + + ospfv3['new_frr_config'] = render_to_string('frr/ospfv3.frr.tmpl', ospfv3) + return None + +def apply(ospfv3): + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + frr_cfg.load_configuration(frr_daemon) + frr_cfg.modify_section(r'^interface \S+', '') + frr_cfg.modify_section('^router ospf6$', '') + frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospfv3['new_frr_config']) + frr_cfg.commit_configuration(frr_daemon) + + # If FRR config is blank, re-run the blank commit x times due to frr-reload + # behavior/bug not properly clearing out on one commit. + if ospfv3['new_frr_config'] == '': + for a in range(5): + frr_cfg.commit_configuration(frr_daemon) + + # Save configuration to /run/frr/{daemon}.conf + frr.save_configuration(frr_daemon) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/conf_mode/protocols_rip.py b/src/conf_mode/protocols_rip.py index 8ddd705f2..34d42d630 100755 --- a/src/conf_mode/protocols_rip.py +++ b/src/conf_mode/protocols_rip.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2021 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 @@ -18,15 +18,19 @@ import os from sys import exit -from vyos import ConfigError from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.configverify import verify_route_maps from vyos.util import call -from vyos.template import render - +from vyos.util import dict_search +from vyos.xml import defaults +from vyos.template import render_to_string +from vyos import ConfigError +from vyos import frr from vyos import airbag airbag.enable() -config_file = r'/tmp/ripd.frr' +frr_daemon = 'ripd' def get_config(config=None): if config: @@ -34,277 +38,86 @@ def get_config(config=None): else: conf = Config() base = ['protocols', 'rip'] - rip_conf = { - 'rip_conf' : False, - 'default_distance' : [], - 'default_originate' : False, - 'old_rip' : { - 'default_metric' : [], - 'distribute' : {}, - 'neighbors' : {}, - 'networks' : {}, - 'net_distance' : {}, - 'passive_iface' : {}, - 'redist' : {}, - 'route' : {}, - 'ifaces' : {}, - 'timer_garbage' : 120, - 'timer_timeout' : 180, - 'timer_update' : 30 - }, - 'rip' : { - 'default_metric' : None, - 'distribute' : {}, - 'neighbors' : {}, - 'networks' : {}, - 'net_distance' : {}, - 'passive_iface' : {}, - 'redist' : {}, - 'route' : {}, - 'ifaces' : {}, - 'timer_garbage' : 120, - 'timer_timeout' : 180, - 'timer_update' : 30 - } - } - - if not (conf.exists(base) or conf.exists_effective(base)): - return None - - if conf.exists(base): - rip_conf['rip_conf'] = True - - conf.set_level(base) - - # Get default distance - if conf.exists_effective('default-distance'): - rip_conf['old_default_distance'] = conf.return_effective_value('default-distance') - - if conf.exists('default-distance'): - rip_conf['default_distance'] = conf.return_value('default-distance') - - # Get default information originate (originate default route) - if conf.exists_effective('default-information originate'): - rip_conf['old_default_originate'] = True - - if conf.exists('default-information originate'): - rip_conf['default_originate'] = True - - # Get default-metric - if conf.exists_effective('default-metric'): - rip_conf['old_rip']['default_metric'] = conf.return_effective_value('default-metric') - - if conf.exists('default-metric'): - rip_conf['rip']['default_metric'] = conf.return_value('default-metric') - - # Get distribute list interface old_rip - for dist_iface in conf.list_effective_nodes('distribute-list interface'): - # Set level 'distribute-list interface ethX' - conf.set_level(base + ['distribute-list', 'interface', dist_iface]) - rip_conf['rip']['distribute'].update({ - dist_iface : { - 'iface_access_list_in': conf.return_effective_value('access-list in'.format(dist_iface)), - 'iface_access_list_out': conf.return_effective_value('access-list out'.format(dist_iface)), - 'iface_prefix_list_in': conf.return_effective_value('prefix-list in'.format(dist_iface)), - 'iface_prefix_list_out': conf.return_effective_value('prefix-list out'.format(dist_iface)) - } - }) - - # Access-list in old_rip - if conf.exists_effective('access-list in'.format(dist_iface)): - rip_conf['old_rip']['iface_access_list_in'] = conf.return_effective_value('access-list in'.format(dist_iface)) - # Access-list out old_rip - if conf.exists_effective('access-list out'.format(dist_iface)): - rip_conf['old_rip']['iface_access_list_out'] = conf.return_effective_value('access-list out'.format(dist_iface)) - # Prefix-list in old_rip - if conf.exists_effective('prefix-list in'.format(dist_iface)): - rip_conf['old_rip']['iface_prefix_list_in'] = conf.return_effective_value('prefix-list in'.format(dist_iface)) - # Prefix-list out old_rip - if conf.exists_effective('prefix-list out'.format(dist_iface)): - rip_conf['old_rip']['iface_prefix_list_out'] = conf.return_effective_value('prefix-list out'.format(dist_iface)) - - conf.set_level(base) - - # Get distribute list interface - for dist_iface in conf.list_nodes('distribute-list interface'): - # Set level 'distribute-list interface ethX' - conf.set_level(base + ['distribute-list', 'interface', dist_iface]) - rip_conf['rip']['distribute'].update({ - dist_iface : { - 'iface_access_list_in': conf.return_value('access-list in'.format(dist_iface)), - 'iface_access_list_out': conf.return_value('access-list out'.format(dist_iface)), - 'iface_prefix_list_in': conf.return_value('prefix-list in'.format(dist_iface)), - 'iface_prefix_list_out': conf.return_value('prefix-list out'.format(dist_iface)) - } - }) - - # Access-list in - if conf.exists('access-list in'.format(dist_iface)): - rip_conf['rip']['iface_access_list_in'] = conf.return_value('access-list in'.format(dist_iface)) - # Access-list out - if conf.exists('access-list out'.format(dist_iface)): - rip_conf['rip']['iface_access_list_out'] = conf.return_value('access-list out'.format(dist_iface)) - # Prefix-list in - if conf.exists('prefix-list in'.format(dist_iface)): - rip_conf['rip']['iface_prefix_list_in'] = conf.return_value('prefix-list in'.format(dist_iface)) - # Prefix-list out - if conf.exists('prefix-list out'.format(dist_iface)): - rip_conf['rip']['iface_prefix_list_out'] = conf.return_value('prefix-list out'.format(dist_iface)) - - conf.set_level(base + ['distribute-list']) - - # Get distribute list, access-list in - if conf.exists_effective('access-list in'): - rip_conf['old_rip']['dist_acl_in'] = conf.return_effective_value('access-list in') - - if conf.exists('access-list in'): - rip_conf['rip']['dist_acl_in'] = conf.return_value('access-list in') - - # Get distribute list, access-list out - if conf.exists_effective('access-list out'): - rip_conf['old_rip']['dist_acl_out'] = conf.return_effective_value('access-list out') - - if conf.exists('access-list out'): - rip_conf['rip']['dist_acl_out'] = conf.return_value('access-list out') - - # Get ditstribute list, prefix-list in - if conf.exists_effective('prefix-list in'): - rip_conf['old_rip']['dist_prfx_in'] = conf.return_effective_value('prefix-list in') - - if conf.exists('prefix-list in'): - rip_conf['rip']['dist_prfx_in'] = conf.return_value('prefix-list in') - - # Get distribute list, prefix-list out - if conf.exists_effective('prefix-list out'): - rip_conf['old_rip']['dist_prfx_out'] = conf.return_effective_value('prefix-list out') + rip = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) - if conf.exists('prefix-list out'): - rip_conf['rip']['dist_prfx_out'] = conf.return_value('prefix-list out') + # Bail out early if configuration tree does not exist + if not conf.exists(base): + return rip - conf.set_level(base) + # 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) + # merge in remaining default values + rip = dict_merge(default_values, rip) - # Get network Interfaces - if conf.exists_effective('interface'): - rip_conf['old_rip']['ifaces'] = conf.return_effective_values('interface') + # We also need some additional information from the config, prefix-lists + # and route-maps for instance. They will be used in verify() + base = ['policy'] + tmp = conf.get_config_dict(base, key_mangling=('-', '_')) + # Merge policy dict into OSPF dict + rip = dict_merge(tmp, rip) - if conf.exists('interface'): - rip_conf['rip']['ifaces'] = conf.return_values('interface') + return rip - # Get neighbors - if conf.exists_effective('neighbor'): - rip_conf['old_rip']['neighbors'] = conf.return_effective_values('neighbor') - - if conf.exists('neighbor'): - rip_conf['rip']['neighbors'] = conf.return_values('neighbor') - - # Get networks - if conf.exists_effective('network'): - rip_conf['old_rip']['networks'] = conf.return_effective_values('network') - - if conf.exists('network'): - rip_conf['rip']['networks'] = conf.return_values('network') - - # Get network-distance old_rip - for net_dist in conf.list_effective_nodes('network-distance'): - rip_conf['old_rip']['net_distance'].update({ - net_dist : { - 'access_list' : conf.return_effective_value('network-distance {0} access-list'.format(net_dist)), - 'distance' : conf.return_effective_value('network-distance {0} distance'.format(net_dist)), - } - }) - - # Get network-distance - for net_dist in conf.list_nodes('network-distance'): - rip_conf['rip']['net_distance'].update({ - net_dist : { - 'access_list' : conf.return_value('network-distance {0} access-list'.format(net_dist)), - 'distance' : conf.return_value('network-distance {0} distance'.format(net_dist)), - } - }) - - # Get passive-interface - if conf.exists_effective('passive-interface'): - rip_conf['old_rip']['passive_iface'] = conf.return_effective_values('passive-interface') - - if conf.exists('passive-interface'): - rip_conf['rip']['passive_iface'] = conf.return_values('passive-interface') - - # Get redistribute for old_rip - for protocol in conf.list_effective_nodes('redistribute'): - rip_conf['old_rip']['redist'].update({ - protocol : { - 'metric' : conf.return_effective_value('redistribute {0} metric'.format(protocol)), - 'route_map' : conf.return_effective_value('redistribute {0} route-map'.format(protocol)), - } - }) - - # Get redistribute - for protocol in conf.list_nodes('redistribute'): - rip_conf['rip']['redist'].update({ - protocol : { - 'metric' : conf.return_value('redistribute {0} metric'.format(protocol)), - 'route_map' : conf.return_value('redistribute {0} route-map'.format(protocol)), - } - }) - - conf.set_level(base) - - # Get route - if conf.exists_effective('route'): - rip_conf['old_rip']['route'] = conf.return_effective_values('route') - - if conf.exists('route'): - rip_conf['rip']['route'] = conf.return_values('route') - - # Get timers garbage - if conf.exists_effective('timers garbage-collection'): - rip_conf['old_rip']['timer_garbage'] = conf.return_effective_value('timers garbage-collection') - - if conf.exists('timers garbage-collection'): - rip_conf['rip']['timer_garbage'] = conf.return_value('timers garbage-collection') - - # Get timers timeout - if conf.exists_effective('timers timeout'): - rip_conf['old_rip']['timer_timeout'] = conf.return_effective_value('timers timeout') +def verify(rip): + if not rip: + return None - if conf.exists('timers timeout'): - rip_conf['rip']['timer_timeout'] = conf.return_value('timers timeout') + acl_in = dict_search('distribute_list.access_list.in', rip) + if acl_in and acl_in not in (dict_search('policy.access_list', rip) or []): + raise ConfigError(f'Inbound ACL "{acl_in}" does not exist!') - # Get timers update - if conf.exists_effective('timers update'): - rip_conf['old_rip']['timer_update'] = conf.return_effective_value('timers update') + acl_out = dict_search('distribute_list.access_list.out', rip) + if acl_out and acl_out not in (dict_search('policy.access_list', rip) or []): + raise ConfigError(f'Outbound ACL "{acl_out}" does not exist!') - if conf.exists('timers update'): - rip_conf['rip']['timer_update'] = conf.return_value('timers update') + prefix_list_in = dict_search('distribute_list.prefix_list.in', rip) + if prefix_list_in and prefix_list_in.replace('-','_') not in (dict_search('policy.prefix_list', rip) or []): + raise ConfigError(f'Inbound prefix-list "{prefix_list_in}" does not exist!') - return rip_conf + prefix_list_out = dict_search('distribute_list.prefix_list.out', rip) + if prefix_list_out and prefix_list_out.replace('-','_') not in (dict_search('policy.prefix_list', rip) or []): + raise ConfigError(f'Outbound prefix-list "{prefix_list_out}" does not exist!') -def verify(rip): - if rip is None: - return None + if 'interface' in rip: + for interface, interface_options in rip['interface'].items(): + if 'authentication' in interface_options: + if {'md5', 'plaintext_password'} <= set(interface_options['authentication']): + raise ConfigError('Can not use both md5 and plaintext-password at the same time!') + if 'split_horizon' in interface_options: + if {'disable', 'poison_reverse'} <= set(interface_options['split_horizon']): + raise ConfigError(f'You can not have "split-horizon poison-reverse" enabled ' \ + f'with "split-horizon disable" for "{interface}"!') - # Check for network. If network-distance acl is set and distance not set - for net in rip['rip']['net_distance']: - if not rip['rip']['net_distance'][net]['distance']: - raise ConfigError(f"Must specify distance for network {net}") + verify_route_maps(rip) def generate(rip): - if rip is None: + if not rip: + rip['new_frr_config'] = '' return None - render(config_file, 'frr/rip.frr.tmpl', rip) + rip['new_frr_config'] = render_to_string('frr/rip.frr.tmpl', rip) + return None def apply(rip): - if rip is None: - return None - - if os.path.exists(config_file): - call(f'vtysh -d ripd -f {config_file}') - os.remove(config_file) - else: - print("File {0} not found".format(config_file)) - + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + frr_cfg.load_configuration(frr_daemon) + frr_cfg.modify_section(r'key chain \S+', '') + frr_cfg.modify_section(r'interface \S+', '') + frr_cfg.modify_section('router rip', '') + frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', rip['new_frr_config']) + frr_cfg.commit_configuration(frr_daemon) + + # 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 rip['new_frr_config'] == '': + for a in range(5): + frr_cfg.commit_configuration(frr_daemon) + + # Save configuration to /run/frr/{daemon}.conf + frr.save_configuration(frr_daemon) return None @@ -317,4 +130,3 @@ if __name__ == '__main__': except ConfigError as e: print(e) exit(1) - diff --git a/src/conf_mode/protocols_ripng.py b/src/conf_mode/protocols_ripng.py new file mode 100755 index 000000000..eff4297f9 --- /dev/null +++ b/src/conf_mode/protocols_ripng.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os + +from sys import exit + +from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.configverify import verify_route_maps +from vyos.util import call +from vyos.util import dict_search +from vyos.xml import defaults +from vyos.template import render_to_string +from vyos import ConfigError +from vyos import frr +from vyos import airbag +airbag.enable() + +frr_daemon = 'ripngd' + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + base = ['protocols', 'ripng'] + ripng = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + + # Bail out early if configuration tree does not exist + if not conf.exists(base): + return ripng + + # 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) + # merge in remaining default values + ripng = dict_merge(default_values, ripng) + + # We also need some additional information from the config, prefix-lists + # and route-maps for instance. They will be used in verify() + base = ['policy'] + tmp = conf.get_config_dict(base, key_mangling=('-', '_')) + # Merge policy dict into OSPF dict + ripng = dict_merge(tmp, ripng) + + return ripng + +def verify(ripng): + if not ripng: + return None + + acl_in = dict_search('distribute_list.access_list.in', ripng) + if acl_in and acl_in not in (dict_search('policy.access_list6', ripng) or []): + raise ConfigError(f'Inbound access-list6 "{acl_in}" does not exist!') + + acl_out = dict_search('distribute_list.access_list.out', ripng) + if acl_out and acl_out not in (dict_search('policy.access_list6', ripng) or []): + raise ConfigError(f'Outbound access-list6 "{acl_out}" does not exist!') + + prefix_list_in = dict_search('distribute_list.prefix_list.in', ripng) + if prefix_list_in and prefix_list_in.replace('-','_') not in (dict_search('policy.prefix_list6', ripng) or []): + raise ConfigError(f'Inbound prefix-list6 "{prefix_list_in}" does not exist!') + + prefix_list_out = dict_search('distribute_list.prefix_list.out', ripng) + if prefix_list_out and prefix_list_out.replace('-','_') not in (dict_search('policy.prefix_list6', ripng) or []): + raise ConfigError(f'Outbound prefix-list6 "{prefix_list_out}" does not exist!') + + if 'interface' in ripng: + for interface, interface_options in ripng['interface'].items(): + if 'authentication' in interface_options: + if {'md5', 'plaintext_password'} <= set(interface_options['authentication']): + raise ConfigError('Can not use both md5 and plaintext-password at the same time!') + if 'split_horizon' in interface_options: + if {'disable', 'poison_reverse'} <= set(interface_options['split_horizon']): + raise ConfigError(f'You can not have "split-horizon poison-reverse" enabled ' \ + f'with "split-horizon disable" for "{interface}"!') + + verify_route_maps(ripng) + +def generate(ripng): + if not ripng: + ripng['new_frr_config'] = '' + return None + + ripng['new_frr_config'] = render_to_string('frr/ripng.frr.tmpl', ripng) + return None + +def apply(ripng): + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + frr_cfg.load_configuration(frr_daemon) + frr_cfg.modify_section(r'key chain \S+', '') + frr_cfg.modify_section(r'interface \S+', '') + frr_cfg.modify_section('router ripng', '') + frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ripng['new_frr_config']) + frr_cfg.commit_configuration(frr_daemon) + + # 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 ripng['new_frr_config'] == '': + for a in range(5): + frr_cfg.commit_configuration(frr_daemon) + + # Save configuration to /run/frr/{daemon}.conf + frr.save_configuration(frr_daemon) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/conf_mode/protocols_rpki.py b/src/conf_mode/protocols_rpki.py new file mode 100755 index 000000000..75b870b05 --- /dev/null +++ b/src/conf_mode/protocols_rpki.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os + +from sys import exit + +from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.template import render_to_string +from vyos.util import call +from vyos.util import dict_search +from vyos.xml import defaults +from vyos import ConfigError +from vyos import frr +from vyos import airbag +airbag.enable() + +frr_daemon = 'bgpd' + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + base = ['protocols', 'rpki'] + + rpki = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + if not conf.exists(base): + return rpki + + # 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) + rpki = dict_merge(default_values, rpki) + + return rpki + +def verify(rpki): + if not rpki: + return None + + if 'cache' in rpki: + preferences = [] + for peer, peer_config in rpki['cache'].items(): + for mandatory in ['port', 'preference']: + if mandatory not in peer_config: + raise ConfigError(f'RPKI cache "{peer}" {mandatory} must be defined!') + + if 'preference' in peer_config: + preference = peer_config['preference'] + if preference in preferences: + raise ConfigError(f'RPKI cache with preference {preference} already configured!') + preferences.append(preference) + + if 'ssh' in peer_config: + files = ['private_key_file', 'public_key_file', 'known_hosts_file'] + for file in files: + if file not in peer_config['ssh']: + raise ConfigError('RPKI+SSH requires username, public/private ' \ + 'keys and known-hosts file to be defined!') + + filename = peer_config['ssh'][file] + if not os.path.exists(filename): + raise ConfigError(f'RPKI SSH {file.replace("-","-")} "{filename}" does not exist!') + + return None + +def generate(rpki): + rpki['new_frr_config'] = render_to_string('frr/rpki.frr.tmpl', rpki) + return None + +def apply(rpki): + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + frr_cfg.load_configuration(frr_daemon) + frr_cfg.modify_section('rpki', '') + frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', rpki['new_frr_config']) + frr_cfg.commit_configuration(frr_daemon) + + # If FRR config is blank, re-run the blank commit x times due to frr-reload + # behavior/bug not properly clearing out on one commit. + if rpki['new_frr_config'] == '': + for a in range(5): + frr_cfg.commit_configuration(frr_daemon) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/conf_mode/protocols_static.py b/src/conf_mode/protocols_static.py new file mode 100755 index 000000000..0de073a6d --- /dev/null +++ b/src/conf_mode/protocols_static.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 sys import argv + +from vyos.config import Config +from vyos.configverify import verify_route_maps +from vyos.configverify import verify_vrf +from vyos.template import render_to_string +from vyos.util import call +from vyos import ConfigError +from vyos import frr +from vyos import airbag +airbag.enable() + +frr_daemon = 'staticd' + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + + vrf = None + if len(argv) > 1: + vrf = argv[1] + + base_path = ['protocols', 'static'] + # eqivalent of the C foo ? 'a' : 'b' statement + base = vrf and ['vrf', 'name', vrf, 'protocols', 'static'] or base_path + static = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + + # Assign the name of our VRF context + if vrf: static['vrf'] = vrf + + return static + +def verify(static): + verify_route_maps(static) + + for route in ['route', 'route6']: + # if there is no route(6) key in the dictionary we can immediately + # bail out early + if route not in static: + continue + + # When leaking routes to other VRFs we must ensure that the destination + # VRF exists + for prefix, prefix_options in static[route].items(): + # both the interface and next-hop CLI node can have a VRF subnode, + # thus we check this using a for loop + for type in ['interface', 'next_hop']: + if type in prefix_options: + for interface, interface_config in prefix_options[type].items(): + verify_vrf(interface_config) + + return None + +def generate(static): + static['new_frr_config'] = render_to_string('frr/static.frr.tmpl', static) + return None + +def apply(static): + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + frr_cfg.load_configuration(frr_daemon) + + if 'vrf' in static: + vrf = static['vrf'] + frr_cfg.modify_section(f'^vrf {vrf}$', '') + else: + frr_cfg.modify_section(r'^ip route .*', '') + frr_cfg.modify_section(r'^ipv6 route .*', '') + + frr_cfg.add_before(r'(interface .*|line vty)', static['new_frr_config']) + frr_cfg.commit_configuration(frr_daemon) + + # 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 static['new_frr_config'] == '': + for a in range(5): + frr_cfg.commit_configuration(frr_daemon) + + # Save configuration to /run/frr/{daemon}.conf + frr.save_configuration(frr_daemon) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/conf_mode/service_console-server.py b/src/conf_mode/service_console-server.py index 0e5fc75b0..51050e702 100755 --- a/src/conf_mode/service_console-server.py +++ b/src/conf_mode/service_console-server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -17,6 +17,7 @@ import os from sys import exit +from psutil import process_iter from vyos.config import Config from vyos.configdict import dict_merge @@ -25,7 +26,8 @@ from vyos.util import call from vyos.xml import defaults from vyos import ConfigError -config_file = r'/run/conserver/conserver.cf' +config_file = '/run/conserver/conserver.cf' +dropbear_systemd_file = '/etc/systemd/system/dropbear@{port}.service.d/override.conf' def get_config(config=None): if config: @@ -59,14 +61,19 @@ def verify(proxy): if not proxy: return None + processes = process_iter(['name', 'cmdline']) if 'device' in proxy: - for device in proxy['device']: - if 'speed' not in proxy['device'][device]: - raise ConfigError(f'Serial port speed must be defined for "{device}"!') + for device, device_config in proxy['device'].items(): + for process in processes: + if 'agetty' in process.name() and device in process.cmdline(): + raise ConfigError(f'Port "{device}" already provides a '\ + 'console used by "system console"!') + + if 'speed' not in device_config: + raise ConfigError(f'Port "{device}" requires speed to be set!') - if 'ssh' in proxy['device'][device]: - if 'port' not in proxy['device'][device]['ssh']: - raise ConfigError(f'SSH port must be defined for "{device}"!') + if 'ssh' in device_config and 'port' not in device_config['ssh']: + raise ConfigError(f'Port "{device}" requires SSH port to be set!') return None @@ -75,9 +82,22 @@ def generate(proxy): return None render(config_file, 'conserver/conserver.conf.tmpl', proxy) + if 'device' in proxy: + for device, device_config in proxy['device'].items(): + if 'ssh' not in device_config: + continue + + tmp = { + 'device' : device, + 'port' : device_config['ssh']['port'], + } + render(dropbear_systemd_file.format(**tmp), + 'conserver/dropbear@.service.tmpl', tmp) + return None def apply(proxy): + call('systemctl daemon-reload') call('systemctl stop dropbear@*.service conserver-server.service') if not proxy: @@ -88,10 +108,11 @@ def apply(proxy): call('systemctl restart conserver-server.service') if 'device' in proxy: - for device in proxy['device']: - if 'ssh' in proxy['device'][device]: - port = proxy['device'][device]['ssh']['port'] - call(f'systemctl restart dropbear@{device}.service') + for device, device_config in proxy['device'].items(): + if 'ssh' not in device_config: + continue + port = device_config['ssh']['port'] + call(f'systemctl restart dropbear@{port}.service') return None diff --git a/src/conf_mode/service_webproxy.py b/src/conf_mode/service_webproxy.py index 8dfae348a..cbbd2e0bc 100755 --- a/src/conf_mode/service_webproxy.py +++ b/src/conf_mode/service_webproxy.py @@ -123,9 +123,6 @@ def verify(proxy): ldap_auth = dict_search('authentication.method', proxy) == 'ldap' for address, config in proxy['listen_address'].items(): - if not is_addr_assigned(address): - raise ConfigError( - f'listen-address "{address}" not assigned on any interface!') if ldap_auth and 'disable_transparent' not in config: raise ConfigError('Authentication can not be configured when ' \ 'proxy is in transparent mode') diff --git a/src/conf_mode/ssh.py b/src/conf_mode/ssh.py index 8f99053d2..67724b043 100755 --- a/src/conf_mode/ssh.py +++ b/src/conf_mode/ssh.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -17,6 +17,8 @@ import os from sys import exit +from syslog import syslog +from syslog import LOG_INFO from vyos.config import Config from vyos.configdict import dict_merge @@ -28,9 +30,13 @@ from vyos import ConfigError from vyos import airbag airbag.enable() -config_file = r'/run/ssh/sshd_config' +config_file = r'/run/sshd/sshd_config' systemd_override = r'/etc/systemd/system/ssh.service.d/override.conf' +key_rsa = '/etc/ssh/ssh_host_rsa_key' +key_dsa = '/etc/ssh/ssh_host_dsa_key' +key_ed25519 = '/etc/ssh/ssh_host_ed25519_key' + def get_config(config=None): if config: conf = config @@ -66,8 +72,22 @@ def generate(ssh): return None + # This usually happens only once on a fresh system, SSH keys need to be + # freshly generted, one per every system! + if not os.path.isfile(key_rsa): + syslog(LOG_INFO, 'SSH RSA host key not found, generating new key!') + call(f'ssh-keygen -q -N "" -t rsa -f {key_rsa}') + if not os.path.isfile(key_dsa): + syslog(LOG_INFO, 'SSH DSA host key not found, generating new key!') + call(f'ssh-keygen -q -N "" -t dsa -f {key_dsa}') + if not os.path.isfile(key_ed25519): + syslog(LOG_INFO, 'SSH ed25519 host key not found, generating new key!') + call(f'ssh-keygen -q -N "" -t ed25519 -f {key_ed25519}') + render(config_file, 'ssh/sshd_config.tmpl', ssh) render(systemd_override, 'ssh/override.conf.tmpl', ssh) + # Reload systemd manager configuration + call('systemctl daemon-reload') return None @@ -75,13 +95,9 @@ def apply(ssh): if not ssh: # SSH access is removed in the commit call('systemctl stop ssh.service') + return None - # Reload systemd manager configuration - call('systemctl daemon-reload') - - if ssh: - call('systemctl restart ssh.service') - + call('systemctl restart ssh.service') return None if __name__ == '__main__': diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py index 39bad717d..99af5c757 100755 --- a/src/conf_mode/system-login.py +++ b/src/conf_mode/system-login.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -16,33 +16,30 @@ import os -from crypt import crypt, METHOD_SHA512 -from netifaces import interfaces +from crypt import crypt +from crypt import METHOD_SHA512 from psutil import users -from pwd import getpwall, getpwnam +from pwd import getpwall +from pwd import getpwnam from spwd import getspnam from sys import exit from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.configverify import verify_vrf from vyos.template import render -from vyos.util import cmd, call, DEVNULL, chmod_600, chmod_755 +from vyos.template import is_ipv4 +from vyos.util import cmd +from vyos.util import call +from vyos.util import DEVNULL +from vyos.util import dict_search +from vyos.xml import defaults from vyos import ConfigError - from vyos import airbag airbag.enable() radius_config_file = "/etc/pam_radius_auth.conf" -default_config_data = { - 'deleted': False, - 'add_users': [], - 'del_users': [], - 'radius_server': [], - 'radius_source_address': '', - 'radius_vrf': '' -} - - def get_local_users(): """Return list of dynamically allocated users (see Debian Policy Manual)""" local_users = [] @@ -57,211 +54,131 @@ def get_local_users(): def get_config(config=None): - login = default_config_data if config: conf = config else: conf = Config() - base_level = ['system', 'login'] - - # We do not need to check if the nodes exist or not and bail out early - # ... this would interrupt the following logic on determine which users - # should be deleted and which users should stay. - # - # All fine so far! - - # Read in all local users and store to list - for username in conf.list_nodes(base_level + ['user']): - user = { - 'name': username, - 'password_plaintext': '', - 'password_encrypted': '!', - 'public_keys': [], - 'full_name': '', - 'home_dir': '/home/' + username, - } - conf.set_level(base_level + ['user', username]) - - # Plaintext password - if conf.exists(['authentication', 'plaintext-password']): - user['password_plaintext'] = conf.return_value( - ['authentication', 'plaintext-password']) - - # Encrypted password - if conf.exists(['authentication', 'encrypted-password']): - user['password_encrypted'] = conf.return_value( - ['authentication', 'encrypted-password']) - - # User real name - if conf.exists(['full-name']): - user['full_name'] = conf.return_value(['full-name']) - - # User home-directory - if conf.exists(['home-directory']): - user['home_dir'] = conf.return_value(['home-directory']) - - # Read in public keys - for id in conf.list_nodes(['authentication', 'public-keys']): - key = { - 'name': id, - 'key': '', - 'options': '', - 'type': '' - } - conf.set_level(base_level + ['user', username, 'authentication', - 'public-keys', id]) - - # Public Key portion - if conf.exists(['key']): - key['key'] = conf.return_value(['key']) - - # Options for individual public key - if conf.exists(['options']): - key['options'] = conf.return_value(['options']) - - # Type of public key - if conf.exists(['type']): - key['type'] = conf.return_value(['type']) - - # Append individual public key to list of user keys - user['public_keys'].append(key) - - login['add_users'].append(user) - - # - # RADIUS configuration - # - conf.set_level(base_level + ['radius']) - - if conf.exists(['source-address']): - login['radius_source_address'] = conf.return_value(['source-address']) - - # retrieve VRF instance - if conf.exists(['vrf']): - login['radius_vrf'] = conf.return_value(['vrf']) - - # Read in all RADIUS servers and store to list - for server in conf.list_nodes(['server']): - server_cfg = { - 'address': server, - 'disabled': False, - 'key': '', - 'port': '1812', - 'timeout': '2', - 'priority': 255 - } - conf.set_level(base_level + ['radius', 'server', server]) - - # Check if RADIUS server was temporary disabled - if conf.exists(['disable']): - server_cfg['disabled'] = True - - # RADIUS shared secret - if conf.exists(['key']): - server_cfg['key'] = conf.return_value(['key']) - - # RADIUS authentication port - if conf.exists(['port']): - server_cfg['port'] = conf.return_value(['port']) - - # RADIUS session timeout - if conf.exists(['timeout']): - server_cfg['timeout'] = conf.return_value(['timeout']) - - # Check if RADIUS server has priority - if conf.exists(['priority']): - server_cfg['priority'] = int(conf.return_value(['priority'])) - - # Append individual RADIUS server configuration to global server list - login['radius_server'].append(server_cfg) + base = ['system', 'login'] + login = conf.get_config_dict(base, key_mangling=('-', '_'), + get_first_key=True) # users no longer existing in the running configuration need to be deleted local_users = get_local_users() - cli_users = [tmp['name'] for tmp in login['add_users']] - # create a list of all users, cli and users - all_users = list(set(local_users+cli_users)) + cli_users = [] + if 'user' in login: + cli_users = list(login['user']) + + # XXX: T2665: we can not safely rely on the defaults() when there are + # tagNodes in place, it is better to blend in the defaults manually. + default_values = defaults(base + ['user']) + for user in login['user']: + login['user'][user] = dict_merge(default_values, login['user'][user]) + + # XXX: T2665: we can not safely rely on the defaults() when there are + # tagNodes in place, it is better to blend in the defaults manually. + default_values = defaults(base + ['radius', 'server']) + for server in dict_search('radius.server', login) or []: + login['radius']['server'][server] = dict_merge(default_values, + login['radius']['server'][server]) + + # XXX: for a yet unknown reason when we only have one source-address + # get_config_dict() will show a string over a string + if 'radius' in login and 'source_address' in login['radius']: + if isinstance(login['radius']['source_address'], str): + login['radius']['source_address'] = [login['radius']['source_address']] - # Remove any normal users that dos not exist in the current configuration. - # This can happen if user is added but configuration was not saved and - # system is rebooted. - login['del_users'] = [tmp for tmp in all_users if tmp not in cli_users] + # create a list of all users, cli and users + all_users = list(set(local_users + cli_users)) + # We will remove any normal users that dos not exist in the current + # configuration. This can happen if user is added but configuration was not + # saved and system is rebooted. + rm_users = [tmp for tmp in all_users if tmp not in cli_users] + if rm_users: login.update({'rm_users' : rm_users}) return login - def verify(login): - cur_user = os.environ['SUDO_USER'] - if cur_user in login['del_users']: - raise ConfigError( - 'Attempting to delete current user: {}'.format(cur_user)) - - for user in login['add_users']: - for key in user['public_keys']: - if not key['type']: - raise ConfigError( - 'SSH public key type missing for "{name}"!'.format(**key)) - - if not key['key']: - raise ConfigError( - 'SSH public key for id "{name}" missing!'.format(**key)) + if 'rm_users' in login: + cur_user = os.environ['SUDO_USER'] + if cur_user in login['rm_users']: + raise ConfigError(f'Attempting to delete current user: {cur_user}') + + if 'user' in login: + for user, user_config in login['user'].items(): + for pubkey, pubkey_options in (dict_search('authentication.public_keys', user_config) or {}).items(): + if 'type' not in pubkey_options: + raise ConfigError(f'Missing type for public-key "{pubkey}"!') + if 'key' not in pubkey_options: + raise ConfigError(f'Missing key for public-key "{pubkey}"!') # At lease one RADIUS server must not be disabled - if len(login['radius_server']) > 0: + if 'radius' in login: + if 'server' not in login['radius']: + raise ConfigError('No RADIUS server defined!') + fail = True - for server in login['radius_server']: - if not server['disabled']: + for server, server_config in dict_search('radius.server', login).items(): + if 'key' not in server_config: + raise ConfigError(f'RADIUS server "{server}" requires key!') + + if 'disabled' not in server_config: fail = False + continue if fail: - raise ConfigError('At least one RADIUS server must be active.') + raise ConfigError('All RADIUS servers are disabled') + + verify_vrf(login['radius']) - vrf_name = login['radius_vrf'] - if vrf_name and vrf_name not in interfaces(): - raise ConfigError(f'VRF "{vrf_name}" does not exist') + if 'source_address' in login['radius']: + ipv4_count = 0 + ipv6_count = 0 + for address in login['radius']['source_address']: + if is_ipv4(address): ipv4_count += 1 + else: ipv6_count += 1 + + if ipv4_count > 1: + raise ConfigError('Only one IPv4 source-address can be set!') + if ipv6_count > 1: + raise ConfigError('Only one IPv6 source-address can be set!') return None def generate(login): # calculate users encrypted password - for user in login['add_users']: - if user['password_plaintext']: - user['password_encrypted'] = crypt( - user['password_plaintext'], METHOD_SHA512) - user['password_plaintext'] = '' - - # remove old plaintext password and set new encrypted password - env = os.environ.copy() - env['vyos_libexec_dir'] = '/usr/libexec/vyos' - - call("/opt/vyatta/sbin/my_delete system login user '{name}' " - "authentication plaintext-password" - .format(**user), env=env) - - call("/opt/vyatta/sbin/my_set system login user '{name}' " - "authentication encrypted-password '{password_encrypted}'" - .format(**user), env=env) - - else: - try: - if getspnam(user['name']).sp_pwdp == user['password_encrypted']: - # If the current encrypted bassword matches the encrypted password - # from the config - do not update it. This will remove the encrypted - # value from the system logs. - # - # The encrypted password will be set only once during the first boot - # after an image upgrade. - user['password_encrypted'] = '' - except: - pass - - if len(login['radius_server']) > 0: - render(radius_config_file, 'system-login/pam_radius_auth.conf.tmpl', - login) - - uid = getpwnam('root').pw_uid - gid = getpwnam('root').pw_gid - os.chown(radius_config_file, uid, gid) - chmod_600(radius_config_file) + if 'user' in login: + for user, user_config in login['user'].items(): + tmp = dict_search('authentication.plaintext_password', user_config) + if tmp: + encrypted_password = crypt(tmp, METHOD_SHA512) + login['user'][user]['authentication']['encrypted_password'] = encrypted_password + del login['user'][user]['authentication']['plaintext_password'] + + # remove old plaintext password and set new encrypted password + env = os.environ.copy() + env['vyos_libexec_dir'] = '/usr/libexec/vyos' + + call(f"/opt/vyatta/sbin/my_delete system login user '{user}' " \ + f"authentication plaintext-password", env=env) + + call(f"/opt/vyatta/sbin/my_set system login user '{user}' " \ + f"authentication encrypted-password '{encrypted_password}'", env=env) + else: + try: + if getspnam(user).sp_pwdp == dict_search('authentication.encrypted_password', user_config): + # If the current encrypted bassword matches the encrypted password + # from the config - do not update it. This will remove the encrypted + # value from the system logs. + # + # The encrypted password will be set only once during the first boot + # after an image upgrade. + del login['user'][user]['authentication']['encrypted_password'] + except: + pass + + if 'radius' in login: + render(radius_config_file, 'login/pam_radius_auth.conf.tmpl', login, + permission=0o600, user='root', group='root') else: if os.path.isfile(radius_config_file): os.unlink(radius_config_file) @@ -270,95 +187,71 @@ def generate(login): def apply(login): - for user in login['add_users']: - # make new user using vyatta shell and make home directory (-m), - # default group of 100 (users) - command = "useradd -m -N" - # check if user already exists: - if user['name'] in get_local_users(): - # update existing account - command = "usermod" - - # all accounts use /bin/vbash - command += " -s /bin/vbash" - # we need to use '' quotes when passing formatted data to the shell - # else it will not work as some data parts are lost in translation - if user['password_encrypted']: - command += " -p '{}'".format(user['password_encrypted']) - - if user['full_name']: - command += " -c '{}'".format(user['full_name']) - - if user['home_dir']: - command += " -d '{}'".format(user['home_dir']) - - command += " -G frrvty,vyattacfg,sudo,adm,dip,disk" - command += " {}".format(user['name']) - - try: - cmd(command) - - uid = getpwnam(user['name']).pw_uid - gid = getpwnam(user['name']).pw_gid - - # we should not rely on the value stored in user['home_dir'], as a - # crazy user will choose username root or any other system user - # which will fail. Should we deny using root at all? - home_dir = getpwnam(user['name']).pw_dir - - # install ssh keys - ssh_key_dir = home_dir + '/.ssh' - if not os.path.isdir(ssh_key_dir): - os.mkdir(ssh_key_dir) - os.chown(ssh_key_dir, uid, gid) - chmod_755(ssh_key_dir) - - ssh_key_file = ssh_key_dir + '/authorized_keys' - with open(ssh_key_file, 'w') as f: - f.write("# Automatically generated by VyOS\n") - f.write("# Do not edit, all changes will be lost\n") - - for id in user['public_keys']: - line = '' - if id['options']: - line = '{} '.format(id['options']) - - line += '{} {} {}\n'.format(id['type'], - id['key'], id['name']) - f.write(line) - - os.chown(ssh_key_file, uid, gid) - chmod_600(ssh_key_file) - - except Exception as e: - print(e) - raise ConfigError('Adding user "{name}" raised exception' - .format(**user)) - - for user in login['del_users']: - try: - # Logout user if he is logged in - if user in list(set([tmp[0] for tmp in users()])): - print('{} is logged in, forcing logout'.format(user)) - call('pkill -HUP -u {}'.format(user)) - - # Remove user account but leave home directory to be safe - call(f'userdel -r {user}', stderr=DEVNULL) - - except Exception as e: - raise ConfigError(f'Deleting user "{user}" raised exception: {e}') + if 'user' in login: + for user, user_config in login['user'].items(): + # make new user using vyatta shell and make home directory (-m), + # default group of 100 (users) + command = 'useradd -m -N' + # check if user already exists: + if user in get_local_users(): + # update existing account + command = 'usermod' + + # all accounts use /bin/vbash + command += ' -s /bin/vbash' + # we need to use '' quotes when passing formatted data to the shell + # else it will not work as some data parts are lost in translation + tmp = dict_search('authentication.encrypted_password', user_config) + if tmp: command += f" -p '{tmp}'" + + tmp = dict_search('full_name', user_config) + if tmp: command += f" -c '{tmp}'" + + tmp = dict_search('home_directory', user_config) + if tmp: command += f" -d '{tmp}'" + else: command += f" -d '/home/{user}'" + + command += f' -G frrvty,vyattacfg,sudo,adm,dip,disk {user}' + try: + cmd(command) + + # we should not rely on the value stored in + # user_config['home_directory'], as a crazy user will choose + # username root or any other system user which will fail. + # + # XXX: Should we deny using root at all? + home_dir = getpwnam(user).pw_dir + render(f'{home_dir}/.ssh/authorized_keys', 'login/authorized_keys.tmpl', + user_config, permission=0o600, user=user, group='users') + + except Exception as e: + raise ConfigError(f'Adding user "{user}" raised exception: "{e}"') + + if 'rm_users' in login: + for user in login['rm_users']: + try: + # Logout user if he is still logged in + if user in list(set([tmp[0] for tmp in users()])): + print(f'{user} is logged in, forcing logout!') + call(f'pkill -HUP -u {user}') + + # Remove user account but leave home directory to be safe + call(f'userdel -r {user}', stderr=DEVNULL) + + except Exception as e: + raise ConfigError(f'Deleting user "{user}" raised exception: {e}') # # RADIUS configuration # - if len(login['radius_server']) > 0: - try: - env = os.environ.copy() - env['DEBIAN_FRONTEND'] = 'noninteractive' + env = os.environ.copy() + env['DEBIAN_FRONTEND'] = 'noninteractive' + try: + if 'radius' in login: # Enable RADIUS in PAM - cmd("pam-auth-update --package --enable radius", env=env) - - # Make NSS system aware of RADIUS, too + cmd('pam-auth-update --package --enable radius', env=env) + # Make NSS system aware of RADIUS + # This fancy snipped was copied from old Vyatta code command = "sed -i -e \'/\smapname/b\' \ -e \'/^passwd:/s/\s\s*/&mapuid /\' \ -e \'/^passwd:.*#/s/#.*/mapname &/\' \ @@ -366,31 +259,20 @@ def apply(login): -e \'/^group:.*#/s/#.*/ mapname &/\' \ -e \'/^group:[^#]*$/s/: */&mapname /\' \ /etc/nsswitch.conf" - - cmd(command) - - except Exception as e: - raise ConfigError('RADIUS configuration failed: {}'.format(e)) - - else: - try: - env = os.environ.copy() - env['DEBIAN_FRONTEND'] = 'noninteractive' - + else: # Disable RADIUS in PAM - cmd("pam-auth-update --package --remove radius", env=env) - + cmd('pam-auth-update --package --remove radius', env=env) + # Drop RADIUS from NSS NSS system + # This fancy snipped was copied from old Vyatta code command = "sed -i -e \'/^passwd:.*mapuid[ \t]/s/mapuid[ \t]//\' \ -e \'/^passwd:.*[ \t]mapname/s/[ \t]mapname//\' \ -e \'/^group:.*[ \t]mapname/s/[ \t]mapname//\' \ -e \'s/[ \t]*$//\' \ /etc/nsswitch.conf" - cmd(command) - - except Exception as e: - raise ConfigError( - 'Removing RADIUS configuration failed.\n{}'.format(e)) + cmd(command) + except Exception as e: + raise ConfigError(f'RADIUS configuration failed: {e}') return None diff --git a/src/conf_mode/system-option.py b/src/conf_mode/system-option.py index 910c14474..454611c55 100755 --- a/src/conf_mode/system-option.py +++ b/src/conf_mode/system-option.py @@ -87,10 +87,10 @@ def apply(options): # Ctrl-Alt-Delete action if os.path.exists(systemd_action_file): os.unlink(systemd_action_file) - if 'ctrl_alt_del' in options: - if options['ctrl_alt_del'] == 'reboot': + if 'ctrl_alt_delete' in options: + if options['ctrl_alt_delete'] == 'reboot': os.symlink('/lib/systemd/system/reboot.target', systemd_action_file) - elif options['ctrl_alt_del'] == 'poweroff': + elif options['ctrl_alt_delete'] == 'poweroff': os.symlink('/lib/systemd/system/poweroff.target', systemd_action_file) # Configure HTTP client @@ -104,11 +104,11 @@ def apply(options): os.unlink(ssh_config) # Reboot system on kernel panic + timeout = '0' + if 'reboot_on_panic' in options: + timeout = '60' with open('/proc/sys/kernel/panic', 'w') as f: - if 'reboot_on_panic' in options: - f.write('60') - else: - f.write('0') + f.write(timeout) # tuned - performance tuning if 'performance' in options: diff --git a/src/conf_mode/system_console.py b/src/conf_mode/system_console.py index b17818797..33a546bd3 100755 --- a/src/conf_mode/system_console.py +++ b/src/conf_mode/system_console.py @@ -17,9 +17,8 @@ import os import re -from fileinput import input as replace_in_file from vyos.config import Config -from vyos.util import call +from vyos.util import call, read_file, write_file from vyos.template import render from vyos import ConfigError, airbag airbag.enable() @@ -98,15 +97,27 @@ def generate(console): if not os.path.isfile(grub_config): return None - # stdin/stdout are redirected in replace_in_file(), thus print() is fine + lines = read_file(grub_config).split('\n') + p = re.compile(r'^(.* console=ttyS0),[0-9]+(.*)$') - for line in replace_in_file(grub_config, inplace=True): + write = False + newlines = [] + for line in lines: if line.startswith('serial --unit'): - line = f'serial --unit=0 --speed={speed}\n' + newline = f'serial --unit=0 --speed={speed}' elif p.match(line): - line = '{},{}{}\n'.format(p.search(line)[1], speed, p.search(line)[2]) + newline = '{},{}{}'.format(p.search(line)[1], speed, p.search(line)[2]) + else: + newline = line + + if newline != line: + write = True + + newlines.append(newline) + newlines.append('') - print(line, end='') + if write: + write_file(grub_config, '\n'.join(newlines)) return None diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py index c4ba859b7..414e514c5 100755 --- a/src/conf_mode/vrf.py +++ b/src/conf_mode/vrf.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -17,32 +17,22 @@ import os from sys import exit -from copy import deepcopy from json import loads from vyos.config import Config -from vyos.configdict import list_diff +from vyos.configdict import node_changed from vyos.ifconfig import Interface -from vyos.util import read_file, cmd -from vyos import ConfigError from vyos.template import render - +from vyos.util import call +from vyos.util import cmd +from vyos.util import dict_search +from vyos.util import get_interface_config +from vyos import ConfigError from vyos import airbag airbag.enable() config_file = r'/etc/iproute2/rt_tables.d/vyos-vrf.conf' -default_config_data = { - 'bind_to_all': '0', - 'deleted': False, - 'vrf_add': [], - 'vrf_existing': [], - 'vrf_remove': [] -} - -def _cmd(command): - cmd(command, raising=ConfigError, message='Error changing VRF') - def list_rules(): command = 'ip -j -4 rule show' answer = loads(cmd(command)) @@ -81,112 +71,61 @@ def get_config(config=None): conf = config else: conf = Config() - vrf_config = deepcopy(default_config_data) - cfg_base = ['vrf'] - if not conf.exists(cfg_base): - # get all currently effetive VRFs and mark them for deletion - vrf_config['vrf_remove'] = conf.list_effective_nodes(cfg_base + ['name']) - else: - # set configuration level base - conf.set_level(cfg_base) - - # Should services be allowed to bind to all VRFs? - if conf.exists(['bind-to-all']): - vrf_config['bind_to_all'] = '1' - - # Determine vrf interfaces (currently effective) - to determine which - # vrf interface is no longer present and needs to be removed - eff_vrf = conf.list_effective_nodes(['name']) - act_vrf = conf.list_nodes(['name']) - vrf_config['vrf_remove'] = list_diff(eff_vrf, act_vrf) - - # read in individual VRF definition and build up - # configuration - for name in conf.list_nodes(['name']): - vrf_inst = { - 'description' : '', - 'members': [], - 'name' : name, - 'table' : '', - 'table_mod': False - } - conf.set_level(cfg_base + ['name', name]) - - if conf.exists(['table']): - # VRF table can't be changed on demand, thus we need to read in the - # current and the effective routing table number - act_table = conf.return_value(['table']) - eff_table = conf.return_effective_value(['table']) - vrf_inst['table'] = act_table - if eff_table and eff_table != act_table: - vrf_inst['table_mod'] = True - - if conf.exists(['description']): - vrf_inst['description'] = conf.return_value(['description']) - - # append individual VRF configuration to global configuration list - vrf_config['vrf_add'].append(vrf_inst) - - # set configuration level base - conf.set_level(cfg_base) - - # check VRFs which need to be removed as they are not allowed to have - # interfaces attached - tmp = [] - for name in vrf_config['vrf_remove']: - vrf_inst = { - 'interfaces': [], - 'name': name, - 'routes': [] - } - - # find member interfaces of this particulat VRF - vrf_inst['interfaces'] = vrf_interfaces(conf, name) - - # find routing protocols used by this VRF - vrf_inst['routes'] = vrf_routing(conf, name) - - # append individual VRF configuration to temporary configuration list - tmp.append(vrf_inst) - - # replace values in vrf_remove with list of dictionaries - # as we need it in verify() - we can't delete a VRF with members attached - vrf_config['vrf_remove'] = tmp - return vrf_config - -def verify(vrf_config): - # ensure VRF is not assigned to any interface - for vrf in vrf_config['vrf_remove']: - if len(vrf['interfaces']) > 0: - raise ConfigError(f"VRF {vrf['name']} can not be deleted. It has active member interfaces!") + base = ['vrf'] + vrf = conf.get_config_dict(base, get_first_key=True) - if len(vrf['routes']) > 0: - raise ConfigError(f"VRF {vrf['name']} can not be deleted. It has active routing protocols!") + # determine which VRF has been removed + for name in node_changed(conf, base + ['name']): + if 'vrf_remove' not in vrf: + vrf.update({'vrf_remove' : {}}) - table_ids = [] - for vrf in vrf_config['vrf_add']: - # table id is mandatory - if not vrf['table']: - raise ConfigError(f"VRF {vrf['name']} table id is mandatory!") + vrf['vrf_remove'][name] = {} + # get VRF bound interfaces + interfaces = vrf_interfaces(conf, name) + if interfaces: vrf['vrf_remove'][name]['interface'] = interfaces + # get VRF bound routing instances + routes = vrf_routing(conf, name) + if routes: vrf['vrf_remove'][name]['route'] = routes - # routing table id can't be changed - OS restriction - if vrf['table_mod']: - raise ConfigError(f"VRF {vrf['name']} table id modification is not possible!") + return vrf - # VRf routing table ID must be unique on the system - if vrf['table'] in table_ids: - raise ConfigError(f"VRF {vrf['name']} table id {vrf['table']} is not unique!") - - table_ids.append(vrf['table']) +def verify(vrf): + # ensure VRF is not assigned to any interface + if 'vrf_remove' in vrf: + for name, config in vrf['vrf_remove'].items(): + if 'interface' in config: + raise ConfigError(f'Can not remove VRF "{name}", it still has '\ + f'member interfaces!') + if 'route' in config: + raise ConfigError(f'Can not remove VRF "{name}", it still has '\ + f'static routes installed!') + + if 'name' in vrf: + table_ids = [] + for name, config in vrf['name'].items(): + # table id is mandatory + if 'table' not in config: + raise ConfigError(f'VRF "{name}" table id is mandatory!') + + # routing table id can't be changed - OS restriction + if os.path.isdir(f'/sys/class/net/{name}'): + tmp = str(dict_search('linkinfo.info_data.table', get_interface_config(name))) + if tmp and tmp != config['table']: + raise ConfigError(f'VRF "{name}" table id modification not possible!') + + # VRf routing table ID must be unique on the system + if config['table'] in table_ids: + raise ConfigError(f'VRF "{name}" table id is not unique!') + table_ids.append(config['table']) return None -def generate(vrf_config): - render(config_file, 'vrf/vrf.conf.tmpl', vrf_config) +def generate(vrf): + render(config_file, 'vrf/vrf.conf.tmpl', vrf) return None -def apply(vrf_config): +def apply(vrf): # Documentation # # - https://github.com/torvalds/linux/blob/master/Documentation/networking/vrf.txt @@ -196,40 +135,48 @@ def apply(vrf_config): # - https://netdevconf.info/1.2/slides/oct6/02_ahern_what_is_l3mdev_slides.pdf # set the default VRF global behaviour - bind_all = vrf_config['bind_to_all'] - if read_file('/proc/sys/net/ipv4/tcp_l3mdev_accept') != bind_all: - _cmd(f'sysctl -wq net.ipv4.tcp_l3mdev_accept={bind_all}') - _cmd(f'sysctl -wq net.ipv4.udp_l3mdev_accept={bind_all}') - - for vrf in vrf_config['vrf_remove']: - name = vrf['name'] - if os.path.isdir(f'/sys/class/net/{name}'): - _cmd(f'ip -4 route del vrf {name} unreachable default metric 4278198272') - _cmd(f'ip -6 route del vrf {name} unreachable default metric 4278198272') - _cmd(f'ip link delete dev {name}') - - for vrf in vrf_config['vrf_add']: - name = vrf['name'] - table = vrf['table'] - - if not os.path.isdir(f'/sys/class/net/{name}'): - # For each VRF apart from your default context create a VRF - # interface with a separate routing table - _cmd(f'ip link add {name} type vrf table {table}') - # Start VRf - _cmd(f'ip link set dev {name} up') - # The kernel Documentation/networking/vrf.txt also recommends - # adding unreachable routes to the VRF routing tables so that routes - # afterwards are taken. - _cmd(f'ip -4 route add vrf {name} unreachable default metric 4278198272') - _cmd(f'ip -6 route add vrf {name} unreachable default metric 4278198272') - # We also should add proper loopback IP addresses to the newly - # created VRFs for services bound to the loopback address (SNMP, NTP) - _cmd(f'ip -4 addr add 127.0.0.1/8 dev {name}') - _cmd(f'ip -6 addr add ::1/128 dev {name}') - - # set VRF description for e.g. SNMP monitoring - Interface(name).set_alias(vrf['description']) + bind_all = '0' + if 'bind_to_all' in vrf: + bind_all = '1' + call(f'sysctl -wq net.ipv4.tcp_l3mdev_accept={bind_all}') + call(f'sysctl -wq net.ipv4.udp_l3mdev_accept={bind_all}') + + for tmp in (dict_search('vrf_remove', vrf) or []): + if os.path.isdir(f'/sys/class/net/{tmp}'): + call(f'ip -4 route del vrf {tmp} unreachable default metric 4278198272') + call(f'ip -6 route del vrf {tmp} unreachable default metric 4278198272') + call(f'ip link delete dev {tmp}') + + if 'name' in vrf: + for name, config in vrf['name'].items(): + table = config['table'] + + if not os.path.isdir(f'/sys/class/net/{name}'): + # For each VRF apart from your default context create a VRF + # interface with a separate routing table + call(f'ip link add {name} type vrf table {table}') + # The kernel Documentation/networking/vrf.txt also recommends + # adding unreachable routes to the VRF routing tables so that routes + # afterwards are taken. + call(f'ip -4 route add vrf {name} unreachable default metric 4278198272') + call(f'ip -6 route add vrf {name} unreachable default metric 4278198272') + # We also should add proper loopback IP addresses to the newly + # created VRFs for services bound to the loopback address (SNMP, NTP) + call(f'ip -4 addr add 127.0.0.1/8 dev {name}') + call(f'ip -6 addr add ::1/128 dev {name}') + + # set VRF description for e.g. SNMP monitoring + vrf_if = Interface(name) + vrf_if.set_alias(config.get('description', '')) + # Enable/Disable of an interface must always be done at the end of the + # derived class to make use of the ref-counting set_admin_state() + # function. We will only enable the interface if 'up' was called as + # often as 'down'. This is required by some interface implementations + # as certain parameters can only be changed when the interface is + # in admin-down state. This ensures the link does not flap during + # reconfiguration. + state = 'down' if 'disable' in config else 'up' + vrf_if.set_admin_state(state) # Linux routing uses rules to find tables - routing targets are then # looked up in those tables. If the lookup got a matching route, the @@ -248,20 +195,20 @@ def apply(vrf_config): local_pref = [r.get('priority') for r in list_rules() if r.get('table') == 'local'][0] # change preference when VRFs are enabled and local lookup table is default - if not local_pref and vrf_config['vrf_add']: + if not local_pref and 'name' in vrf: for af in ['-4', '-6']: - _cmd(f'ip {af} rule add pref 32765 table local') - _cmd(f'ip {af} rule del pref 0') + call(f'ip {af} rule add pref 32765 table local') + call(f'ip {af} rule del pref 0') # return to default lookup preference when no VRF is configured - if not vrf_config['vrf_add']: + if 'name' not in vrf: for af in ['-4', '-6']: - _cmd(f'ip {af} rule add pref 0 table local') - _cmd(f'ip {af} rule del pref 32765') + call(f'ip {af} rule add pref 0 table local') + call(f'ip {af} rule del pref 32765') # clean out l3mdev-table rule if present if 1000 in [r.get('priority') for r in list_rules() if r.get('priority') == 1000]: - _cmd(f'ip {af} rule del pref 1000') + call(f'ip {af} rule del pref 1000') return None diff --git a/src/conf_mode/vrrp.py b/src/conf_mode/vrrp.py index 4510dd3e7..680a80859 100755 --- a/src/conf_mode/vrrp.py +++ b/src/conf_mode/vrrp.py @@ -75,6 +75,7 @@ def get_config(config=None): group["backup_script"] = config.return_value("transition-script backup") group["fault_script"] = config.return_value("transition-script fault") group["stop_script"] = config.return_value("transition-script stop") + group["script_mode_force"] = config.exists("transition-script mode-force") if config.exists("no-preempt"): group["preempt"] = False @@ -183,6 +184,11 @@ def verify(data): if isinstance(pa, IPv4Address): raise ConfigError("VRRP group {0} uses IPv6 but its peer-address is IPv4".format(group["name"])) + # Warn the user about the deprecated mode-force option + if group['script_mode_force']: + print("""Warning: "transition-script mode-force" VRRP option is deprecated and will be removed in VyOS 1.4.""") + print("""It's no longer necessary, so you can safely remove it from your config now.""") + # Disallow same VRID on multiple interfaces _groups = sorted(vrrp_groups, key=(lambda x: x["interface"])) count = len(_groups) - 1 diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper b/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper index d1161e704..fc035766b 100644 --- a/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper +++ b/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper @@ -1,5 +1,8 @@ # redefine ip command to use FRR when it is available +# default route distance +IF_METRIC=${IF_METRIC:-210} + # get status of FRR function frr_alive () { /usr/lib/frr/watchfrr.sh all_status @@ -15,11 +18,12 @@ function frr_alive () { # convert ip route command to vtysh function iptovtysh () { # prepare variables for vtysh command - local VTYSH_DISTANCE="210" - local VTYSH_TAG="210" + local VTYSH_ACTION=$3 local VTYSH_NETADDR="" local VTYSH_GATEWAY="" local VTYSH_DEV="" + local VTYSH_TAG="210" + local VTYSH_DISTANCE="" # convert default route to 0.0.0.0/0 if [ "$4" == "default" ] ; then VTYSH_NETADDR="0.0.0.0/0" @@ -30,26 +34,32 @@ function iptovtysh () { if [[ ! $VTYSH_NETADDR =~ ^.*/[[:digit:]]+$ ]] ; then VTYSH_NETADDR="$VTYSH_NETADDR/32" fi + shift 4 # get gateway address - if [ "$5" == "via" ] ; then - VTYSH_GATEWAY=$6 + if [ "$1" == "via" ] ; then + VTYSH_GATEWAY=$2 + shift 2 fi # get device name - if [ "$5" == "dev" ]; then - VTYSH_DEV=$6 - elif [ "$7" == "dev" ]; then - VTYSH_DEV=$8 + if [ "$1" == "dev" ]; then + VTYSH_DEV=$2 + shift 2 + fi + # get distance + if [ "$1" == "metric" ]; then + VTYSH_DISTANCE=$2 + shift 2 fi # Add route to VRF routing table - local VTYSH_VRF_NAME=$(basename /sys/class/net/$VTYSH_DEV/upper_* | sed -e 's/upper_//') - if [ -n $VTYSH_VRF_NAME ]; then + local VTYSH_VRF_NAME=$(/usr/sbin/ip link show dev $VTYSH_DEV | sed -nre '1s/.* master ([^ ]*) .*/\1/p') + if /usr/sbin/ip -d link show dev $VTYSH_DEV | grep -q "vrf_slave"; then VTYSH_VRF="vrf $VTYSH_VRF_NAME" fi VTYSH_CMD="ip route $VTYSH_NETADDR $VTYSH_GATEWAY $VTYSH_DEV tag $VTYSH_TAG $VTYSH_DISTANCE $VTYSH_VRF" # delete route if the command is "del" - if [ "$3" == "del" ] ; then + if [ "$VTYSH_ACTION" == "del" ] ; then VTYSH_CMD="no $VTYSH_CMD" fi logmsg info "Converted vtysh command: \"$VTYSH_CMD\"" diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup b/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup index b768e1ae5..edb7c7b27 100644 --- a/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup +++ b/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup @@ -13,6 +13,8 @@ if [[ $reason =~ (EXPIRE|FAIL|RELEASE|STOP) ]]; then $hostsd_client --delete-name-servers --tag "dhcp-${interface}" hostsd_changes=y + if_metric="$IF_METRIC" + # try to delete default ip route for router in $old_routers; do # check if we are bound to a VRF @@ -21,8 +23,10 @@ if [[ $reason =~ (EXPIRE|FAIL|RELEASE|STOP) ]]; then vrf="vrf $vrf_name" fi - logmsg info "Deleting default route: via $router dev ${interface} ${vrf}" - ip -4 route del default via $router dev ${interface} ${vrf} + logmsg info "Deleting default route: via $router dev ${interface} ${if_metric:+metric $if_metric} ${vrf}" + ip -4 route del default via $router dev ${interface} ${if_metric:+metric $if_metric} ${vrf} + + if_metric=$((if_metric+1)) done # delete rfc3442 routes diff --git a/src/etc/sysctl.d/30-vyos-router.conf b/src/etc/sysctl.d/30-vyos-router.conf new file mode 100644 index 000000000..8265e12dc --- /dev/null +++ b/src/etc/sysctl.d/30-vyos-router.conf @@ -0,0 +1,98 @@ +# +# VyOS specific sysctl settings, see sysctl.conf (5) for information. +# + +# Panic on OOPS +kernel.panic_on_oops=1 + +# Timeout before rebooting on panic +kernel.panic=60 + +# Send all core files to /var/core/core.program.pid.time +kernel.core_pattern=/var/core/core-%e-%p-%t + +# ARP configuration +# arp_filter - allow multiple network interfaces on same subnet +# arp_announce - avoid local addresses no on target's subnet +# arp_ignore - reply only if target IP is local_address on the interface + +# arp_filter defaults to 1 so set all to 0 so vrrp interfaces can override it. +net.ipv4.conf.all.arp_filter=0 + +# https://phabricator.vyos.net/T300 +net.ipv4.conf.all.arp_ignore=0 + +net.ipv4.conf.all.arp_announce=2 + +# Enable packet forwarding for IPv4 +net.ipv4.ip_forward=1 + +# if a primary address is removed from an interface promote the +# secondary address if available +net.ipv4.conf.all.promote_secondaries=1 + +# Ignore ICMP broadcasts sent to broadcast/multicast +net.ipv4.icmp_echo_ignore_broadcasts=1 + +# Ignore bogus ICMP errors +net.ipv4.icmp_ignore_bogus_error_responses=1 + +# Send ICMP responses with primary address of exiting interface +net.ipv4.icmp_errors_use_inbound_ifaddr=1 + +# Log packets with impossible addresses to kernel log +net.ipv4.conf.all.log_martians=1 + +# Do not ignore all ICMP ECHO requests by default +net.ipv4.icmp_echo_ignore_all=0 + +# Disable source validation by default +net.ipv4.conf.all.rp_filter=0 +net.ipv4.conf.default.rp_filter=0 + +# Enable tcp syn-cookies by default +net.ipv4.tcp_syncookies=1 + +# Disable accept_redirects by default for any interface +net.ipv4.conf.all.accept_redirects=0 +net.ipv4.conf.default.accept_redirects=0 +net.ipv6.conf.all.accept_redirects=0 +net.ipv6.conf.default.accept_redirects=0 + +# Disable accept_source_route by default +net.ipv4.conf.all.accept_source_route=0 +net.ipv4.conf.default.accept_source_route=0 +net.ipv6.conf.all.accept_source_route=0 +net.ipv6.conf.default.accept_source_route=0 + +# Enable send_redirects by default +net.ipv4.conf.all.send_redirects=1 +net.ipv4.conf.default.send_redirects=1 + +# Increase size of buffer for netlink +net.core.rmem_max=2097152 + +# Enable packet forwarding for IPv6 +net.ipv6.conf.all.forwarding=1 + +# Increase route table limit +net.ipv6.route.max_size = 262144 + +# Do not forget IPv6 addresses when a link goes down +net.ipv6.conf.default.keep_addr_on_down=1 +net.ipv6.conf.all.keep_addr_on_down=1 + +# Default value of 20 seems to interfere with larger OSPF and VRRP setups +net.ipv4.igmp_max_memberships = 512 + +# Enable conntrack helper by default +net.netfilter.nf_conntrack_helper=1 + +# Increase default garbage collection thresholds +net.ipv4.neigh.default.gc_thresh1 = 1024 +net.ipv4.neigh.default.gc_thresh2 = 4096 +net.ipv4.neigh.default.gc_thresh3 = 8192 +# +net.ipv6.neigh.default.gc_thresh1 = 1024 +net.ipv6.neigh.default.gc_thresh2 = 4096 +net.ipv6.neigh.default.gc_thresh3 = 8192 diff --git a/src/etc/udev/rules.d/42-qemu-usb.rules b/src/etc/udev/rules.d/42-qemu-usb.rules new file mode 100644 index 000000000..a79543df7 --- /dev/null +++ b/src/etc/udev/rules.d/42-qemu-usb.rules @@ -0,0 +1,14 @@ +# +# Enable autosuspend for qemu emulated usb hid devices. +# +# Note that there are buggy qemu versions which advertise remote +# wakeup support but don't actually implement it correctly. This +# is the reason why we need a match for the serial number here. +# The serial number "42" is used to tag the implementations where +# remote wakeup is working. +# +# Gerd Hoffmann <kraxel@xxxxxxxxxx> + +ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Mouse", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" +ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Tablet", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" +ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Keyboard", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" diff --git a/src/etc/udev/rules.d/63-hyperv-vf-net.rules b/src/etc/udev/rules.d/63-hyperv-vf-net.rules new file mode 100644 index 000000000..b4dcb5a39 --- /dev/null +++ b/src/etc/udev/rules.d/63-hyperv-vf-net.rules @@ -0,0 +1,5 @@ +ATTR{[dmi/id]sys_vendor}!="Microsoft Corporation", GOTO="end_hyperv_nic" + +ACTION=="add", SUBSYSTEM=="net", DRIVERS=="hv_pci", NAME="vf_%k" + +LABEL="end_hyperv_nic" diff --git a/src/etc/udev/rules.d/64-vyos-vmware-net.rules b/src/etc/udev/rules.d/64-vyos-vmware-net.rules new file mode 100644 index 000000000..66a4a069b --- /dev/null +++ b/src/etc/udev/rules.d/64-vyos-vmware-net.rules @@ -0,0 +1,14 @@ +ATTR{[dmi/id]sys_vendor}!="VMware, Inc.", GOTO="end_vmware_nic" + +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet0", ENV{VYOS_IFNAME}="eth0" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet1", ENV{VYOS_IFNAME}="eth1" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet2", ENV{VYOS_IFNAME}="eth2" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet3", ENV{VYOS_IFNAME}="eth3" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet4", ENV{VYOS_IFNAME}="eth4" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet5", ENV{VYOS_IFNAME}="eth5" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet6", ENV{VYOS_IFNAME}="eth6" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet7", ENV{VYOS_IFNAME}="eth7" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet8", ENV{VYOS_IFNAME}="eth8" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet9", ENV{VYOS_IFNAME}="eth9" + +LABEL="end_vmware_nic" diff --git a/src/etc/udev/rules.d/65-vyatta-net.rules b/src/etc/udev/rules.d/65-vyatta-net.rules new file mode 100644 index 000000000..2b48c1213 --- /dev/null +++ b/src/etc/udev/rules.d/65-vyatta-net.rules @@ -0,0 +1,26 @@ +# These rules use vyatta_net_name to persistently name network interfaces +# per "hwid" association in the Vyatta configuration file. + +ACTION!="add", GOTO="vyatta_net_end" +SUBSYSTEM!="net", GOTO="vyatta_net_end" + +# ignore the interface if a name has already been set +NAME=="?*", GOTO="vyatta_net_end" + +# Do name change for ethernet and wireless devices only +KERNEL!="eth*|wlan*", GOTO="vyatta_net_end" + +# ignore "secondary" monitor interfaces of mac80211 drivers +KERNEL=="wlan*", ATTRS{type}=="803", GOTO="vyatta_net_end" + +# If using VyOS predefined names +ENV{VYOS_IFNAME}!="eth*", GOTO="end_vyos_predef_names" + +DRIVERS=="?*", PROGRAM="vyatta_net_name %k $attr{address} $env{VYOS_IFNAME}", NAME="%c", GOTO="vyatta_net_end" + +LABEL="end_vyos_predef_names" + +# ignore interfaces without a driver link like bridges and VLANs +DRIVERS=="?*", PROGRAM="vyatta_net_name %k $attr{address}", NAME="%c" + +LABEL="vyatta_net_end" diff --git a/src/etc/udev/rules.d/99-vyos-wwan.rules b/src/etc/udev/rules.d/99-vyos-wwan.rules new file mode 100644 index 000000000..67f30a3dd --- /dev/null +++ b/src/etc/udev/rules.d/99-vyos-wwan.rules @@ -0,0 +1,11 @@ +ACTION!="add|change", GOTO="mbim_to_qmi_rules_end" + +SUBSYSTEM!="usb", GOTO="mbim_to_qmi_rules_end" + +# ignore any device with only one configuration +ATTR{bNumConfigurations}=="1", GOTO="mbim_to_qmi_rules_end" + +# force Sierra Wireless MC7710 to configuration #1 +ATTR{idVendor}=="1199",ATTR{idProduct}=="68a2",ATTR{bConfigurationValue}="1" + +LABEL="mbim_to_qmi_rules_end" diff --git a/src/helpers/strip-private.py b/src/helpers/strip-private.py new file mode 100755 index 000000000..420a039eb --- /dev/null +++ b/src/helpers/strip-private.py @@ -0,0 +1,147 @@ +#!/usr/bin/python3 + +# Copyright 2021 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + +import argparse +import re +import sys + +from netaddr import IPNetwork, AddrFormatError + + +parser = argparse.ArgumentParser(description='strip off private information from VyOS config') + +strictness = parser.add_mutually_exclusive_group() +strictness.add_argument('--loose', action='store_true', help='remove only information specified as arguments') +strictness.add_argument('--strict', action='store_true', help='remove any private information (implies all arguments below). This is the default behavior.') + +parser.add_argument('--mac', action='store_true', help='strip off MAC addresses') +parser.add_argument('--hostname', action='store_true', help='strip off system host and domain names') +parser.add_argument('--username', action='store_true', help='strip off user names') +parser.add_argument('--dhcp', action='store_true', help='strip off DHCP shared network and static mapping names') +parser.add_argument('--domain', action='store_true', help='strip off domain names') +parser.add_argument('--asn', action='store_true', help='strip off BGP ASNs') +parser.add_argument('--snmp', action='store_true', help='strip off SNMP location information') +parser.add_argument('--lldp', action='store_true', help='strip off LLDP location information') + +address_preserval = parser.add_mutually_exclusive_group() +address_preserval.add_argument('--address', action='store_true', help='strip off all IPv4 and IPv6 addresses') +address_preserval.add_argument('--public-address', action='store_true', help='only strip off public IPv4 and IPv6 addresses') +address_preserval.add_argument('--keep-address', action='store_true', help='preserve all IPv4 and IPv6 addresses') + +# Censor the first half of the address. +ipv4_re = re.compile(r'(\d{1,3}\.){2}(\d{1,3}\.\d{1,3})') +ipv4_subst = r'xxx.xxx.\2' + +# Censor all but the first two fields. +ipv6_re = re.compile(r'([0-9a-fA-F]{1,4}\:){2}(\S+)') +ipv6_subst = r'xxxx:xxxx:\2' + +def ip_match(match: re.Match, subst: str) -> str: + """ + Take a Match and a substitution pattern, check if the match contains a valid IP address, strip + information if it is. This routine is intended to be passed to `re.sub' as a replacement pattern. + """ + result = match.group(0) + # Is this a valid IP address? + try: + addr = IPNetwork(result).ip + # No? Then we've got nothing to do with it. + except AddrFormatError: + return result + # Should we strip it? + if args.address or (args.public_address and not addr.is_private()): + return match.expand(subst) + # No? Then we'll leave it as is. + else: + return result + +def strip_address(line: str) -> str: + """ + Strip IPv4 and IPv6 addresses from the given string. + """ + return ipv4_re.sub(lambda match: ip_match(match, ipv4_subst), ipv6_re.sub(lambda match: ip_match(match, ipv6_subst), line)) + +def strip_lines(rules: tuple) -> None: + """ + Read stdin line by line and apply the given stripping rules. + """ + try: + for line in sys.stdin: + if not args.keep_address: + line = strip_address(line) + for (condition, regexp, subst) in rules: + if condition: + line = regexp.sub(subst, line) + print(line, end='') + # stdin can be cut for any reason, such as user interrupt or the pager terminating before the text can be read. + # All we can do is gracefully exit. + except (BrokenPipeError, EOFError, KeyboardInterrupt): + sys.exit(1) + +if __name__ == "__main__": + args = parser.parse_args() + # Strict mode is the default and the absence of loose mode implies presence of strict mode. + if not args.loose: + for arg in [args.mac, args.domain, args.hostname, args.username, args.dhcp, args.asn, args.snmp, args.lldp]: + arg = True + if not args.public_address and not args.keep_address: + args.address = True + elif not args.address and not args.public_address: + args.keep_address = True + # (condition, precompiled regexp, substitution string) + stripping_rules = [ + # Strip passwords + (True, re.compile(r'password \S+'), 'password xxxxxx'), + # Strip public key information + (True, re.compile(r'public-keys \S+'), 'public-keys xxxx@xxx.xxx'), + (True, re.compile(r'type \'ssh-(rsa|dss)\''), 'type ssh-xxx'), + (True, re.compile(r' key \S+'), ' key xxxxxx'), + # Strip OpenVPN secrets + (True, re.compile(r'(shared-secret-key-file|ca-cert-file|cert-file|dh-file|key-file|client) (\S+)'), r'\1 xxxxxx'), + # Strip IPSEC secrets + (True, re.compile(r'pre-shared-secret \S+'), 'pre-shared-secret xxxxxx'), + # Strip OSPF md5-key + (True, re.compile(r'md5-key \S+'), 'md5-key xxxxxx'), + + # Strip MAC addresses + (args.mac, re.compile(r'([0-9a-fA-F]{2}\:){5}([0-9a-fA-F]{2}((\:{0,1})){3})'), r'XX:XX:XX:XX:XX:\2'), + + # Strip host-name, domain-name, and domain-search + (args.hostname, re.compile(r'(host-name|domain-name|domain-search) \S+'), r'\1 xxxxxx'), + + # Strip user-names + (args.username, re.compile(r'(user|username|user-id) \S+'), r'\1 xxxxxx'), + # Strip full-name + (args.username, re.compile(r'(full-name) [ -_A-Z a-z]+'), r'\1 xxxxxx'), + + # Strip DHCP static-mapping and shared network names + (args.dhcp, re.compile(r'(shared-network-name|static-mapping) \S+'), r'\1 xxxxxx'), + + # Strip host/domain names + (args.domain, re.compile(r' (peer|remote-host|local-host|server) ([\w-]+\.)+[\w-]+'), r' \1 xxxxx.tld'), + + # Strip BGP ASNs + (args.asn, re.compile(r'(bgp|remote-as) (\d+)'), r'\1 XXXXXX'), + + # Strip LLDP location parameters + (args.lldp, re.compile(r'(altitude|datum|latitude|longitude|ca-value|country-code) (\S+)'), r'\1 xxxxxx'), + + # Strip SNMP location + (args.snmp, re.compile(r'(location) \S+'), r'\1 xxxxxx'), + ] + strip_lines(stripping_rules) + diff --git a/src/migration-scripts/bgp/0-to-1 b/src/migration-scripts/bgp/0-to-1 new file mode 100755 index 000000000..b1d5a6514 --- /dev/null +++ b/src/migration-scripts/bgp/0-to-1 @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +# T3417: migrate IS-IS tagNode to node as we can only have one IS-IS process + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['protocols', 'bgp'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +# Only one BGP process is supported, thus this operation is savea +asn = config.list_nodes(base) +bgp_base = base + asn + +# We need a temporary copy of the config +tmp_base = ['protocols', 'bgp2'] +config.copy(bgp_base, tmp_base) + +# Now it's save to delete the old configuration +config.delete(base) + +# Rename temporary copy to new final config and set new "local-as" option +config.rename(tmp_base, 'bgp') +config.set(base + ['local-as'], value=asn[0]) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + exit(1) diff --git a/src/migration-scripts/conntrack/1-to-2 b/src/migration-scripts/conntrack/1-to-2 new file mode 100755 index 000000000..4fc88a1ed --- /dev/null +++ b/src/migration-scripts/conntrack/1-to-2 @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 + +# Delete "set system conntrack modules gre" option + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +if not config.exists(['system', 'conntrack', 'modules', 'gre']): + # Nothing to do + sys.exit(0) +else: + # Delete abandoned node + config.delete(['system', 'conntrack', 'modules', 'gre']) + + try: + with open(file_name, 'w') as f: + f.write(config.to_string()) + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) diff --git a/src/migration-scripts/interfaces/18-to-19 b/src/migration-scripts/interfaces/18-to-19 new file mode 100755 index 000000000..06e07572f --- /dev/null +++ b/src/migration-scripts/interfaces/18-to-19 @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +from sys import argv +from sys import exit +from vyos.configtree import ConfigTree + +def migrate_ospf(config, path, interface): + path = path + ['ospf'] + if config.exists(path): + new_base = ['protocols', 'ospf', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ip ospf" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + +def migrate_ospfv3(config, path, interface): + path = path + ['ospfv3'] + if config.exists(path): + new_base = ['protocols', 'ospfv3', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ipv6 ospfv3" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + +def migrate_rip(config, path, interface): + path = path + ['rip'] + if config.exists(path): + new_base = ['protocols', 'rip', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ip rip" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + +def migrate_ripng(config, path, interface): + path = path + ['ripng'] + if config.exists(path): + new_base = ['protocols', 'ripng', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ipv6 ripng" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + +if __name__ == '__main__': + if (len(argv) < 1): + print("Must specify file name!") + exit(1) + + file_name = argv[1] + with open(file_name, 'r') as f: + config_file = f.read() + + config = ConfigTree(config_file) + + # + # Migrate "interface ethernet eth0 ip ospf" to "protocols ospf interface eth0" + # + for type in config.list_nodes(['interfaces']): + for interface in config.list_nodes(['interfaces', type]): + ip_base = ['interfaces', type, interface, 'ip'] + ipv6_base = ['interfaces', type, interface, 'ipv6'] + migrate_rip(config, ip_base, interface) + migrate_ripng(config, ipv6_base, interface) + migrate_ospf(config, ip_base, interface) + migrate_ospfv3(config, ipv6_base, interface) + + vif_path = ['interfaces', type, interface, 'vif'] + if config.exists(vif_path): + for vif in config.list_nodes(vif_path): + vif_ip_base = vif_path + [vif, 'ip'] + vif_ipv6_base = vif_path + [vif, 'ipv6'] + ifname = f'{interface}.{vif}' + + migrate_rip(config, vif_ip_base, ifname) + migrate_ripng(config, vif_ipv6_base, ifname) + migrate_ospf(config, vif_ip_base, ifname) + migrate_ospfv3(config, vif_ipv6_base, ifname) + + + vif_s_path = ['interfaces', type, interface, 'vif-s'] + if config.exists(vif_s_path): + for vif_s in config.list_nodes(vif_s_path): + vif_s_ip_base = vif_s_path + [vif_s, 'ip'] + vif_s_ipv6_base = vif_s_path + [vif_s, 'ipv6'] + + # vif-c interfaces MUST be migrated before their parent vif-s + # interface as the migrate_*() functions delete the path! + vif_c_path = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c'] + if config.exists(vif_c_path): + for vif_c in config.list_nodes(vif_c_path): + vif_c_ip_base = vif_c_path + [vif_c, 'ip'] + vif_c_ipv6_base = vif_c_path + [vif_c, 'ipv6'] + ifname = f'{interface}.{vif_s}.{vif_c}' + + migrate_rip(config, vif_c_ip_base, ifname) + migrate_ripng(config, vif_c_ipv6_base, ifname) + migrate_ospf(config, vif_c_ip_base, ifname) + migrate_ospfv3(config, vif_c_ipv6_base, ifname) + + + ifname = f'{interface}.{vif_s}' + migrate_rip(config, vif_s_ip_base, ifname) + migrate_ripng(config, vif_s_ipv6_base, ifname) + migrate_ospf(config, vif_s_ip_base, ifname) + migrate_ospfv3(config, vif_s_ipv6_base, ifname) + + try: + with open(file_name, 'w') as f: + f.write(config.to_string()) + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) diff --git a/src/migration-scripts/interfaces/19-to-20 b/src/migration-scripts/interfaces/19-to-20 new file mode 100755 index 000000000..e96663e54 --- /dev/null +++ b/src/migration-scripts/interfaces/19-to-20 @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +from sys import argv +from sys import exit +from vyos.configtree import ConfigTree + +if __name__ == '__main__': + if (len(argv) < 1): + print("Must specify file name!") + exit(1) + + file_name = argv[1] + with open(file_name, 'r') as f: + config_file = f.read() + + config = ConfigTree(config_file) + + for type in ['tunnel', 'l2tpv3']: + base = ['interfaces', type] + if not config.exists(base): + # Nothing to do + continue + + for interface in config.list_nodes(base): + # Migrate "interface tunnel <tunX> encapsulation gre-bridge" to gretap + encap_path = base + [interface, 'encapsulation'] + if type == 'tunnel' and config.exists(encap_path): + tmp = config.return_value(encap_path) + if tmp == 'gre-bridge': + config.set(encap_path, value='gretap') + + # Migrate "interface tunnel|l2tpv3 <interface> local-ip" to source-address + # Migrate "interface tunnel|l2tpv3 <interface> remote-ip" to remote + local_ip_path = base + [interface, 'local-ip'] + if config.exists(local_ip_path): + config.rename(local_ip_path, 'source-address') + + remote_ip_path = base + [interface, 'remote-ip'] + if config.exists(remote_ip_path): + config.rename(remote_ip_path, 'remote') + + try: + with open(file_name, 'w') as f: + f.write(config.to_string()) + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) diff --git a/src/migration-scripts/isis/0-to-1 b/src/migration-scripts/isis/0-to-1 new file mode 100755 index 000000000..93cbbbed5 --- /dev/null +++ b/src/migration-scripts/isis/0-to-1 @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +# T3417: migrate IS-IS tagNode to node as we can only have one IS-IS process + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['protocols', 'isis'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +# Only one IS-IS process is supported, thus this operation is save +isis_base = base + config.list_nodes(base) + +# We need a temporary copy of the config +tmp_base = ['protocols', 'isis2'] +config.copy(isis_base, tmp_base) + +# Now it's save to delete the old configuration +config.delete(base) + +# Rename temporary copy to new final config (IS-IS domain key is static and no +# longer required to be set via CLI) +config.rename(tmp_base, 'isis') + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + exit(1) diff --git a/src/migration-scripts/nat/4-to-5 b/src/migration-scripts/nat/4-to-5 index dda191719..b791996e2 100755 --- a/src/migration-scripts/nat/4-to-5 +++ b/src/migration-scripts/nat/4-to-5 @@ -36,9 +36,15 @@ if not config.exists(['nat']): exit(0) else: for direction in ['source', 'destination']: + # If a node doesn't exist, we obviously have nothing to do. if not config.exists(['nat', direction]): continue + # However, we also need to handle the case when a 'source' or 'destination' sub-node does exist, + # but there are no rules under it. + if not config.list_nodes(['nat', direction]): + continue + for rule in config.list_nodes(['nat', direction, 'rule']): base = ['nat', direction, 'rule', rule] diff --git a/src/migration-scripts/nat66/0-to-1 b/src/migration-scripts/nat66/0-to-1 new file mode 100755 index 000000000..83b421926 --- /dev/null +++ b/src/migration-scripts/nat66/0-to-1 @@ -0,0 +1,71 @@ +#!/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/>. + +from sys import argv,exit +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +def merge_npt(config,base,rule): + merge_base = ['nat66','source','rule',rule] + # Configure migration functions + if config.exists(base + ['description']): + tmp = config.return_value(base + ['description']) + config.set(merge_base + ['description'],value=tmp) + + if config.exists(base + ['disable']): + tmp = config.return_value(base + ['disable']) + config.set(merge_base + ['disable'],value=tmp) + + if config.exists(base + ['outbound-interface']): + tmp = config.return_value(base + ['outbound-interface']) + config.set(merge_base + ['outbound-interface'],value=tmp) + + if config.exists(base + ['source','prefix']): + tmp = config.return_value(base + ['source','prefix']) + config.set(merge_base + ['source','prefix'],value=tmp) + + if config.exists(base + ['translation','prefix']): + tmp = config.return_value(base + ['translation','prefix']) + config.set(merge_base + ['translation','address'],value=tmp) + +if not config.exists(['nat', 'nptv6']): + # Nothing to do + exit(0) + +for rule in config.list_nodes(['nat', 'nptv6', 'rule']): + base = ['nat', 'nptv6', 'rule', rule] + # Merge 'nat nptv6' to 'nat66 source' + merge_npt(config,base,rule) + +# Delete the original NPT configuration +config.delete(['nat','nptv6']); + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) diff --git a/src/migration-scripts/quagga/6-to-7 b/src/migration-scripts/quagga/6-to-7 new file mode 100755 index 000000000..25cf5eebd --- /dev/null +++ b/src/migration-scripts/quagga/6-to-7 @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +# - T3037, BGP address-family ipv6-unicast capability dynamic does not exist in +# FRR, there is only a base, per neighbor dynamic capability, migrate config + +from sys import argv +from sys import exit +from vyos.configtree import ConfigTree +from vyos.template import is_ipv4 +from vyos.template import is_ipv6 + +if (len(argv) < 2): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['protocols', 'bgp'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +# Check if BGP is actually configured and obtain the ASN +asn_list = config.list_nodes(base) +if asn_list: + # There's always just one BGP node, if any + bgp_base = base + [asn_list[0]] + + for neighbor_type in ['neighbor', 'peer-group']: + if not config.exists(bgp_base + [neighbor_type]): + continue + for neighbor in config.list_nodes(bgp_base + [neighbor_type]): + # T2844 - add IPv4 AFI disable-send-community support + send_comm_path = bgp_base + [neighbor_type, neighbor, 'disable-send-community'] + if config.exists(send_comm_path): + new_base = bgp_base + [neighbor_type, neighbor, 'address-family', 'ipv4-unicast'] + config.set(new_base) + config.copy(send_comm_path, new_base + ['disable-send-community']) + config.delete(send_comm_path) + + cap_dynamic = False + peer_group = None + for afi in ['ipv4-unicast', 'ipv6-unicast']: + afi_path = bgp_base + [neighbor_type, neighbor, 'address-family', afi] + # Exit loop early if AFI does not exist + if not config.exists(afi_path): + continue + + cap_path = afi_path + ['capability', 'dynamic'] + if config.exists(cap_path): + cap_dynamic = True + config.delete(cap_path) + + # We have now successfully migrated the address-family + # specific dynamic capability to the neighbor/peer-group + # level. If this has been the only option under the + # address-family nodes, we can clean them up by checking if + # no other nodes are left under that tree and if so, delete + # the parent. + # + # We walk from the most inner node to the most outer one. + cleanup = -1 + while len(config.list_nodes(cap_path[:cleanup])) == 0: + config.delete(cap_path[:cleanup]) + cleanup -= 1 + + peer_group_path = afi_path + ['peer-group'] + if config.exists(peer_group_path): + if ((is_ipv4(neighbor) and afi == 'ipv4-unicast') or + (is_ipv6(neighbor) and afi == 'ipv6-unicast')): + peer_group = config.return_value(peer_group_path) + + config.delete(peer_group_path) + + # We have now successfully migrated the address-family + # specific peer-group to the neighbor level. If this has + # been the only option under the address-family nodes, we + # can clean them up by checking if no other nodes are left + # under that tree and if so, delete the parent. + # + # We walk from the most inner node to the most outer one. + cleanup = -1 + while len(config.list_nodes(peer_group_path[:cleanup])) == 0: + config.delete(peer_group_path[:cleanup]) + cleanup -= 1 + + if cap_dynamic: + config.set(bgp_base + [neighbor_type, neighbor, 'capability', 'dynamic']) + if peer_group: + config.set(bgp_base + [neighbor_type, neighbor, 'peer-group'], value=peer_group) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) diff --git a/src/migration-scripts/quagga/7-to-8 b/src/migration-scripts/quagga/7-to-8 new file mode 100755 index 000000000..9c277a6f1 --- /dev/null +++ b/src/migration-scripts/quagga/7-to-8 @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +# - T2450: drop interface-route and interface-route6 from "protocols static" + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree + +def migrate_interface_route(config, base, path, route_route6): + """ Generic migration function which can be called on every instance of + interface-route, beeing it ipv4, ipv6 or nested under the "static table" nodes. + + What we do? + - Drop 'interface-route' or 'interface-route6' and migrate the route unter the + 'route' or 'route6' tag node. + """ + if config.exists(base + path): + for route in config.list_nodes(base + path): + interface = config.list_nodes(base + path + [route, 'next-hop-interface']) + + tmp = base + path + [route, 'next-hop-interface'] + for interface in config.list_nodes(tmp): + new_base = base + [route_route6, route, 'interface'] + config.set(new_base) + config.set_tag(base + [route_route6]) + config.set_tag(new_base) + config.copy(tmp + [interface], new_base + [interface]) + + config.delete(base + path) + +def migrate_route(config, base, path, route_route6): + """ Generic migration function which can be called on every instance of + route, beeing it ipv4, ipv6 or even nested under the static table nodes. + + What we do? + - for consistency reasons rename next-hop-interface to interface + - for consistency reasons rename next-hop-vrf to vrf + """ + if config.exists(base + path): + for route in config.list_nodes(base + path): + next_hop = base + path + [route, 'next-hop'] + if config.exists(next_hop): + for gateway in config.list_nodes(next_hop): + # IPv4 routes calls it next-hop-interface, rename this to + # interface instead so it's consitent with IPv6 + interface_path = next_hop + [gateway, 'next-hop-interface'] + if config.exists(interface_path): + config.rename(interface_path, 'interface') + + # When VRFs got introduced, I (c-po) named it next-hop-vrf, + # we can also call it vrf which is simply shorter. + vrf_path = next_hop + [gateway, 'next-hop-vrf'] + if config.exists(vrf_path): + config.rename(vrf_path, 'vrf') + + +if (len(argv) < 2): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['protocols', 'static'] + +config = ConfigTree(config_file) +if not config.exists(base): + # Nothing to do + exit(0) + +# Migrate interface-route into route +migrate_interface_route(config, base, ['interface-route'], 'route') + +# Migrate interface-route6 into route6 +migrate_interface_route(config, base, ['interface-route6'], 'route6') + +# Cleanup nodes inside route +migrate_route(config, base, ['route'], 'route') + +# Cleanup nodes inside route6 +migrate_route(config, base, ['route6'], 'route6') + +# +# PBR table cleanup +table_path = base + ['table'] +if config.exists(table_path): + for table in config.list_nodes(table_path): + # Migrate interface-route into route + migrate_interface_route(config, table_path + [table], ['interface-route'], 'route') + + # Migrate interface-route6 into route6 + migrate_interface_route(config, table_path + [table], ['interface-route6'], 'route6') + + # Cleanup nodes inside route + migrate_route(config, table_path + [table], ['route'], 'route') + + # Cleanup nodes inside route6 + migrate_route(config, table_path + [table], ['route6'], 'route6') + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) diff --git a/src/migration-scripts/quagga/8-to-9 b/src/migration-scripts/quagga/8-to-9 new file mode 100755 index 000000000..15c44924f --- /dev/null +++ b/src/migration-scripts/quagga/8-to-9 @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +# - T3391: Migrate "maximum-paths" setting from "protocols bgp asn maximum-paths" +# under the IPv4 address-family tree. Reason is we currently have no way in +# configuring this for IPv6 address-family. This mimics the FRR configuration. + +from sys import argv +from sys import exit +from vyos.configtree import ConfigTree + +if (len(argv) < 2): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['protocols', 'bgp'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +# Check if BGP is actually configured and obtain the ASN +asn_list = config.list_nodes(base) +if asn_list: + # There's always just one BGP node, if any + bgp_base = base + [asn_list[0]] + + maximum_paths = bgp_base + ['maximum-paths'] + if config.exists(maximum_paths): + for bgp_type in ['ebgp', 'ibgp']: + if config.exists(maximum_paths + [bgp_type]): + new_base = bgp_base + ['address-family', 'ipv4-unicast', 'maximum-paths'] + config.set(new_base) + config.copy(maximum_paths + [bgp_type], new_base + [bgp_type]) + config.delete(maximum_paths) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) diff --git a/src/migration-scripts/rpki/0-to-1 b/src/migration-scripts/rpki/0-to-1 new file mode 100755 index 000000000..5b4893205 --- /dev/null +++ b/src/migration-scripts/rpki/0-to-1 @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +from sys import exit +from sys import argv +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['protocols', 'rpki'] +config = ConfigTree(config_file) + +# Nothing to do +if not config.exists(base): + exit(0) + +if config.exists(base + ['cache']): + preference = 1 + for cache in config.list_nodes(base + ['cache']): + address_node = base + ['cache', cache, 'address'] + if config.exists(address_node): + address = config.return_value(address_node) + # We do not longer support the address leafNode, RPKI cache server + # IP address is now used from the tagNode + config.delete(address_node) + # VyOS 1.2 had no per instance preference, setting new defaults + config.set(base + ['cache', cache, 'preference'], value=preference) + # Increase preference for the next caching peer - actually VyOS 1.2 + # supported only one but better save then sorry (T3253) + preference += 1 + + # T3293: If the RPKI cache name equals the configured address, + # renaming is not possible, as rename expects the new path to not + # exist. + if not config.exists(base + ['cache', address]): + config.rename(base + ['cache', cache], address) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) diff --git a/src/migration-scripts/system/18-to-19 b/src/migration-scripts/system/18-to-19 index dd2abce00..fd0e15d42 100755 --- a/src/migration-scripts/system/18-to-19 +++ b/src/migration-scripts/system/18-to-19 @@ -80,8 +80,8 @@ else: dhcp_interfaces.append(f'{intf}.{vif_s}') # try vif-c - if config.exists(intf_base + ['vif-c', vif_c]): - for vif_c in config.list_nodes(vif_s_base + ['vif-c', vif_c]): + if config.exists(intf_base + ['vif-c']): + for vif_c in config.list_nodes(vif_s_base + ['vif-c']): vif_c_base = vif_s_base + ['vif-c', vif_c] if config.exists(vif_c_base + ['address']): for addr in config.return_values(vif_c_base + ['address']): diff --git a/src/migration-scripts/vrf/0-to-1 b/src/migration-scripts/vrf/0-to-1 new file mode 100755 index 000000000..29b2fab74 --- /dev/null +++ b/src/migration-scripts/vrf/0-to-1 @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +# - T2450: drop interface-route and interface-route6 from "protocols vrf" + +from sys import argv +from sys import exit +from vyos.configtree import ConfigTree + +if (len(argv) < 2): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['protocols', 'vrf'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +for vrf in config.list_nodes(base): + static_base = base + [vrf, 'static'] + if not config.exists(static_base): + continue + + # + # Migrate interface-route into route + # + interface_route_path = static_base + ['interface-route'] + if config.exists(interface_route_path): + for route in config.list_nodes(interface_route_path): + interface = config.list_nodes(interface_route_path + [route, 'next-hop-interface']) + + tmp = interface_route_path + [route, 'next-hop-interface'] + for interface in config.list_nodes(tmp): + new_base = static_base + ['route', route, 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(tmp + [interface], new_base + [interface]) + + config.delete(interface_route_path) + + # + # Migrate interface-route6 into route6 + # + interface_route_path = static_base + ['interface-route6'] + if config.exists(interface_route_path): + for route in config.list_nodes(interface_route_path): + interface = config.list_nodes(interface_route_path + [route, 'next-hop-interface']) + + tmp = interface_route_path + [route, 'next-hop-interface'] + for interface in config.list_nodes(tmp): + new_base = static_base + ['route6', route, 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(tmp + [interface], new_base + [interface]) + + config.delete(interface_route_path) + + # + # Cleanup nodes inside route + # + route_path = static_base + ['route'] + if config.exists(route_path): + for route in config.list_nodes(route_path): + next_hop = route_path + [route, 'next-hop'] + if config.exists(next_hop): + for gateway in config.list_nodes(next_hop): + interface_path = next_hop + [gateway, 'next-hop-interface'] + if config.exists(interface_path): + config.rename(interface_path, 'interface') + vrf_path = next_hop + [gateway, 'next-hop-vrf'] + if config.exists(vrf_path): + config.rename(vrf_path, 'vrf') + + # + # Cleanup nodes inside route6 + # + route_path = static_base + ['route6'] + if config.exists(route_path): + for route in config.list_nodes(route_path): + next_hop = route_path + [route, 'next-hop'] + if config.exists(next_hop): + for gateway in config.list_nodes(next_hop): + vrf_path = next_hop + [gateway, 'next-hop-vrf'] + if config.exists(vrf_path): + config.rename(vrf_path, 'vrf') + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) diff --git a/src/migration-scripts/vrf/1-to-2 b/src/migration-scripts/vrf/1-to-2 new file mode 100755 index 000000000..20128e957 --- /dev/null +++ b/src/migration-scripts/vrf/1-to-2 @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +# - T3344: migrate routing options from "protocols vrf" to "vrf <name> protocols" + +from sys import argv +from sys import exit +from vyos.configtree import ConfigTree + +if (len(argv) < 2): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['protocols', 'vrf'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +vrf_base = ['vrf', 'name'] +config.set(vrf_base) +config.set_tag(vrf_base) + +# Copy all existing static routes to the new base node under "vrf name <name> protocols static" +for vrf in config.list_nodes(base): + static_base = base + [vrf, 'static'] + if not config.exists(static_base): + continue + + new_static_base = vrf_base + [vrf, 'protocols'] + config.set(new_static_base) + config.copy(static_base, new_static_base + ['static']) + +# Now delete the old configuration +config.delete(base) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) diff --git a/src/op_mode/powerctrl.py b/src/op_mode/powerctrl.py index c000d7d06..f8b5a3dda 100755 --- a/src/op_mode/powerctrl.py +++ b/src/op_mode/powerctrl.py @@ -177,7 +177,7 @@ def main(): action="store_true") action.add_argument("--check", - help="Check pending chutdown", + help="Check pending shutdown", action="store_true") args = parser.parse_args() diff --git a/src/op_mode/ppp-server-ctrl.py b/src/op_mode/ppp-server-ctrl.py index 171107b4a..670cdf879 100755 --- a/src/op_mode/ppp-server-ctrl.py +++ b/src/op_mode/ppp-server-ctrl.py @@ -59,7 +59,10 @@ def main(): output, err = popen(cmd_dict['cmd_base'].format(cmd_dict['vpn_types'][args.proto]) + args.action + ses_pattern, stderr=DEVNULL, decode='utf-8') if not err: - print(output) + try: + print(output) + except: + sys.exit(0) else: print("{} server is not running".format(args.proto)) diff --git a/src/op_mode/show_interfaces.py b/src/op_mode/show_interfaces.py index de41274a7..39e5dc7ac 100755 --- a/src/op_mode/show_interfaces.py +++ b/src/op_mode/show_interfaces.py @@ -30,19 +30,28 @@ from vyos.util import cmd # interfaces = Sections.reserved() -interfaces = ['eno', 'ens', 'enp', 'enx', 'eth', 'vmnet', 'lo', 'tun', 'wan', 'pppoe', 'pppoa', 'adsl'] +interfaces = ['eno', 'ens', 'enp', 'enx', 'eth', 'vmnet', 'lo', 'tun', 'wan', 'pppoe'] glob_ifnames = '/sys/class/net/({})*'.format('|'.join(interfaces)) actions = {} -def register (name): +def register(name): """ - decorator to register a function into actions with a name - it allows to use actions[name] to call the registered function + Decorator to register a function into actions with a name. + `actions[name]' can be used to call the registered functions. + We wrap each function in a SIGPIPE handler as all registered functions + can be subject to a broken pipe if there are a lot of interfaces. """ def _register(function): - actions[name] = function - return function + def handled_function(*args, **kwargs): + try: + function(*args, **kwargs) + except BrokenPipeError: + # Flush output to /dev/null and bail out. + os.dup2(os.open(os.devnull, os.O_WRONLY), sys.stdout.fileno()) + sys.exit(1) + actions[name] = handled_function + return handled_function return _register @@ -168,7 +177,7 @@ def run_show_intf(ifnames, iftypes, vif, vrrp): out = cmd(f'ip addr show {interface.ifname}') out = re.sub(f'^\d+:\s+','',out) - if re.search("link/tunnel6", out): + if re.search('link/tunnel6', out): tunnel = cmd(f'ip -6 tun show {interface.ifname}') # tun0: ip/ipv6 remote ::2 local ::1 encaplimit 4 hoplimit 64 tclass inherit flowlabel inherit (flowinfo 0x00000000) tunnel = re.sub('.*encap', 'encap', tunnel) diff --git a/src/op_mode/show_ipsec_sa.py b/src/op_mode/show_ipsec_sa.py index b7927fcc2..066e36b5e 100755 --- a/src/op_mode/show_ipsec_sa.py +++ b/src/op_mode/show_ipsec_sa.py @@ -70,6 +70,7 @@ for sa in sas: else: for csa in installed_sas: isa = installed_sas[csa] + csa_name = isa['name'] bytes_in = hurry.filesize.size(int(isa["bytes-in"].decode())) bytes_out = hurry.filesize.size(int(isa["bytes-out"].decode())) @@ -103,7 +104,7 @@ for sa in sas: if dh_group: proposal = "{0}/{1}".format(proposal, dh_group) - data = [peer, state, uptime, bytes_str, pkts_str, remote_host, remote_id, proposal] + data = [csa_name, state, uptime, bytes_str, pkts_str, remote_host, remote_id, proposal] sa_data.append(data) headers = ["Connection", "State", "Uptime", "Bytes In/Out", "Packets In/Out", "Remote address", "Remote ID", "Proposal"] diff --git a/src/op_mode/show_nat66_rules.py b/src/op_mode/show_nat66_rules.py new file mode 100755 index 000000000..fe5113015 --- /dev/null +++ b/src/op_mode/show_nat66_rules.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 jmespath +import json + +from argparse import ArgumentParser +from jinja2 import Template +from sys import exit +from vyos.util import cmd +from vyos.util import dict_search + +parser = ArgumentParser() +group = parser.add_mutually_exclusive_group() +group.add_argument("--source", help="Show statistics for configured source NAT rules", action="store_true") +group.add_argument("--destination", help="Show statistics for configured destination NAT rules", action="store_true") +args = parser.parse_args() + +if args.source or args.destination: + tmp = cmd('sudo nft -j list table ip6 nat') + tmp = json.loads(tmp) + + format_nat66_rule = '{0: <10} {1: <50} {2: <50} {3: <10}' + print(format_nat66_rule.format("Rule", "Source" if args.source else "Destination", "Translation", "Outbound Interface" if args.source else "Inbound Interface")) + print(format_nat66_rule.format("----", "------" if args.source else "-----------", "-----------", "------------------" if args.source else "-----------------")) + + data_json = jmespath.search('nftables[?rule].rule[?chain]', tmp) + for idx in range(0, len(data_json)): + data = data_json[idx] + + # If there is no index 3, we don't think this is the record we need to check + if len(data['expr']) <= 3: + continue + + comment = data['comment'] + rule = comment.replace('SRC-NAT66-','') + rule = rule.replace('DST-NAT66-','') + chain = data['chain'] + if not (args.source and chain == 'POSTROUTING') or (not args.source and chain == 'PREROUTING'): + continue + interface = dict_search('match.right', data['expr'][0]) + srcdest = dict_search('match.right.prefix.addr', data['expr'][2]) + if srcdest: + addr_tmp = dict_search('match.right.prefix.len', data['expr'][2]) + if addr_tmp: + srcdest = srcdest + '/' + str(addr_tmp) + else: + srcdest = dict_search('match.right', data['expr'][2]) + + tran_addr = dict_search('snat.addr.prefix.addr' if args.source else 'dnat.addr.prefix.addr', data['expr'][3]) + if tran_addr: + addr_tmp = dict_search('snat.addr.prefix.len' if args.source else 'dnat.addr.prefix.len', data['expr'][3]) + if addr_tmp: + srcdest = srcdest + '/' + str(addr_tmp) + else: + if 'masquerade' in data['expr'][3]: + tran_addr = 'masquerade' + else: + tran_addr = dict_search('snat.addr' if args.source else 'dnat.addr', data['expr'][3]) + + print(format_nat66_rule.format(rule, srcdest, tran_addr, interface)) + + exit(0) +else: + parser.print_help() + exit(1) + diff --git a/src/op_mode/show_nat66_statistics.py b/src/op_mode/show_nat66_statistics.py new file mode 100755 index 000000000..bc81692ae --- /dev/null +++ b/src/op_mode/show_nat66_statistics.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2018 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 jmespath +import json + +from argparse import ArgumentParser +from jinja2 import Template +from sys import exit +from vyos.util import cmd + +OUT_TMPL_SRC=""" +rule pkts bytes interface +---- ---- ----- --------- +{% for r in output %} +{% if r.comment %} +{% set packets = r.counter.packets %} +{% set bytes = r.counter.bytes %} +{% set interface = r.interface %} +{# remove rule comment prefix #} +{% set comment = r.comment | replace('SRC-NAT66-', '') | replace('DST-NAT66-', '') %} +{{ "%-4s" | format(comment) }} {{ "%9s" | format(packets) }} {{ "%12s" | format(bytes) }} {{ interface }} +{% endif %} +{% endfor %} +""" + +parser = ArgumentParser() +group = parser.add_mutually_exclusive_group() +group.add_argument("--source", help="Show statistics for configured source NAT rules", action="store_true") +group.add_argument("--destination", help="Show statistics for configured destination NAT rules", action="store_true") +args = parser.parse_args() + +if args.source or args.destination: + tmp = cmd('sudo nft -j list table ip6 nat') + tmp = json.loads(tmp) + + source = r"nftables[?rule.chain=='POSTROUTING'].rule.{chain: chain, handle: handle, comment: comment, counter: expr[].counter | [0], interface: expr[].match.right | [0] }" + destination = r"nftables[?rule.chain=='PREROUTING'].rule.{chain: chain, handle: handle, comment: comment, counter: expr[].counter | [0], interface: expr[].match.right | [0] }" + data = { + 'output' : jmespath.search(source if args.source else destination, tmp), + 'direction' : 'source' if args.source else 'destination' + } + + tmpl = Template(OUT_TMPL_SRC, lstrip_blocks=True) + print(tmpl.render(data)) + exit(0) +else: + parser.print_help() + exit(1) + diff --git a/src/op_mode/show_nat66_translations.py b/src/op_mode/show_nat66_translations.py new file mode 100755 index 000000000..045d64065 --- /dev/null +++ b/src/op_mode/show_nat66_translations.py @@ -0,0 +1,204 @@ +#!/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/>. + +''' +show nat translations +''' + +import os +import sys +import ipaddress +import argparse +import xmltodict + +from vyos.util import popen +from vyos.util import DEVNULL + +conntrack = '/usr/sbin/conntrack' + +verbose_format = "%-20s %-18s %-20s %-18s" +normal_format = "%-20s %-20s %-4s %-8s %s" + + +def headers(verbose, pipe): + if verbose: + return verbose_format % ('Pre-NAT src', 'Pre-NAT dst', 'Post-NAT src', 'Post-NAT dst') + return normal_format % ('Pre-NAT', 'Post-NAT', 'Prot', 'Timeout', 'Type' if pipe else '') + + +def command(srcdest, proto, ipaddr): + command = f'{conntrack} -o xml -L -f ipv6' + + if proto: + command += f' -p {proto}' + + if srcdest == 'source': + command += ' -n' + if ipaddr: + command += f' --orig-src {ipaddr}' + if srcdest == 'destination': + command += ' -g' + if ipaddr: + command += f' --orig-dst {ipaddr}' + + return command + + +def run(command): + xml, code = popen(command,stderr=DEVNULL) + if code: + sys.exit('conntrack failed') + return xml + + +def content(xmlfile): + xml = '' + with open(xmlfile,'r') as r: + xml += r.read() + return xml + + +def pipe(): + xml = '' + while True: + line = sys.stdin.readline() + xml += line + if '</conntrack>' in line: + break + + sys.stdin = open('/dev/tty') + return xml + + +def process(data, stats, protocol, pipe, verbose, flowtype=''): + if not data: + return + + parsed = xmltodict.parse(data) + + print(headers(verbose, pipe)) + + # to help the linter to detect typos + ORIGINAL = 'original' + REPLY = 'reply' + INDEPENDANT = 'independent' + SPORT = 'sport' + DPORT = 'dport' + SRC = 'src' + DST = 'dst' + + for rule in parsed['conntrack']['flow']: + src, dst, sport, dport, proto = {}, {}, {}, {}, {} + packet_count, byte_count = {}, {} + timeout, use = 0, 0 + + rule_type = rule.get('type', '') + + for meta in rule['meta']: + # print(meta) + direction = meta['@direction'] + + if direction in (ORIGINAL, REPLY): + if 'layer3' in meta: + l3 = meta['layer3'] + src[direction] = l3[SRC] + dst[direction] = l3[DST] + + if 'layer4' in meta: + l4 = meta['layer4'] + sp = l4.get(SPORT, '') + dp = l4.get(DPORT, '') + if sp: + sport[direction] = sp + if dp: + dport[direction] = dp + proto[direction] = l4.get('@protoname','') + + if stats and 'counters' in meta: + packet_count[direction] = meta['packets'] + byte_count[direction] = meta['bytes'] + continue + + if direction == INDEPENDANT: + timeout = meta['timeout'] + use = meta['use'] + continue + + in_src = '%s:%s' % (src[ORIGINAL], sport[ORIGINAL]) if ORIGINAL in sport else src[ORIGINAL] + in_dst = '%s:%s' % (dst[ORIGINAL], dport[ORIGINAL]) if ORIGINAL in dport else dst[ORIGINAL] + + # inverted the the perl code !!? + out_dst = '%s:%s' % (dst[REPLY], dport[REPLY]) if REPLY in dport else dst[REPLY] + out_src = '%s:%s' % (src[REPLY], sport[REPLY]) if REPLY in sport else src[REPLY] + + if flowtype == 'source': + v = ORIGINAL in sport and REPLY in dport + f = '%s:%s' % (src[ORIGINAL], sport[ORIGINAL]) if v else src[ORIGINAL] + t = '%s:%s' % (dst[REPLY], dport[REPLY]) if v else dst[REPLY] + else: + v = ORIGINAL in dport and REPLY in sport + f = '%s:%s' % (dst[ORIGINAL], dport[ORIGINAL]) if v else dst[ORIGINAL] + t = '%s:%s' % (src[REPLY], sport[REPLY]) if v else src[REPLY] + + # Thomas: I do not believe proto should be an option + p = proto.get('original', '') + if protocol and p != protocol: + continue + + if verbose: + msg = verbose_format % (in_src, in_dst, out_dst, out_src) + p = f'{p}: ' if p else '' + msg += f'\n {p}{f} ==> {t}' + msg += f' timeout: {timeout}' if timeout else '' + msg += f' use: {use} ' if use else '' + msg += f' type: {rule_type}' if rule_type else '' + print(msg) + else: + print(normal_format % (f, t, p, timeout, rule_type if rule_type else '')) + + if stats: + for direction in ('original', 'reply'): + if direction in packet_count: + print(' %-8s: packets %s, bytes %s' % direction, packet_count[direction], byte_count[direction]) + + +def main(): + parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__) + parser.add_argument('--verbose', help='provide more details about the flows', action='store_true') + parser.add_argument('--proto', help='filter by protocol', default='', type=str) + parser.add_argument('--file', help='read the conntrack xml from a file', type=str) + parser.add_argument('--stats', help='add usage statistics', action='store_true') + parser.add_argument('--type', help='NAT type (source, destination)', required=True, type=str) + parser.add_argument('--ipaddr', help='source ip address to filter on', type=ipaddress.ip_address) + parser.add_argument('--pipe', help='read conntrack xml data from stdin', action='store_true') + + arg = parser.parse_args() + + if arg.type not in ('source', 'destination'): + sys.exit('Unknown NAT type!') + + if arg.pipe: + process(pipe(), arg.stats, arg.proto, arg.pipe, arg.verbose, arg.type) + elif arg.file: + process(content(arg.file), arg.stats, arg.proto, arg.pipe, arg.verbose, arg.type) + else: + try: + process(run(command(arg.type, arg.proto, arg.ipaddr)), arg.stats, arg.proto, arg.pipe, arg.verbose, arg.type) + except: + pass + +if __name__ == '__main__': + main() diff --git a/src/op_mode/show_nat_rules.py b/src/op_mode/show_nat_rules.py new file mode 100755 index 000000000..a98fbef8c --- /dev/null +++ b/src/op_mode/show_nat_rules.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 jmespath +import json + +from argparse import ArgumentParser +from jinja2 import Template +from sys import exit +from vyos.util import cmd +from vyos.util import dict_search + +parser = ArgumentParser() +group = parser.add_mutually_exclusive_group() +group.add_argument("--source", help="Show statistics for configured source NAT rules", action="store_true") +group.add_argument("--destination", help="Show statistics for configured destination NAT rules", action="store_true") +args = parser.parse_args() + +if args.source or args.destination: + tmp = cmd('sudo nft -j list table ip nat') + tmp = json.loads(tmp) + + format_nat66_rule = '{0: <10} {1: <50} {2: <50} {3: <10}' + print(format_nat66_rule.format("Rule", "Source" if args.source else "Destination", "Translation", "Outbound Interface" if args.source else "Inbound Interface")) + print(format_nat66_rule.format("----", "------" if args.source else "-----------", "-----------", "------------------" if args.source else "-----------------")) + + data_json = jmespath.search('nftables[?rule].rule[?chain]', tmp) + for idx in range(0, len(data_json)): + data = data_json[idx] + comment = data['comment'] + rule = int(''.join(list(filter(str.isdigit, comment)))) + chain = data['chain'] + if not (args.source and chain == 'POSTROUTING') or (not args.source and chain == 'PREROUTING'): + continue + interface = dict_search('match.right', data['expr'][0]) + srcdest = dict_search('match.right.prefix.addr', data['expr'][1]) + if srcdest: + addr_tmp = dict_search('match.right.prefix.len', data['expr'][1]) + if addr_tmp: + srcdest = srcdest + '/' + str(addr_tmp) + else: + srcdest = dict_search('match.right', data['expr'][1]) + tran_addr = dict_search('snat.addr.prefix.addr' if args.source else 'dnat.addr.prefix.addr', data['expr'][3]) + if tran_addr: + addr_tmp = dict_search('snat.addr.prefix.len' if args.source else 'dnat.addr.prefix.len', data['expr'][3]) + if addr_tmp: + srcdest = srcdest + '/' + str(addr_tmp) + else: + if 'masquerade' in data['expr'][3]: + tran_addr = 'masquerade' + elif 'log' in data['expr'][3]: + continue + else: + tran_addr = dict_search('snat.addr' if args.source else 'dnat.addr', data['expr'][3]) + + print(format_nat66_rule.format(rule, srcdest, tran_addr, interface)) + + exit(0) +else: + parser.print_help() + exit(1) + diff --git a/src/op_mode/show_nat_statistics.py b/src/op_mode/show_nat_statistics.py index 482993d06..c568c8305 100755 --- a/src/op_mode/show_nat_statistics.py +++ b/src/op_mode/show_nat_statistics.py @@ -44,7 +44,7 @@ group.add_argument("--destination", help="Show statistics for configured destina args = parser.parse_args() if args.source or args.destination: - tmp = cmd('sudo nft -j list table nat') + tmp = cmd('sudo nft -j list table ip nat') tmp = json.loads(tmp) source = r"nftables[?rule.chain=='POSTROUTING'].rule.{chain: chain, handle: handle, comment: comment, counter: expr[].counter | [0], interface: expr[].match.right | [0] }" diff --git a/src/op_mode/show_nat_translations.py b/src/op_mode/show_nat_translations.py index 04c20e584..25091e9fc 100755 --- a/src/op_mode/show_nat_translations.py +++ b/src/op_mode/show_nat_translations.py @@ -51,6 +51,8 @@ def command(srcdest, proto, ipaddr): command += f' --orig-src {ipaddr}' if srcdest == 'destination': command += ' -g' + if ipaddr: + command += f' --orig-dst {ipaddr}' return command diff --git a/src/op_mode/show_neigh.py b/src/op_mode/show_neigh.py new file mode 100755 index 000000000..94e745493 --- /dev/null +++ b/src/op_mode/show_neigh.py @@ -0,0 +1,96 @@ +#!/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/>. + +#ip -j -f inet neigh list | jq +#[ + #{ + #"dst": "192.168.101.8", + #"dev": "enp0s25", + #"lladdr": "78:d2:94:72:77:7e", + #"state": [ + #"STALE" + #] + #}, + #{ + #"dst": "192.168.101.185", + #"dev": "enp0s25", + #"lladdr": "34:46:ec:76:f8:9b", + #"state": [ + #"STALE" + #] + #}, + #{ + #"dst": "192.168.101.225", + #"dev": "enp0s25", + #"lladdr": "c2:cb:fa:bf:a0:35", + #"state": [ + #"STALE" + #] + #}, + #{ + #"dst": "192.168.101.1", + #"dev": "enp0s25", + #"lladdr": "00:98:2b:f8:3f:11", + #"state": [ + #"REACHABLE" + #] + #}, + #{ + #"dst": "192.168.101.181", + #"dev": "enp0s25", + #"lladdr": "d8:9b:3b:d5:88:22", + #"state": [ + #"STALE" + #] + #} +#] + +import sys +import argparse +import json +from vyos.util import cmd + +def main(): + #parese args + parser = argparse.ArgumentParser() + parser.add_argument('--family', help='Protocol family', required=True) + args = parser.parse_args() + + neigh_raw_json = cmd(f'ip -j -f {args.family} neigh list') + neigh_raw_json = neigh_raw_json.lower() + neigh_json = json.loads(neigh_raw_json) + + format_neigh = '%-50s %-10s %-20s %s' + print(format_neigh % ("IP Address", "Device", "State", "LLADDR")) + print(format_neigh % ("----------", "------", "-----", "------")) + + if neigh_json is not None: + for neigh_item in neigh_json: + dev = neigh_item['dev'] + dst = neigh_item['dst'] + lladdr = neigh_item['lladdr'] if 'lladdr' in neigh_item else '' + state = neigh_item['state'] + + i = 0 + for state_item in state: + if i == 0: + print(format_neigh % (dst, dev, state_item, lladdr)) + else: + print(format_neigh % ('', '', state_item, '')) + i+=1 + +if __name__ == '__main__': + main() diff --git a/src/op_mode/show_ntp.sh b/src/op_mode/show_ntp.sh new file mode 100755 index 000000000..e9dd6c5c9 --- /dev/null +++ b/src/op_mode/show_ntp.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +basic=0 +info=0 + +while [[ "$#" -gt 0 ]]; do + case $1 in + --info) info=1 ;; + --basic) basic=1 ;; + --server) server=$2; shift ;; + *) echo "Unknown parameter passed: $1" ;; + esac + shift +done + +if ! ps -C ntpd &>/dev/null; then + echo NTP daemon disabled + exit 1 +fi + +PID=$(pgrep ntpd) +VRF_NAME=$(ip vrf identify ${PID}) + +if [ ! -z ${VRF_NAME} ]; then + VRF_CMD="sudo ip vrf exec ${VRF_NAME}" +fi + +if [ $basic -eq 1 ]; then + $VRF_CMD ntpq -n -c peers +elif [ $info -eq 1 ]; then + echo "=== sysingo ===" + $VRF_CMD ntpq -n -c sysinfo + echo + echo "=== kerninfo ===" + $VRF_CMD ntpq -n -c kerninfo +elif [ ! -z $server ]; then + $VRF_CMD /usr/sbin/ntpdate -q $server +fi + diff --git a/src/op_mode/vtysh_wrapper.sh b/src/op_mode/vtysh_wrapper.sh new file mode 100755 index 000000000..47d88330b --- /dev/null +++ b/src/op_mode/vtysh_wrapper.sh @@ -0,0 +1,4 @@ +#!/bin/sh +declare -a tmp +tmp=$@ +vtysh -c "$tmp" diff --git a/src/services/vyos-configd b/src/services/vyos-configd index 5b1ab1f1f..6f770b696 100755 --- a/src/services/vyos-configd +++ b/src/services/vyos-configd @@ -25,6 +25,7 @@ import logging import signal import importlib.util import zmq +from contextlib import contextmanager from vyos.defaults import directories from vyos.configsource import ConfigSourceString, ConfigSourceError @@ -33,6 +34,8 @@ from vyos import ConfigError CFG_GROUP = 'vyattacfg' +script_stdout_log = '/tmp/vyos-configd-script-stdout' + debug = True logger = logging.getLogger(__name__) @@ -59,7 +62,8 @@ configd_env_unset_file = os.path.join(directories['data'], 'vyos-configd-env-uns # sourced on entering config session configd_env_file = '/etc/default/vyos-configd-env' -session_tty = None +session_out = None +session_mode = None def key_name_from_file_name(f): return os.path.splitext(f)[0] @@ -104,16 +108,33 @@ conf_mode_scripts = dict(zip(imports, modules)) exclude_set = {key_name_from_file_name(f) for f in filenames if f not in include} include_set = {key_name_from_file_name(f) for f in filenames if f in include} -def explicit_print(t, m): +@contextmanager +def stdout_redirected(filename, mode): + saved_stdout_fd = None + destination_file = None try: - with open(t, 'w') as f: - f.write(m) - f.write("\n") - f.flush() - except Exception: - pass + sys.stdout.flush() + saved_stdout_fd = os.dup(sys.stdout.fileno()) + destination_file = open(filename, mode) + os.dup2(destination_file.fileno(), sys.stdout.fileno()) + yield + finally: + if saved_stdout_fd is not None: + os.dup2(saved_stdout_fd, sys.stdout.fileno()) + os.close(saved_stdout_fd) + if destination_file is not None: + destination_file.close() + +def explicit_print(path, mode, msg): + try: + with open(path, mode) as f: + f.write(f"\n{msg}\n\n") + except OSError: + logger.critical("error explicit_print") -def run_script(script, config) -> int: +def run_script(script, config, args) -> int: + if args: + script.argv = args config.set_level([]) try: c = script.get_config(config) @@ -122,15 +143,17 @@ def run_script(script, config) -> int: script.apply(c) except ConfigError as e: logger.critical(e) - explicit_print(session_tty, str(e)) + explicit_print(session_out, session_mode, str(e)) return R_ERROR_COMMIT - except Exception: + except Exception as e: + logger.critical(e) return R_ERROR_DAEMON return R_SUCCESS def initialization(socket): - global session_tty + global session_out + global session_mode # Reset config strings: active_string = '' session_string = '' @@ -158,9 +181,15 @@ def initialization(socket): logger.debug(f"config session pid is {pid_string}") try: - session_tty = os.readlink(f"/proc/{pid_string}/fd/1") + session_out = os.readlink(f"/proc/{pid_string}/fd/1") + session_mode = 'w' except FileNotFoundError: - session_tty = None + session_out = None + + # if not a 'live' session, for example on boot, write to file + if not session_out or not os.path.isfile('/tmp/vyos-config-status'): + session_out = script_stdout_log + session_mode = 'a' try: configsource = ConfigSourceString(running_config_text=active_string, @@ -179,22 +208,26 @@ def process_node_data(config, data) -> int: return R_ERROR_DAEMON script_name = None + args = None - res = re.match(r'^.+\/([^/].+).py(VYOS_TAGNODE_VALUE=.+)?', data) + res = re.match(r'^(VYOS_TAGNODE_VALUE=[^/]+)?.*\/([^/]+).py(.*)', data) if res.group(1): - script_name = res.group(1) - if res.group(2): - env = res.group(2).split('=') + env = res.group(1).split('=') os.environ[env[0]] = env[1] - + if res.group(2): + script_name = res.group(2) if not script_name: logger.critical(f"Missing script_name") return R_ERROR_DAEMON + if res.group(3): + args = res.group(3).split() + args.insert(0, f'{script_name}.py') - if script_name in exclude_set: + if script_name not in include_set: return R_PASS - result = run_script(conf_mode_scripts[script_name], config) + with stdout_redirected(session_out, session_mode): + result = run_script(conf_mode_scripts[script_name], config, args) return result diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server index 703628558..8069d7146 100755 --- a/src/services/vyos-http-api-server +++ b/src/services/vyos-http-api-server @@ -1,6 +1,6 @@ -#!/usr/bin/env python3 +#!/usr/share/vyos-http-api-tools/bin/python3 # -# Copyright (C) 2019 VyOS maintainers and contributors +# Copyright (C) 2019-2021 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 @@ -19,25 +19,37 @@ import os import sys import grp +import copy import json +import logging import traceback import threading -import signal +from typing import List, Union, Callable, Dict -import vyos.config - -from flask import Flask, request -from waitress import serve +import uvicorn +from fastapi import FastAPI, Depends, Request, Response, HTTPException +from fastapi.responses import HTMLResponse +from fastapi.exceptions import RequestValidationError +from fastapi.routing import APIRoute +from pydantic import BaseModel, StrictStr, validator -from functools import wraps +import vyos.config from vyos.configsession import ConfigSession, ConfigSessionError - DEFAULT_CONFIG_FILE = '/etc/vyos/http-api.conf' CFG_GROUP = 'vyattacfg' -app = Flask(__name__) +debug = True + +logger = logging.getLogger(__name__) +logs_handler = logging.StreamHandler() +logger.addHandler(logs_handler) + +if debug: + logger.setLevel(logging.DEBUG) +else: + logger.setLevel(logging.INFO) # Giant lock! lock = threading.Lock() @@ -56,55 +68,310 @@ def check_auth(key_list, key): def error(code, msg): resp = {"success": False, "error": msg, "data": None} - return json.dumps(resp), code + resp = json.dumps(resp) + return HTMLResponse(resp, status_code=code) def success(data): resp = {"success": True, "data": data, "error": None} - return json.dumps(resp) - -def get_command(f): - @wraps(f) - def decorated_function(*args, **kwargs): - cmd = request.form.get("data") - if not cmd: - return error(400, "Non-empty data field is required") - try: - cmd = json.loads(cmd) - except Exception as e: - return error(400, "Failed to parse JSON: {0}".format(e)) - return f(cmd, *args, **kwargs) - - return decorated_function - -def auth_required(f): - @wraps(f) - def decorated_function(*args, **kwargs): - key = request.form.get("key") - api_keys = app.config['vyos_keys'] - id = check_auth(api_keys, key) - if not id: - return error(401, "Valid API key is required") - return f(*args, **kwargs) - - return decorated_function - -@app.route('/configure', methods=['POST']) -@get_command -@auth_required -def configure_op(commands): - session = app.config['vyos_session'] + resp = json.dumps(resp) + return HTMLResponse(resp) + +# Pydantic models for validation +# Pydantic will cast when possible, so use StrictStr +# validators added as needed for additional constraints +# schema_extra adds anotations to OpenAPI, to add examples + +class ApiModel(BaseModel): + key: StrictStr + +class BaseConfigureModel(BaseModel): + op: StrictStr + path: List[StrictStr] + value: StrictStr = None + + @validator("path", pre=True, always=True) + def check_non_empty(cls, path): + assert len(path) > 0 + return path + +class ConfigureModel(ApiModel): + op: StrictStr + path: List[StrictStr] + value: StrictStr = None + + @validator("path", pre=True, always=True) + def check_non_empty(cls, path): + assert len(path) > 0 + return path + + class Config: + schema_extra = { + "example": { + "key": "id_key", + "op": "set | delete | comment", + "path": ['config', 'mode', 'path'], + } + } + +class ConfigureListModel(ApiModel): + commands: List[BaseConfigureModel] + + class Config: + schema_extra = { + "example": { + "key": "id_key", + "commands": "list of commands", + } + } + +class RetrieveModel(ApiModel): + op: StrictStr + path: List[StrictStr] + configFormat: StrictStr = None + + class Config: + schema_extra = { + "example": { + "key": "id_key", + "op": "returnValue | returnValues | exists | showConfig", + "path": ['config', 'mode', 'path'], + "configFormat": "json (default) | json_ast | raw", + + } + } + +class ConfigFileModel(ApiModel): + op: StrictStr + file: StrictStr = None + + class Config: + schema_extra = { + "example": { + "key": "id_key", + "op": "save | load", + "file": "filename", + } + } + +class ImageModel(ApiModel): + op: StrictStr + url: StrictStr = None + name: StrictStr = None + + class Config: + schema_extra = { + "example": { + "key": "id_key", + "op": "add | delete", + "url": "imagelocation", + "name": "imagename", + } + } + +class GenerateModel(ApiModel): + op: StrictStr + path: List[StrictStr] + + class Config: + schema_extra = { + "example": { + "key": "id_key", + "op": "generate", + "path": ["op", "mode", "path"], + } + } + +class ShowModel(ApiModel): + op: StrictStr + path: List[StrictStr] + + class Config: + schema_extra = { + "example": { + "key": "id_key", + "op": "show", + "path": ["op", "mode", "path"], + } + } + +class Success(BaseModel): + success: bool + data: Union[str, bool, Dict] + error: str + +class Error(BaseModel): + success: bool = False + data: Union[str, bool, Dict] + error: str + +responses = { + 200: {'model': Success}, + 400: {'model': Error}, + 422: {'model': Error, 'description': 'Validation Error'}, + 500: {'model': Error} +} + +def auth_required(data: ApiModel): + key = data.key + api_keys = app.state.vyos_keys + id = check_auth(api_keys, key) + if not id: + raise HTTPException(status_code=401, detail="Valid API key is required") + app.state.vyos_id = id + +# override Request and APIRoute classes in order to convert form request to json; +# do all explicit validation here, for backwards compatability of error messages; +# the explicit validation may be dropped, if desired, in favor of native +# validation by FastAPI/Pydantic, as is used for application/json requests +class MultipartRequest(Request): + ERR_MISSING_KEY = False + ERR_MISSING_DATA = False + ERR_NOT_JSON = False + ERR_NOT_DICT = False + ERR_NO_OP = False + ERR_NO_PATH = False + ERR_EMPTY_PATH = False + ERR_PATH_NOT_LIST = False + ERR_VALUE_NOT_STRING = False + ERR_PATH_NOT_LIST_OF_STR = False + offending_command = {} + exception = None + async def body(self) -> bytes: + if not hasattr(self, "_body"): + forms = {} + merge = {} + body = await super().body() + self._body = body + + form_data = await self.form() + if form_data: + logger.debug("processing form data") + for k, v in form_data.multi_items(): + forms[k] = v + + if 'data' not in forms: + self.ERR_MISSING_DATA = True + else: + try: + tmp = json.loads(forms['data']) + except json.JSONDecodeError as e: + self.ERR_NOT_JSON = True + self.exception = e + tmp = {} + if isinstance(tmp, list): + merge['commands'] = tmp + else: + merge = tmp + + if 'commands' in merge: + cmds = merge['commands'] + else: + cmds = copy.deepcopy(merge) + cmds = [cmds] + + for c in cmds: + if not isinstance(c, dict): + self.ERR_NOT_DICT = True + self.offending_command = c + elif 'op' not in c: + self.ERR_NO_OP = True + self.offending_command = c + elif 'path' not in c: + self.ERR_NO_PATH = True + self.offending_command = c + elif not c['path']: + self.ERR_EMPTY_PATH = True + self.offending_command = c + elif not isinstance(c['path'], list): + self.ERR_PATH_NOT_LIST = True + self.offending_command = c + elif not all(isinstance(el, str) for el in c['path']): + self.ERR_PATH_NOT_LIST_OF_STR = True + self.offending_command = c + elif 'value' in c and not isinstance(c['value'], str): + self.ERR_VALUE_NOT_STRING = True + self.offending_command = c + + if 'key' not in forms and 'key' not in merge: + self.ERR_MISSING_KEY = True + if 'key' in forms and 'key' not in merge: + merge['key'] = forms['key'] + + new_body = json.dumps(merge) + new_body = new_body.encode() + self._body = new_body + + return self._body + +class MultipartRoute(APIRoute): + def get_route_handler(self) -> Callable: + original_route_handler = super().get_route_handler() + + async def custom_route_handler(request: Request) -> Response: + request = MultipartRequest(request.scope, request.receive) + endpoint = request.url.path + try: + response: Response = await original_route_handler(request) + except HTTPException as e: + return error(e.status_code, e.detail) + except Exception as e: + if request.ERR_MISSING_KEY: + return error(422, "Valid API key is required") + if request.ERR_MISSING_DATA: + return error(422, "Non-empty data field is required") + if request.ERR_NOT_JSON: + return error(400, "Failed to parse JSON: {0}".format(request.exception)) + if endpoint == '/configure': + if request.ERR_NOT_DICT: + return error(400, "Malformed command \"{0}\": any command must be a dict".format(json.dumps(request.offending_command))) + if request.ERR_NO_OP: + return error(400, "Malformed command \"{0}\": missing \"op\" field".format(json.dumps(request.offending_command))) + if request.ERR_NO_PATH: + return error(400, "Malformed command \"{0}\": missing \"path\" field".format(json.dumps(request.offending_command))) + if request.ERR_EMPTY_PATH: + return error(400, "Malformed command \"{0}\": empty path".format(json.dumps(request.offending_command))) + if request.ERR_PATH_NOT_LIST: + return error(400, "Malformed command \"{0}\": \"path\" field must be a list".format(json.dumps(request.offending_command))) + if request.ERR_VALUE_NOT_STRING: + return error(400, "Malformed command \"{0}\": \"value\" field must be a string".format(json.dumps(request.offending_command))) + if request.ERR_PATH_NOT_LIST_OF_STR: + return error(400, "Malformed command \"{0}\": \"path\" field must be a list of strings".format(json.dumps(request.offending_command))) + if endpoint in ('/retrieve','/generate','/show'): + if request.ERR_NO_OP or request.ERR_NO_PATH: + return error(400, "Missing required field. \"op\" and \"path\" fields are required") + if endpoint in ('/config-file', '/image'): + if request.ERR_NO_OP: + return error(400, "Missing required field \"op\"") + + raise e + + return response + + return custom_route_handler + +app = FastAPI(debug=True, + title="VyOS API", + version="0.1.0", + responses={**responses}, + dependencies=[Depends(auth_required)]) + +app.router.route_class = MultipartRoute + +@app.exception_handler(RequestValidationError) +async def validation_exception_handler(request, exc): + return error(400, str(exc.errors()[0])) + +@app.post('/configure') +def configure_op(data: Union[ConfigureModel, ConfigureListModel]): + session = app.state.vyos_session env = session.get_session_env() config = vyos.config.Config(session_env=env) - strict_field = request.form.get("strict") - if strict_field == "true": - strict = True - else: - strict = False - # Allow users to pass just one command - if not isinstance(commands, list): - commands = [commands] + if not isinstance(data, ConfigureListModel): + data = [data] + else: + data = data.commands # We don't want multiple people/apps to be able to commit at once, # or modify the shared session while someone else is doing the same, @@ -114,53 +381,25 @@ def configure_op(commands): status = 200 error_msg = None try: - for c in commands: - # What we've got may not even be a dict - if not isinstance(c, dict): - raise ConfigSessionError("Malformed command \"{0}\": any command must be a dict".format(json.dumps(c))) - - # Missing op or path is a show stopper - if not ('op' in c): - raise ConfigSessionError("Malformed command \"{0}\": missing \"op\" field".format(json.dumps(c))) - if not ('path' in c): - raise ConfigSessionError("Malformed command \"{0}\": missing \"path\" field".format(json.dumps(c))) - - # Missing value is fine, substitute for empty string - if 'value' in c: - value = c['value'] - else: - value = "" - - op = c['op'] - path = c['path'] - - if not path: - raise ConfigSessionError("Malformed command \"{0}\": empty path".format(json.dumps(c))) - - # Type checking - if not isinstance(path, list): - raise ConfigSessionError("Malformed command \"{0}\": \"path\" field must be a list".format(json.dumps(c))) + for c in data: + op = c.op + path = c.path - if not isinstance(value, str): - raise ConfigSessionError("Malformed command \"{0}\": \"value\" field must be a string".format(json.dumps(c))) - - # Account for the case when value field is present and set to null - if not value: + if c.value: + value = c.value + else: value = "" - # For vyos.configsessios calls that have no separate value arguments, + # For vyos.configsession calls that have no separate value arguments, # and for type checking too - try: - cfg_path = " ".join(path + [value]).strip() - except TypeError: - raise ConfigSessionError("Malformed command \"{0}\": \"path\" field must be a list of strings".format(json.dumps(c))) + cfg_path = " ".join(path + [value]).strip() if op == 'set': # XXX: it would be nice to do a strict check for "path already exists", # but there's probably no way to do that session.set(path, value=value) elif op == 'delete': - if strict and not config.exists(cfg_path): + if app.state.vyos_strict and not config.exists(cfg_path): raise ConfigSessionError("Cannot delete [{0}]: path/value does not exist".format(cfg_path)) session.delete(path, value=value) elif op == 'comment': @@ -169,16 +408,16 @@ def configure_op(commands): raise ConfigSessionError("\"{0}\" is not a valid operation".format(op)) # end for session.commit() - print("Configuration modified via HTTP API using key \"{0}\"".format(id)) + logger.info(f"Configuration modified via HTTP API using key '{app.state.vyos_id}'") except ConfigSessionError as e: session.discard() status = 400 - if app.config['vyos_debug']: - print(traceback.format_exc(), file=sys.stderr) + if app.state.vyos_debug: + logger.critical(f"ConfigSessionError:\n {traceback.format_exc()}") error_msg = str(e) except Exception as e: session.discard() - print(traceback.format_exc(), file=sys.stderr) + logger.critical(traceback.format_exc()) status = 500 # Don't give the details away to the outer world @@ -188,22 +427,17 @@ def configure_op(commands): if status != 200: return error(status, error_msg) - else: - return success(None) -@app.route('/retrieve', methods=['POST']) -@get_command -@auth_required -def retrieve_op(command): - session = app.config['vyos_session'] + return success(None) + +@app.post("/retrieve") +def retrieve_op(data: RetrieveModel): + session = app.state.vyos_session env = session.get_session_env() config = vyos.config.Config(session_env=env) - try: - op = command['op'] - path = " ".join(command['path']) - except KeyError: - return error(400, "Missing required field. \"op\" and \"path\" fields are required") + op = data.op + path = " ".join(data.path) try: if op == 'returnValue': @@ -214,10 +448,10 @@ def retrieve_op(command): res = config.exists(path) elif op == 'showConfig': config_format = 'json' - if 'configFormat' in command: - config_format = command['configFormat'] + if data.configFormat: + config_format = data.configFormat - res = session.show_config(path=command['path']) + res = session.show_config(path=data.path) if config_format == 'json': config_tree = vyos.configtree.ConfigTree(res) res = json.loads(config_tree.to_json()) @@ -233,33 +467,28 @@ def retrieve_op(command): except ConfigSessionError as e: return error(400, str(e)) except Exception as e: - print(traceback.format_exc(), file=sys.stderr) + logger.critical(traceback.format_exc()) return error(500, "An internal error occured. Check the logs for details.") return success(res) -@app.route('/config-file', methods=['POST']) -@get_command -@auth_required -def config_file_op(command): - session = app.config['vyos_session'] +@app.post('/config-file') +def config_file_op(data: ConfigFileModel): + session = app.state.vyos_session - try: - op = command['op'] - except KeyError: - return error(400, "Missing required field \"op\"") + op = data.op try: if op == 'save': - try: - path = command['file'] - except KeyError: + if data.file: + path = data.file + else: path = '/config/config.boot' res = session.save_config(path) elif op == 'load': - try: - path = command['file'] - except KeyError: + if data.file: + path = data.file + else: return error(400, "Missing required field \"file\"") res = session.migrate_and_load_config(path) res = session.commit() @@ -268,33 +497,28 @@ def config_file_op(command): except ConfigSessionError as e: return error(400, str(e)) except Exception as e: - print(traceback.format_exc(), file=sys.stderr) + logger.critical(traceback.format_exc()) return error(500, "An internal error occured. Check the logs for details.") return success(res) -@app.route('/image', methods=['POST']) -@get_command -@auth_required -def image_op(command): - session = app.config['vyos_session'] +@app.post('/image') +def image_op(data: ImageModel): + session = app.state.vyos_session - try: - op = command['op'] - except KeyError: - return error(400, "Missing required field \"op\"") + op = data.op try: if op == 'add': - try: - url = command['url'] - except KeyError: + if data.url: + url = data.url + else: return error(400, "Missing required field \"url\"") res = session.install_image(url) elif op == 'delete': - try: - name = command['name'] - except KeyError: + if data.name: + name = data.name + else: return error(400, "Missing required field \"name\"") res = session.remove_image(name) else: @@ -302,26 +526,17 @@ def image_op(command): except ConfigSessionError as e: return error(400, str(e)) except Exception as e: - print(traceback.format_exc(), file=sys.stderr) + logger.critical(traceback.format_exc()) return error(500, "An internal error occured. Check the logs for details.") return success(res) +@app.post('/generate') +def generate_op(data: GenerateModel): + session = app.state.vyos_session -@app.route('/generate', methods=['POST']) -@get_command -@auth_required -def generate_op(command): - session = app.config['vyos_session'] - - try: - op = command['op'] - path = command['path'] - except KeyError: - return error(400, "Missing required field. \"op\" and \"path\" fields are required") - - if not isinstance(path, list): - return error(400, "Malformed command: \"path\" field must be a list of strings") + op = data.op + path = data.path try: if op == 'generate': @@ -331,25 +546,17 @@ def generate_op(command): except ConfigSessionError as e: return error(400, str(e)) except Exception as e: - print(traceback.format_exc(), file=sys.stderr) + logger.critical(traceback.format_exc()) return error(500, "An internal error occured. Check the logs for details.") return success(res) -@app.route('/show', methods=['POST']) -@get_command -@auth_required -def show_op(command): - session = app.config['vyos_session'] +@app.post('/show') +def show_op(data: ShowModel): + session = app.state.vyos_session - try: - op = command['op'] - path = command['path'] - except KeyError: - return error(400, "Missing required field. \"op\" and \"path\" fields are required") - - if not isinstance(path, list): - return error(400, "Malformed command: \"path\" field must be a list of strings") + op = data.op + path = data.path try: if op == 'show': @@ -359,14 +566,11 @@ def show_op(command): except ConfigSessionError as e: return error(400, str(e)) except Exception as e: - print(traceback.format_exc(), file=sys.stderr) + logger.critical(traceback.format_exc()) return error(500, "An internal error occured. Check the logs for details.") return success(res) -def shutdown(): - raise KeyboardInterrupt - if __name__ == '__main__': # systemd's user and group options don't work, do it by hand here, # else no one else will be able to commit @@ -380,21 +584,20 @@ if __name__ == '__main__': try: server_config = load_server_config() except Exception as e: - print("Failed to load the HTTP API server config: {0}".format(e)) + logger.critical("Failed to load the HTTP API server config: {0}".format(e)) session = ConfigSession(os.getpid()) - app.config['vyos_session'] = session - app.config['vyos_keys'] = server_config['api_keys'] - app.config['vyos_debug'] = server_config['debug'] - - def sig_handler(signum, frame): - shutdown() + app.state.vyos_session = session + app.state.vyos_keys = server_config['api_keys'] - signal.signal(signal.SIGTERM, sig_handler) + app.state.vyos_debug = True if server_config['debug'] == 'true' else False + app.state.vyos_strict = True if server_config['strict'] == 'true' else False try: - serve(app, host=server_config["listen_address"], - port=server_config["port"]) + uvicorn.run(app, host=server_config["listen_address"], + port=int(server_config["port"]), + proxy_headers=True) except OSError as e: - print(f"OSError {e}") + logger.critical(f"OSError {e}") + sys.exit(1) diff --git a/src/shim/vyshim.c b/src/shim/vyshim.c index 196e3221e..cae8b6152 100644 --- a/src/shim/vyshim.c +++ b/src/shim/vyshim.c @@ -75,28 +75,32 @@ int main(int argc, char* argv[]) void *context = zmq_ctx_new(); void *requester = zmq_socket(context, ZMQ_REQ); + int ex_index; int init_timeout = 0; debug_print("Connecting to vyos-configd ...\n"); zmq_connect(requester, SOCKET_PATH); + for (int i = 1; i < argc ; i++) { + strncat(&string_node_data[0], argv[i], 127); + } + + debug_print("data to send: %s\n", string_node_data); + + char *test = strstr(string_node_data, "VYOS_TAGNODE_VALUE"); + ex_index = test ? 2 : 1; + if (access(COMMIT_MARKER, F_OK) != -1) { init_timeout = initialization(requester); if (!init_timeout) remove(COMMIT_MARKER); } - int end = argc > 3 ? 2 : argc - 1; - // if initial communication failed, pass through execution of script if (init_timeout) { - int ret = pass_through(argv, end); + int ret = pass_through(argv, ex_index); return ret; } - for (int i = end; i > 0 ; i--) { - strncat(&string_node_data[0], argv[i], 127); - } - char error_code[1]; debug_print("Sending node data ...\n"); char *string_node_data_msg = mkjson(MKJSON_OBJ, 2, @@ -116,13 +120,13 @@ int main(int argc, char* argv[]) if (err & PASS) { debug_print("Received PASS\n"); - int ret = pass_through(argv, end); + int ret = pass_through(argv, ex_index); return ret; } if (err & ERROR_DAEMON) { debug_print("Received ERROR_DAEMON\n"); - int ret = pass_through(argv, end); + int ret = pass_through(argv, ex_index); return ret; } @@ -232,14 +236,14 @@ int initialization(void* Requester) return 0; } -int pass_through(char **argv, int end) +int pass_through(char **argv, int ex_index) { - char *newargv[] = { NULL, NULL }; + char **newargv = NULL; pid_t child_pid; - newargv[0] = argv[end]; - if (end > 1) { - putenv(argv[end - 1]); + newargv = &argv[ex_index]; + if (ex_index > 1) { + putenv(argv[ex_index - 1]); } debug_print("pass-through invoked\n"); @@ -248,9 +252,9 @@ int pass_through(char **argv, int end) debug_print("fork() failed\n"); return -1; } else if (child_pid == 0) { - if (-1 == execv(argv[end], newargv)) { + if (-1 == execv(argv[ex_index], newargv)) { debug_print("pass_through execve failed %s: %s\n", - argv[end], strerror(errno)); + argv[ex_index], strerror(errno)); return -1; } } else if (child_pid > 0) { diff --git a/src/system/on-dhcp-event.sh b/src/system/on-dhcp-event.sh index a062dc810..49e53d7e1 100755 --- a/src/system/on-dhcp-event.sh +++ b/src/system/on-dhcp-event.sh @@ -21,21 +21,20 @@ client_mac=$4 domain=$5 hostsd_client="/usr/bin/vyos-hostsd-client" -if [ -z "$client_name" ]; then - logger -s -t on-dhcp-event "Client name was empty, using MAC \"$client_mac\" instead" - client_name=$(echo "client-"$client_mac | tr : -) -fi - -if [ "$domain" == "..YYZ!" ]; then - client_fqdn_name=$client_name - client_search_expr=$client_name -else - client_fqdn_name=$client_name.$domain - client_search_expr="$client_name\\.$domain" -fi - case "$action" in commit) # add mapping for new lease + if [ -z "$client_name" ]; then + logger -s -t on-dhcp-event "Client name was empty, using MAC \"$client_mac\" instead" + client_name=$(echo "client-"$client_mac | tr : -) + fi + + if [ "$domain" == "..YYZ!" ]; then + client_fqdn_name=$client_name + client_search_expr=$client_name + else + client_fqdn_name=$client_name.$domain + client_search_expr="$client_name\\.$domain" + fi $hostsd_client --add-hosts "$client_fqdn_name,$client_ip" --tag "dhcp-server-$client_ip" --apply exit 0 ;; diff --git a/src/systemd/dropbear@.service b/src/systemd/dropbear@.service index 606a7ea6d..acf926af9 100644 --- a/src/systemd/dropbear@.service +++ b/src/systemd/dropbear@.service @@ -4,11 +4,13 @@ Requires=dropbearkey.service Wants=conserver-server.service ConditionPathExists=/run/conserver/conserver.cf After=dropbearkey.service vyos-router.service conserver-server.service +StartLimitIntervalSec=0 [Service] Type=forking -ExecStartPre=/usr/bin/bash -c '/usr/bin/systemctl set-environment PORT=$(cli-shell-api returnActiveValue service console-server device "%I" ssh port)' -ExecStart=-/usr/sbin/dropbear -w -j -k -r /etc/dropbear/dropbear_rsa_host_key -c "/usr/bin/console %I" -P /run/conserver/dropbear.%I.pid -p ${PORT} -PIDFile=/run/conserver/dropbear.%I.pid +ExecStart=/usr/sbin/dropbear -w -j -k -r /etc/dropbear/dropbear_rsa_host_key -P /run/dropbear/dropbear.%I.pid -p %I +PIDFile=/run/dropbear/dropbear.%I.pid KillMode=process -Restart=on-failure +Restart=always +RestartSec=10 +RuntimeDirectoryPreserve=yes diff --git a/src/systemd/ndppd.service b/src/systemd/ndppd.service new file mode 100644 index 000000000..5790d37f1 --- /dev/null +++ b/src/systemd/ndppd.service @@ -0,0 +1,15 @@ +[Unit] +Description=NDP Proxy Daemon +After=vyos-router.service +ConditionPathExists=/run/ndppd/ndppd.conf +StartLimitIntervalSec=0 + +[Service] +Type=forking +ExecStart=/usr/sbin/ndppd -d -p /run/ndppd/ndppd.pid -c /run/ndppd/ndppd.conf +PIDFile=/run/ndppd/ndppd.pid +Restart=on-failure +RestartSec=20 + +[Install] +WantedBy=multi-user.target diff --git a/src/systemd/vyos-http-api.service b/src/systemd/vyos-http-api.service index 4fa68b4ff..ba5df5984 100644 --- a/src/systemd/vyos-http-api.service +++ b/src/systemd/vyos-http-api.service @@ -5,9 +5,8 @@ Requires=vyos-router.service [Service] ExecStartPre=/usr/libexec/vyos/init/vyos-config -ExecStart=/usr/bin/python3 -u /usr/libexec/vyos/services/vyos-http-api-server +ExecStart=/usr/libexec/vyos/services/vyos-http-api-server Type=idle -KillMode=process SyslogIdentifier=vyos-http-api SyslogFacility=daemon diff --git a/src/tests/test_dict_search.py b/src/tests/test_dict_search.py index 6a0fc74ad..991722f0f 100644 --- a/src/tests/test_dict_search.py +++ b/src/tests/test_dict_search.py @@ -20,6 +20,7 @@ from vyos.util import dict_search data = { 'string': 'fooo', 'nested': {'string': 'bar', 'empty': '', 'list': ['foo', 'bar']}, + 'non': {}, 'list': ['bar', 'baz'], 'dict': {'key_1': {}, 'key_2': 'vyos'} } @@ -30,7 +31,8 @@ class TestDictSearch(TestCase): def test_non_existing_keys(self): # TestDictSearch: Return False when querying for non-existent key - self.assertFalse(dict_search('non_existing', data)) + self.assertEqual(dict_search('non_existing', data), None) + self.assertEqual(dict_search('non.existing.fancy.key', data), None) def test_string(self): # TestDictSearch: Return value when querying string @@ -50,8 +52,14 @@ class TestDictSearch(TestCase): def test_nested_dict_key_empty(self): # TestDictSearch: Return False when querying for a nested string whose last key is empty + self.assertEqual(dict_search('nested.empty', data), '') self.assertFalse(dict_search('nested.empty', data)) def test_nested_list(self): # TestDictSearch: Return list items when querying nested list self.assertEqual(dict_search('nested.list', data), data['nested']['list']) + + def test_invalid_input(self): + # TestDictSearch: Return list items when querying nested list + self.assertEqual(dict_search('nested.list', None), None) + self.assertEqual(dict_search(None, data), None) diff --git a/src/tests/test_template.py b/src/tests/test_template.py index 544755692..7800d007f 100644 --- a/src/tests/test_template.py +++ b/src/tests/test_template.py @@ -93,3 +93,22 @@ class TestVyOSTemplate(TestCase): self.assertEqual(vyos.template.dec_ip('2001:db8::b/64', '10'), '2001:db8::1') self.assertEqual(vyos.template.dec_ip('2001:db8::f', '5'), '2001:db8::a') + def test_is_network(self): + self.assertFalse(vyos.template.is_ip_network('192.0.2.0')) + self.assertFalse(vyos.template.is_ip_network('192.0.2.1/24')) + self.assertTrue(vyos.template.is_ip_network('192.0.2.0/24')) + + self.assertFalse(vyos.template.is_ip_network('2001:db8::')) + self.assertFalse(vyos.template.is_ip_network('2001:db8::ffff')) + self.assertTrue(vyos.template.is_ip_network('2001:db8::/48')) + self.assertTrue(vyos.template.is_ip_network('2001:db8:1000::/64')) + + def test_is_network(self): + self.assertTrue(vyos.template.compare_netmask('10.0.0.0/8', '20.0.0.0/8')) + self.assertTrue(vyos.template.compare_netmask('10.0.0.0/16', '20.0.0.0/16')) + self.assertFalse(vyos.template.compare_netmask('10.0.0.0/8', '20.0.0.0/16')) + self.assertFalse(vyos.template.compare_netmask('10.0.0.1', '20.0.0.0/16')) + + self.assertTrue(vyos.template.compare_netmask('2001:db8:1000::/48', '2001:db8:2000::/48')) + self.assertTrue(vyos.template.compare_netmask('2001:db8:1000::/64', '2001:db8:2000::/64')) + self.assertFalse(vyos.template.compare_netmask('2001:db8:1000::/48', '2001:db8:2000::/64')) diff --git a/src/tests/test_util.py b/src/tests/test_util.py index f7405cbde..22bc085c5 100644 --- a/src/tests/test_util.py +++ b/src/tests/test_util.py @@ -17,11 +17,7 @@ from unittest import TestCase from vyos.util import mangle_dict_keys - class TestVyOSUtil(TestCase): - def setUp(self): - pass - def test_key_mangline(self): data = {"foo-bar": {"baz-quux": None}} expected_data = {"foo_bar": {"baz_quux": None}} diff --git a/src/validators/allowed-vlan b/src/validators/allowed-vlan new file mode 100755 index 000000000..11389390b --- /dev/null +++ b/src/validators/allowed-vlan @@ -0,0 +1,19 @@ +#! /usr/bin/python3 + +import sys +import re + +if __name__ == '__main__': + if len(sys.argv)>1: + allowed_vlan = sys.argv[1] + if re.search('[0-9]{1,4}-[0-9]{1,4}', allowed_vlan): + for tmp in allowed_vlan.split('-'): + if int(tmp) not in range(1, 4095): + sys.exit(1) + else: + if int(allowed_vlan) not in range(1, 4095): + sys.exit(1) + else: + sys.exit(2) + + sys.exit(0) diff --git a/src/validators/fqdn b/src/validators/fqdn index 347ffda42..a4027e4ca 100755 --- a/src/validators/fqdn +++ b/src/validators/fqdn @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -17,10 +17,7 @@ import re import sys - -# pattern copied from: https://www.regextester.com/103452 -pattern = "(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)" - +pattern = '[A-Za-z0-9][-.A-Za-z0-9]*' if __name__ == '__main__': if len(sys.argv) != 2: diff --git a/src/validators/interface-name b/src/validators/interface-name new file mode 100755 index 000000000..5bac671b1 --- /dev/null +++ b/src/validators/interface-name @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import re + +from sys import argv +from sys import exit + +pattern = '^(bond|br|dum|en|ersp|eth|gnv|lan|l2tp|l2tpeth|macsec|peth|ppp|pppoe|pptp|sstp|tun|vti|vtun|vxlan|wg|wlan|wlm)[0-9]+(.\d+)?|lo$' + +if __name__ == '__main__': + if len(argv) != 2: + exit(1) + interface = argv[1] + + if re.match(pattern, interface): + exit(0) + if os.path.exists(f'/sys/class/net/{interface}'): + exit(0) + exit(1) diff --git a/src/validators/ipv6-duid b/src/validators/ipv6-duid new file mode 100755 index 000000000..fd4728e50 --- /dev/null +++ b/src/validators/ipv6-duid @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 sys + +pattern = "^([0-9A-Fa-f]{2}:){,127}([0-9A-Fa-f]{2})$" + +if __name__ == '__main__': + if len(sys.argv) != 2: + sys.exit(1) + if not re.match(pattern, sys.argv[1]): + sys.exit(1) + sys.exit(0) diff --git a/src/validators/ipv6-eui64-prefix b/src/validators/ipv6-eui64-prefix new file mode 100755 index 000000000..d7f262633 --- /dev/null +++ b/src/validators/ipv6-eui64-prefix @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 + +# Validator used to check if given IPv6 prefix is of size /64 required by EUI64 + +from sys import argv +from sys import exit + +if __name__ == '__main__': + if len(argv) != 2: + exit(1) + + prefix = argv[1] + if prefix.split('/')[1] == '64': + exit(0) + + exit(1) diff --git a/src/validators/mac-address b/src/validators/mac-address index b2d3496f4..7d020f387 100755 --- a/src/validators/mac-address +++ b/src/validators/mac-address @@ -17,9 +17,7 @@ import re import sys - -pattern = "^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$" - +pattern = "^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$" if __name__ == '__main__': if len(sys.argv) != 2: diff --git a/vyos-configtest b/vyos-configtest deleted file mode 100644 index e69de29bb..000000000 --- a/vyos-configtest +++ /dev/null |