summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/configd-include.json1
-rw-r--r--data/templates/dhcp-client/ipv4.tmpl4
-rw-r--r--data/templates/frr/bgp.frr.tmpl7
-rw-r--r--data/templates/frr/vrf.frr.tmpl9
-rw-r--r--interface-definitions/include/bgp/bgp-common-config.xml.i1
-rw-r--r--interface-definitions/include/interface/dhcp-options.xml.i18
-rw-r--r--interface-definitions/include/isis/isis-redistribute-ipv4.xml.i18
-rw-r--r--interface-definitions/include/ospf/ospf-common-config.xml.i1
-rw-r--r--interface-definitions/include/route-map.xml.i14
-rw-r--r--interface-definitions/include/static/static-route-map.xml.i10
-rw-r--r--interface-definitions/include/vni.xml.i26
-rw-r--r--interface-definitions/interfaces-erspan.xml.in114
-rw-r--r--interface-definitions/interfaces-tunnel.xml.in81
-rw-r--r--interface-definitions/protocols-bgp.xml.in1
-rw-r--r--interface-definitions/protocols-isis.xml.in1
-rw-r--r--interface-definitions/protocols-ospf.xml.in1
-rw-r--r--interface-definitions/protocols-static.xml.in2
-rw-r--r--interface-definitions/vrf.xml.in1
-rw-r--r--python/vyos/configverify.py61
-rw-r--r--python/vyos/ifconfig/__init__.py2
-rwxr-xr-xpython/vyos/ifconfig/erspan.py170
-rw-r--r--python/vyos/ifconfig/tunnel.py12
-rw-r--r--python/vyos/template.py5
-rw-r--r--smoketest/configs/bgp-bfd-communities2
-rw-r--r--smoketest/configs/bgp-evpn-leaf1
-rw-r--r--smoketest/configs/tunnel-broker15
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py2
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_l2tpv3.py13
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_tunnel.py99
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py19
-rwxr-xr-xsrc/completion/list_bgp_neighbors.sh3
-rwxr-xr-xsrc/completion/list_bgp_peer_groups.sh3
-rwxr-xr-xsrc/conf_mode/interfaces-erspan.py108
-rwxr-xr-xsrc/conf_mode/interfaces-tunnel.py30
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py37
-rwxr-xr-xsrc/conf_mode/protocols_isis.py19
-rwxr-xr-xsrc/conf_mode/protocols_ospf.py19
-rwxr-xr-xsrc/conf_mode/protocols_ospfv3.py14
-rwxr-xr-xsrc/conf_mode/protocols_rip.py32
-rwxr-xr-xsrc/conf_mode/protocols_ripng.py30
-rwxr-xr-xsrc/conf_mode/protocols_static.py4
-rwxr-xr-xsrc/conf_mode/vrf.py21
-rw-r--r--src/tests/test_template.py10
43 files changed, 462 insertions, 579 deletions
diff --git a/data/configd-include.json b/data/configd-include.json
index eed858363..4959e5020 100644
--- a/data/configd-include.json
+++ b/data/configd-include.json
@@ -21,7 +21,6 @@
"interfaces-pppoe.py",
"interfaces-pseudo-ethernet.py",
"interfaces-tunnel.py",
-"interfaces-erspan.py",
"interfaces-vxlan.py",
"interfaces-wireguard.py",
"interfaces-wireless.py",
diff --git a/data/templates/dhcp-client/ipv4.tmpl b/data/templates/dhcp-client/ipv4.tmpl
index 71b429db6..c934b7cdb 100644
--- a/data/templates/dhcp-client/ipv4.tmpl
+++ b/data/templates/dhcp-client/ipv4.tmpl
@@ -20,5 +20,9 @@ interface "{{ ifname }}" {
# The require statement lists options that must be sent in order for an offer to be
# accepted. Offers that do not contain all the listed options will be ignored!
require subnet-mask;
+{% if dhcp_options.reject is defined and dhcp_options.reject is not none %}
+ # Block addresses coming from theses dhcp servers if configured.
+ reject {{ dhcp_options.reject | join(', ') }};
+{% endif %}
}
diff --git a/data/templates/frr/bgp.frr.tmpl b/data/templates/frr/bgp.frr.tmpl
index 53e62928b..cbf83e3fd 100644
--- a/data/templates/frr/bgp.frr.tmpl
+++ b/data/templates/frr/bgp.frr.tmpl
@@ -191,6 +191,10 @@ router bgp {{ local_as }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none
{% else %}
no bgp ebgp-requires-policy
{% endif %}
+{% if parameters is defined and parameters.default is defined and parameters.default.no_ipv4_unicast is defined %}
+{# Option must be set before any neighbor - see https://phabricator.vyos.net/T3463 #}
+ no bgp default ipv4-unicast
+{% 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 %}
@@ -359,9 +363,6 @@ router bgp {{ local_as }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none
{% if parameters.default.local_pref is defined and parameters.default.local_pref is not none %}
bgp default local-preference {{ parameters.default.local_pref }}
{% endif %}
-{% if parameters.default.no_ipv4_unicast is defined %}
- no bgp default ipv4-unicast
-{% endif %}
{% endif %}
{% if parameters.deterministic_med is defined %}
bgp deterministic-med
diff --git a/data/templates/frr/vrf.frr.tmpl b/data/templates/frr/vrf.frr.tmpl
new file mode 100644
index 000000000..299c9719e
--- /dev/null
+++ b/data/templates/frr/vrf.frr.tmpl
@@ -0,0 +1,9 @@
+{% if name is defined and name is not none %}
+{% for vrf, vrf_config in name.items() %}
+vrf {{ vrf }}
+{% if vrf_config.vni is defined and vrf_config.vni is not none %}
+ vni {{ vrf_config.vni }}
+{% endif %}
+ exit-vrf
+{% endfor %}
+{% endif %}
diff --git a/interface-definitions/include/bgp/bgp-common-config.xml.i b/interface-definitions/include/bgp/bgp-common-config.xml.i
index c89e2288e..14070e006 100644
--- a/interface-definitions/include/bgp/bgp-common-config.xml.i
+++ b/interface-definitions/include/bgp/bgp-common-config.xml.i
@@ -824,7 +824,6 @@
#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>
diff --git a/interface-definitions/include/interface/dhcp-options.xml.i b/interface-definitions/include/interface/dhcp-options.xml.i
index bd327da2d..b65b0802a 100644
--- a/interface-definitions/include/interface/dhcp-options.xml.i
+++ b/interface-definitions/include/interface/dhcp-options.xml.i
@@ -37,6 +37,24 @@
</constraint>
</properties>
</leafNode>
+ <leafNode name="reject">
+ <properties>
+ <help>IP addresses or subnets from which to reject DHCP leases</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address to match</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 prefix to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv4-prefix"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
</children>
</node>
<!-- include end -->
diff --git a/interface-definitions/include/isis/isis-redistribute-ipv4.xml.i b/interface-definitions/include/isis/isis-redistribute-ipv4.xml.i
index df48b4d28..15c8c0b0b 100644
--- a/interface-definitions/include/isis/isis-redistribute-ipv4.xml.i
+++ b/interface-definitions/include/isis/isis-redistribute-ipv4.xml.i
@@ -16,14 +16,7 @@
</constraint>
</properties>
</leafNode>
- <leafNode name="route-map">
- <properties>
- <help>Route map reference</help>
- <completionHelp>
- <path>policy route-map</path>
- </completionHelp>
- </properties>
- </leafNode>
+ #include <include/route-map.xml.i>
</children>
</node>
<node name="level-2">
@@ -43,14 +36,7 @@
</constraint>
</properties>
</leafNode>
- <leafNode name="route-map">
- <properties>
- <help>Route map reference</help>
- <completionHelp>
- <path>policy route-map</path>
- </completionHelp>
- </properties>
- </leafNode>
+ #include <include/route-map.xml.i>
</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
index 7316af670..a01d1c890 100644
--- a/interface-definitions/include/ospf/ospf-common-config.xml.i
+++ b/interface-definitions/include/ospf/ospf-common-config.xml.i
@@ -697,7 +697,6 @@
</leafNode>
</children>
</node>
-#include <include/route-map.xml.i>
<node name="timers">
<properties>
<help>Adjust routing timers</help>
diff --git a/interface-definitions/include/route-map.xml.i b/interface-definitions/include/route-map.xml.i
index 5a1c137b9..edbe76892 100644
--- a/interface-definitions/include/route-map.xml.i
+++ b/interface-definitions/include/route-map.xml.i
@@ -1,14 +1,18 @@
<!-- 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>
+ <help>Specify route-map name to use</help>
<completionHelp>
<path>policy route-map</path>
</completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Route map name</description>
+ </valueHelp>
+ <constraint>
+ <regex>^[-a-zA-Z0-9.]+$</regex>
+ </constraint>
+ <constraintErrorMessage>Route-map name can only contain alpha-numeric letters and a hyphen</constraintErrorMessage>
</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
deleted file mode 100644
index af825e043..000000000
--- a/interface-definitions/include/static/static-route-map.xml.i
+++ /dev/null
@@ -1,10 +0,0 @@
-<!-- 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/vni.xml.i b/interface-definitions/include/vni.xml.i
index faff4c3c3..be45c0c97 100644
--- a/interface-definitions/include/vni.xml.i
+++ b/interface-definitions/include/vni.xml.i
@@ -1,12 +1,14 @@
- <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 start from vni.xml.i -->
+<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 end -->
diff --git a/interface-definitions/interfaces-erspan.xml.in b/interface-definitions/interfaces-erspan.xml.in
deleted file mode 100644
index 769899415..000000000
--- a/interface-definitions/interfaces-erspan.xml.in
+++ /dev/null
@@ -1,114 +0,0 @@
-<?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-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in
index e3aad2719..536edcb99 100644
--- a/interface-definitions/interfaces-tunnel.xml.in
+++ b/interface-definitions/interfaces-tunnel.xml.in
@@ -80,9 +80,13 @@
<properties>
<help>Encapsulation of this tunnel interface</help>
<completionHelp>
- <list>gre gretap ip6gre ip6gretap ip6ip6 ipip ipip6 sit</list>
+ <list>erspan gre gretap ip6erspan ip6gre ip6gretap ip6ip6 ipip ipip6 sit</list>
</completionHelp>
<valueHelp>
+ <format>erspan</format>
+ <description>Encapsulated Remote Switched Port Analyzer</description>
+ </valueHelp>
+ <valueHelp>
<format>gre</format>
<description>Generic Routing Encapsulation</description>
</valueHelp>
@@ -91,6 +95,10 @@
<description>Generic Routing Encapsulation (virtual L2 tunnel)</description>
</valueHelp>
<valueHelp>
+ <format>ip6erspan</format>
+ <description>Encapsulated Remote Switched Port Analyzer over IPv6 network</description>
+ </valueHelp>
+ <valueHelp>
<format>ip6gre</format>
<description>GRE over IPv6 network</description>
</valueHelp>
@@ -115,9 +123,9 @@
<description>Simple Internet Transition encapsulation</description>
</valueHelp>
<constraint>
- <regex>^(gre|gretap|ip6gre|ip6gretap|ip6ip6|ipip|ipip6|sit)$</regex>
+ <regex>^(erspan|gre|gretap|ip6erspan|ip6gre|ip6gretap|ip6ip6|ipip|ipip6|sit)$</regex>
</constraint>
- <constraintErrorMessage>Invalid encapsulation, must be one of: gre, gretap, ip6gre, ip6gretap, ipip, sit, ipip6 or ip6ip6</constraintErrorMessage>
+ <constraintErrorMessage>Invalid encapsulation, must be one of: erspan, gre, gretap, ip6erspan, ip6gre, ip6gretap, ipip, sit, ipip6 or ip6ip6</constraintErrorMessage>
</properties>
</leafNode>
<leafNode name="multicast">
@@ -145,6 +153,73 @@
<help>Tunnel parameters</help>
</properties>
<children>
+ <node name="erspan">
+ <properties>
+ <help>ERSPAN Tunnel parameters</help>
+ </properties>
+ <children>
+ <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>
+ </properties>
+ </leafNode>
+ <leafNode name="hw-id">
+ <properties>
+ <help>Unique identifier of ERSPAN engine within a system</help>
+ <valueHelp>
+ <format>0-1048575</format>
+ <description>Unique identifier of ERSPAN engine</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-1048575"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="index">
+ <properties>
+ <help>Specifify ERSPAN version 1 index field</help>
+ <valueHelp>
+ <format>0-63</format>
+ <description>Platform-depedent field for specifying port number and direction</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-63"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="version">
+ <properties>
+ <help>Protocol version</help>
+ <valueHelp>
+ <format>1</format>
+ <description>ERSPAN Type II</description>
+ </valueHelp>
+ <valueHelp>
+ <format>2</format>
+ <description>ERSPAN Type III</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-2"/>
+ </constraint>
+ </properties>
+ <defaultValue>1</defaultValue>
+ </leafNode>
+ </children>
+ </node>
<node name="ip">
<properties>
<help>IPv4 specific tunnel parameters</help>
diff --git a/interface-definitions/protocols-bgp.xml.in b/interface-definitions/protocols-bgp.xml.in
index d610f8dff..f4ebddb42 100644
--- a/interface-definitions/protocols-bgp.xml.in
+++ b/interface-definitions/protocols-bgp.xml.in
@@ -9,6 +9,7 @@
</properties>
<children>
#include <include/bgp/bgp-common-config.xml.i>
+ #include <include/route-map.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/protocols-isis.xml.in b/interface-definitions/protocols-isis.xml.in
index 1bc890446..42d5049cc 100644
--- a/interface-definitions/protocols-isis.xml.in
+++ b/interface-definitions/protocols-isis.xml.in
@@ -9,6 +9,7 @@
</properties>
<children>
#include <include/isis/isis-common-config.xml.i>
+ #include <include/route-map.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/protocols-ospf.xml.in b/interface-definitions/protocols-ospf.xml.in
index d9c3325ec..4431a1772 100644
--- a/interface-definitions/protocols-ospf.xml.in
+++ b/interface-definitions/protocols-ospf.xml.in
@@ -9,6 +9,7 @@
</properties>
<children>
#include <include/ospf/ospf-common-config.xml.i>
+ #include <include/route-map.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/protocols-static.xml.in b/interface-definitions/protocols-static.xml.in
index baf13777a..3cc28e296 100644
--- a/interface-definitions/protocols-static.xml.in
+++ b/interface-definitions/protocols-static.xml.in
@@ -11,7 +11,7 @@
<priority>480</priority>
</properties>
<children>
- #include <include/static/static-route-map.xml.i>
+ #include <include/route-map.xml.i>
#include <include/static/static-route.xml.i>
#include <include/static/static-route6.xml.i>
<tagNode name="table">
diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in
index 8a56b1bc0..a1ef45868 100644
--- a/interface-definitions/vrf.xml.in
+++ b/interface-definitions/vrf.xml.in
@@ -85,6 +85,7 @@
<constraintErrorMessage>VRF routing table must be in range from 100 to 2147483647</constraintErrorMessage>
</properties>
</leafNode>
+ #include <include/vni.xml.i>
</children>
</tagNode>
</children>
diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py
index 718b7445d..99c472582 100644
--- a/python/vyos/configverify.py
+++ b/python/vyos/configverify.py
@@ -337,18 +337,16 @@ def verify_accel_ppp_base_service(config):
def verify_diffie_hellman_length(file, min_keysize):
""" Verify Diffie-Hellamn keypair length given via file. It must be greater
then or equal to min_keysize """
+ import os
+ import re
+ from vyos.util import cmd
try:
keysize = str(min_keysize)
except:
return False
- import os
- import re
- from vyos.util import cmd
-
if os.path.exists(file):
-
out = cmd(f'openssl dhparam -inform PEM -in {file} -text')
prog = re.compile('\d+\s+bit')
if prog.search(out):
@@ -358,26 +356,55 @@ def verify_diffie_hellman_length(file, min_keysize):
return False
-def verify_route_maps(config):
+def verify_common_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']
+ # XXX: This function is called in combination with a previous call to:
+ # tmp = conf.get_config_dict(['policy']) - see protocols_ospf.py as example.
+ # We should NOT call this with the key_mangling option as this would rename
+ # route-map hypens '-' to underscores '_' and one could no longer distinguish
+ # what should have been the "proper" route-map name, as foo-bar and foo_bar
+ # are two entire different route-map instances!
+ for route_map in ['route-map', 'route_map']:
+ if route_map not in config:
+ continue
+ tmp = 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 dict_search(f'policy.route-map.{tmp}', config) == None:
+ raise ConfigError(f'Specified route-map "{tmp}" 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!')
+ verify_route_map(protocol_config['route_map'], config)
+
+def verify_route_map(route_map_name, config):
+ """
+ Common helper function used by routing protocol implementations to perform
+ recurring validation if a specified route-map exists!
+ """
+ # Check if the specified route-map exists, if not error out
+ if dict_search(f'policy.route-map.{route_map_name}', config) == None:
+ raise ConfigError(f'Specified route-map "{route_map_name}" does not exist!')
+
+def verify_prefix_list(prefix_list, config, version=''):
+ """
+ Common helper function used by routing protocol implementations to perform
+ recurring validation if a specified prefix-list exists!
+ """
+ # Check if the specified prefix-list exists, if not error out
+ if dict_search(f'policy.prefix-list{version}.{prefix_list}', config) == None:
+ raise ConfigError(f'Specified prefix-list{version} "{prefix_list}" does not exist!')
+
+def verify_access_list(access_list, config, version=''):
+ """
+ Common helper function used by routing protocol implementations to perform
+ recurring validation if a specified prefix-list exists!
+ """
+ # Check if the specified ACL exists, if not error out
+ if dict_search(f'policy.access-list{version}.{access_list}', config) == None:
+ raise ConfigError(f'Specified access-list{version} "{access_list}" does not exist!')
diff --git a/python/vyos/ifconfig/__init__.py b/python/vyos/ifconfig/__init__.py
index f5dfa8e05..e9da1e9f5 100644
--- a/python/vyos/ifconfig/__init__.py
+++ b/python/vyos/ifconfig/__init__.py
@@ -32,8 +32,6 @@ from vyos.ifconfig.vtun import VTunIf
from vyos.ifconfig.vti import VTIIf
from vyos.ifconfig.pppoe import PPPoEIf
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/erspan.py b/python/vyos/ifconfig/erspan.py
deleted file mode 100755
index 03b2acdbf..000000000
--- a/python/vyos/ifconfig/erspan.py
+++ /dev/null
@@ -1,170 +0,0 @@
-# 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/tunnel.py b/python/vyos/ifconfig/tunnel.py
index e5e1300b2..08854a3b0 100644
--- a/python/vyos/ifconfig/tunnel.py
+++ b/python/vyos/ifconfig/tunnel.py
@@ -63,6 +63,10 @@ class TunnelIf(Interface):
'parameters.ip.no_pmtu_discovery' : 'nopmtudisc',
'parameters.ip.tos' : 'tos',
'parameters.ip.ttl' : 'ttl',
+ 'parameters.erspan.direction' : 'erspan_dir',
+ 'parameters.erspan.hw_id' : 'erspan_hwid',
+ 'parameters.erspan.index' : 'erspan',
+ 'parameters.erspan.version' : 'erspan_ver',
}
mapping_ipv6 = {
'parameters.ipv6.encaplimit' : 'encaplimit',
@@ -113,8 +117,12 @@ class TunnelIf(Interface):
mapping = { **self.mapping, **self.mapping_ipv4 }
cmd = 'ip tunnel add {ifname} mode {encapsulation}'
- if self.iftype in ['gretap', 'ip6gretap']:
+ if self.iftype in ['gretap', 'ip6gretap', 'erspan', 'ip6erspan']:
cmd = 'ip link add name {ifname} type {encapsulation}'
+ # ERSPAN requires the serialisation of packets
+ if self.iftype in ['erspan', 'ip6erspan']:
+ cmd += ' seq'
+
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
@@ -131,7 +139,7 @@ class TunnelIf(Interface):
def _change_options(self):
# gretap interfaces do not support changing any parameter
- if self.iftype in ['gretap', 'ip6gretap']:
+ if self.iftype in ['gretap', 'ip6gretap', 'erspan', 'ip6erspan']:
return
if self.config['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre']:
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 85e4d12b3..7810f5edd 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -207,6 +207,11 @@ def network_from_ipv4(address):
cidr_prefix = ip_interface(f'{address}/{netmask}').network
return address_from_cidr(cidr_prefix)
+@register_filter('is_interface')
+def is_interface(interface):
+ """ Check if parameter is a valid local interface name """
+ return os.path.exists(f'/sys/class/net/{interface}')
+
@register_filter('is_ip')
def is_ip(addr):
""" Check addr if it is an IPv4 or IPv6 address """
diff --git a/smoketest/configs/bgp-bfd-communities b/smoketest/configs/bgp-bfd-communities
index 3b3056a51..1a331f9ff 100644
--- a/smoketest/configs/bgp-bfd-communities
+++ b/smoketest/configs/bgp-bfd-communities
@@ -421,8 +421,6 @@ protocols {
local 220
}
}
- graceful-restart {
- }
}
peer-group DAL13 {
address-family {
diff --git a/smoketest/configs/bgp-evpn-leaf b/smoketest/configs/bgp-evpn-leaf
index 73d658de8..020490186 100644
--- a/smoketest/configs/bgp-evpn-leaf
+++ b/smoketest/configs/bgp-evpn-leaf
@@ -29,6 +29,7 @@ interfaces {
loopback lo {
}
vxlan vxlan100 {
+ mtu 1500
parameters {
nolearning
}
diff --git a/smoketest/configs/tunnel-broker b/smoketest/configs/tunnel-broker
index b52ba2541..d4a5c2dfc 100644
--- a/smoketest/configs/tunnel-broker
+++ b/smoketest/configs/tunnel-broker
@@ -21,40 +21,34 @@ interfaces {
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
+ remote-ip 172.18.202.110
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
+ remote-ip 172.18.202.120
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
+ remote-ip 172.18.202.130
session-id 130
source-port 5030
tunnel-id 30
@@ -93,8 +87,7 @@ interfaces {
protocols {
static {
route 0.0.0.0/0 {
- next-hop 172.18.202.1 {
- distance 10
+ next-hop 172.18.202.254 {
}
}
}
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index f897088ef..bc95c78b1 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -437,7 +437,7 @@ 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):
+ def test_dhcpv6_client_options(self):
if not self._test_ipv6_dhcpc6:
self.skipTest('not supported')
diff --git a/smoketest/scripts/cli/test_interfaces_l2tpv3.py b/smoketest/scripts/cli/test_interfaces_l2tpv3.py
index 24cb9464e..06ced5c40 100755
--- a/smoketest/scripts/cli/test_interfaces_l2tpv3.py
+++ b/smoketest/scripts/cli/test_interfaces_l2tpv3.py
@@ -14,13 +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 os
import json
import unittest
from base_interfaces_test import BasicInterfaceTest
from vyos.util import cmd
-class GeneveInterfaceTest(BasicInterfaceTest.TestCase):
+class L2TPv3InterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
def setUpClass(cls):
cls._test_ip = True
@@ -53,9 +54,17 @@ class GeneveInterfaceTest(BasicInterfaceTest.TestCase):
for opt in self._options[interface]:
dict.update({opt.split()[0].replace('-','_'): opt.split()[1]})
- for key in ['peer_session_id', 'peer_tunnel_id', 'session_id', 'tunnel_id']:
+ for key in ['peer_session_id', 'peer_tunnel_id',
+ 'session_id', 'tunnel_id']:
self.assertEqual(str(config[key]), dict[key])
if __name__ == '__main__':
+ # when re-running this test, cleanup loaded modules first so they are
+ # reloaded on demand - not needed but test more and more features
+ for module in ['l2tp_ip6', 'l2tp_ip', 'l2tp_eth', 'l2tp_eth',
+ 'l2tp_netlink', 'l2tp_core']:
+ if os.path.exists(f'/sys/module/{module}'):
+ cmd(f'sudo rmmod {module}')
+
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_tunnel.py b/smoketest/scripts/cli/test_interfaces_tunnel.py
index 6af31ddff..ebb0158dc 100755
--- a/smoketest/scripts/cli/test_interfaces_tunnel.py
+++ b/smoketest/scripts/cli/test_interfaces_tunnel.py
@@ -232,5 +232,104 @@ class TunnelInterfaceTest(BasicInterfaceTest.TestCase):
conf = get_interface_config(interface)
self.assertEqual(new_remote, conf['linkinfo']['info_data']['remote'])
+ def test_erspan_v1(self):
+ interface = f'tun1070'
+ encapsulation = 'erspan'
+ ip_key = '77'
+ idx = '20'
+
+ 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])
+
+ self.cli_set(self._base_path + [interface, 'parameters', 'erspan', 'index', idx])
+
+ # ERSPAN requires ip key parameter
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(self._base_path + [interface, 'parameters', 'ip', 'key', ip_key])
+
+ # 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(0, conf['linkinfo']['info_data']['ttl'])
+ self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['ikey'])
+ self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['okey'])
+ self.assertEqual(int(idx), conf['linkinfo']['info_data']['erspan_index'])
+ # version defaults to 1
+ self.assertEqual(1, conf['linkinfo']['info_data']['erspan_ver'])
+ self.assertTrue( conf['linkinfo']['info_data']['iseq'])
+ self.assertTrue( conf['linkinfo']['info_data']['oseq'])
+
+ # 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'])
+
+ def test_ip6erspan_v2(self):
+ interface = f'tun1070'
+ encapsulation = 'ip6erspan'
+ ip_key = '77'
+ erspan_ver = 2
+ direction = 'ingress'
+
+ 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])
+
+ # ERSPAN requires ip key parameter
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(self._base_path + [interface, 'parameters', 'ip', 'key', ip_key])
+
+ self.cli_set(self._base_path + [interface, 'parameters', 'erspan', 'version', str(erspan_ver)])
+
+ # ERSPAN index is not valid on version 2
+ self.cli_set(self._base_path + [interface, 'parameters', 'erspan', 'index', '10'])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(self._base_path + [interface, 'parameters', 'erspan', 'index'])
+
+ # ERSPAN requires direction to be set
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(self._base_path + [interface, 'parameters', 'erspan', 'direction', direction])
+
+ # 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_v6, conf['linkinfo']['info_data']['local'])
+ self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote'])
+ self.assertEqual(0, conf['linkinfo']['info_data']['ttl'])
+ self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['ikey'])
+ self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['okey'])
+ self.assertEqual(erspan_ver, conf['linkinfo']['info_data']['erspan_ver'])
+ self.assertEqual(direction, conf['linkinfo']['info_data']['erspan_dir'])
+ self.assertTrue( conf['linkinfo']['info_data']['iseq'])
+ self.assertTrue( conf['linkinfo']['info_data']['oseq'])
+
+ # Change remote ip address (inc host by 2
+ new_remote = inc_ip(remote_ip6, 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_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index 4f39948c0..40afca615 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -102,8 +102,7 @@ peer_group_config = {
'password' : 'VyOS-Secure123',
'shutdown' : '',
'cap_over' : '',
-# XXX: not available in current Perl backend
-# 'ttl_security': '5',
+ 'ttl_security': '5',
},
'bar' : {
'description' : 'foo peer bar group',
@@ -127,7 +126,6 @@ peer_group_config = {
},
}
-
class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
def setUp(self):
self.cli_set(['policy', 'route-map', route_map_in, 'rule', '10', 'action', 'permit'])
@@ -142,6 +140,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
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'])
+ self.cli_set(base_path + ['local-as', ASN])
+
def tearDown(self):
self.cli_delete(['policy', 'route-map', route_map_in])
self.cli_delete(['policy', 'route-map', route_map_out])
@@ -214,7 +214,9 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
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
+ # Local AS number MUST be defined - as this is set in setUp() we remove
+ # this once for testing of the proper error
+ self.cli_delete(base_path + ['local-as'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['local-as', ASN])
@@ -257,7 +259,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
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 peer, peer_config in neighbor_config.items():
@@ -332,7 +333,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
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:
@@ -403,8 +403,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
},
}
- self.cli_set(base_path + ['local-as', ASN])
-
# We want to redistribute ...
redistributes = ['connected', 'isis', 'kernel', 'ospf', 'rip', 'static']
for redistribute in redistributes:
@@ -451,8 +449,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
},
}
- self.cli_set(base_path + ['local-as', ASN])
-
# We want to redistribute ...
redistributes = ['connected', 'kernel', 'ospfv3', 'ripng', 'static']
for redistribute in redistributes:
@@ -495,7 +491,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
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:
@@ -527,8 +522,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
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'])
diff --git a/src/completion/list_bgp_neighbors.sh b/src/completion/list_bgp_neighbors.sh
index 77c626452..f74f102ef 100755
--- a/src/completion/list_bgp_neighbors.sh
+++ b/src/completion/list_bgp_neighbors.sh
@@ -30,8 +30,7 @@ while [[ "$#" -gt 0 ]]; do
done
declare -a vals
-eval "bgp_as=$(cli-shell-api listActiveNodes protocols bgp)"
-eval "vals=($(cli-shell-api listActiveNodes protocols bgp $bgp_as neighbor))"
+eval "vals=($(cli-shell-api listActiveNodes protocols bgp neighbor))"
if [ $ipv4 -eq 1 ] && [ $ipv6 -eq 1 ]; then
echo -n '<x.x.x.x>' '<h:h:h:h:h:h:h:h>' ${vals[@]}
diff --git a/src/completion/list_bgp_peer_groups.sh b/src/completion/list_bgp_peer_groups.sh
index 4503d608f..1684271f8 100755
--- a/src/completion/list_bgp_peer_groups.sh
+++ b/src/completion/list_bgp_peer_groups.sh
@@ -16,8 +16,7 @@
# 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))"
+eval "vals=($(cli-shell-api listNodes protocols bgp peer-group))"
echo -n ${vals[@]}
exit 0
diff --git a/src/conf_mode/interfaces-erspan.py b/src/conf_mode/interfaces-erspan.py
deleted file mode 100755
index 97ae3cf55..000000000
--- a/src/conf_mode/interfaces-erspan.py
+++ /dev/null
@@ -1,108 +0,0 @@
-#!/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-tunnel.py b/src/conf_mode/interfaces-tunnel.py
index cab94a5b0..4e6c8a9ab 100755
--- a/src/conf_mode/interfaces-tunnel.py
+++ b/src/conf_mode/interfaces-tunnel.py
@@ -61,6 +61,9 @@ def get_config(config=None):
nhrp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True)
if nhrp: tunnel.update({'nhrp' : list(nhrp.keys())})
+ if 'encapsulation' in tunnel and tunnel['encapsulation'] not in ['erspan', 'ip6erspan']:
+ del tunnel['parameters']['erspan']
+
return tunnel
def verify(tunnel):
@@ -72,14 +75,28 @@ def verify(tunnel):
return None
- if 'encapsulation' not in tunnel:
- error = 'Must configure encapsulation for "{ifname}"!'
- raise ConfigError(error.format(**tunnel))
+ verify_tunnel(tunnel)
+
+ if tunnel['encapsulation'] in ['erspan', 'ip6erspan']:
+ if dict_search('parameters.ip.key', tunnel) == None:
+ raise ConfigError('ERSPAN requires ip key parameter!')
+
+ # this is a default field
+ ver = int(tunnel['parameters']['erspan']['version'])
+ if ver == 1:
+ if 'hw_id' in tunnel['parameters']['erspan']:
+ raise ConfigError('ERSPAN version 1 does not support hw-id!')
+ if 'direction' in tunnel['parameters']['erspan']:
+ raise ConfigError('ERSPAN version 1 does not support direction!')
+ elif ver == 2:
+ if 'idx' in tunnel['parameters']['erspan']:
+ raise ConfigError('ERSPAN version 2 does not index parameter!')
+ if 'direction' not in tunnel['parameters']['erspan']:
+ raise ConfigError('ERSPAN version 2 requires direction to be set!')
verify_mtu_ipv6(tunnel)
verify_address(tunnel)
verify_vrf(tunnel)
- verify_tunnel(tunnel)
if 'source_interface' in tunnel:
verify_interface_exists(tunnel['source_interface'])
@@ -92,7 +109,6 @@ def verify(tunnel):
if tunnel['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre']:
raise ConfigError('Can not disable PMTU discovery for given encapsulation')
-
def generate(tunnel):
return None
@@ -108,8 +124,8 @@ def apply(tunnel):
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 ('deleted' in tunnel or 'encapsulation_changed' in tunnel or encap in
+ ['gretap', 'ip6gretap', 'erspan', 'ip6erspan'] or remote in ['any']):
if interface in interfaces():
tmp = Interface(interface)
tmp.remove()
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index 73cfa9b83..a76aec30b 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -21,7 +21,10 @@ from sys import argv
from vyos.config import Config
from vyos.configdict import dict_merge
+from vyos.configverify import verify_prefix_list
+from vyos.configverify import verify_route_map
from vyos.template import is_ip
+from vyos.template import is_interface
from vyos.template import render_to_string
from vyos.util import call
from vyos.util import dict_search
@@ -58,11 +61,13 @@ def get_config(config=None):
bgp.update({'deleted' : ''})
return 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
+ # We also need some additional information from the config, prefix-lists
+ # and route-maps for instance. They will be used in verify().
+ #
+ # XXX: one MUST always call this without the key_mangling() option! See
+ # vyos.configverify.verify_common_route_maps() for more information.
+ tmp = conf.get_config_dict(['policy'])
+ # Merge policy dict into "regular" config dict
bgp = dict_merge(tmp, bgp)
return bgp
@@ -128,7 +133,12 @@ def verify(bgp):
# 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}"')
+ raise ConfigError(f'Can not configure a local address as neighbor "{peer}"')
+ elif is_interface(peer):
+ if 'peer_group' in peer_config:
+ raise ConfigError(f'peer-group must be set under the interface node of "{peer}"')
+ if 'remote_as' in peer_config:
+ raise ConfigError(f'remote-as must be set under the interface node of "{peer}"')
for afi in ['ipv4_unicast', 'ipv6_unicast', 'l2vpn_evpn']:
# Bail out early if address family is not configured
@@ -142,24 +152,15 @@ def verify(bgp):
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!')
+ verify_prefix_list(afi_config['prefix_list'][tmp], bgp)
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!')
+ verify_prefix_list(afi_config['prefix_list'][tmp], bgp, version='6')
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!')
+ verify_route_map(afi_config['route_map'][tmp], bgp)
if 'route_reflector_client' in afi_config:
if 'remote_as' in peer_config and bgp['local_as'] != peer_config['remote_as']:
diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py
index 571520cfe..8b75f9873 100755
--- a/src/conf_mode/protocols_isis.py
+++ b/src/conf_mode/protocols_isis.py
@@ -22,6 +22,7 @@ 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_common_route_maps
from vyos.configverify import verify_interface_exists
from vyos.util import call
from vyos.util import dict_search
@@ -70,10 +71,12 @@ def get_config(config=None):
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
+ # and route-maps for instance. They will be used in verify().
+ #
+ # XXX: one MUST always call this without the key_mangling() option! See
+ # vyos.configverify.verify_common_route_maps() for more information.
+ tmp = conf.get_config_dict(['policy'])
+ # Merge policy dict into "regular" config dict
isis = dict_merge(tmp, isis)
return isis
@@ -91,6 +94,8 @@ def verify(isis):
if int(tmp[-1]) != 0:
raise ConfigError('Last byte of IS-IS network entity title must always be 0!')
+ verify_common_route_maps(isis)
+
# If interface not set
if 'interface' not in isis:
raise ConfigError('Interface used for routing updates is mandatory!')
@@ -141,12 +146,6 @@ def verify(isis):
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)
diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py
index 30cc33dcf..a97d5b5ed 100755
--- a/src/conf_mode/protocols_ospf.py
+++ b/src/conf_mode/protocols_ospf.py
@@ -22,7 +22,8 @@ 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_common_route_maps
+from vyos.configverify import verify_route_map
from vyos.configverify import verify_interface_exists
from vyos.template import render_to_string
from vyos.util import call
@@ -130,10 +131,12 @@ def get_config(config=None):
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
+ # and route-maps for instance. They will be used in verify().
+ #
+ # XXX: one MUST always call this without the key_mangling() option! See
+ # vyos.configverify.verify_common_route_maps() for more information.
+ tmp = conf.get_config_dict(['policy'])
+ # Merge policy dict into "regular" config dict
ospf = dict_merge(tmp, ospf)
return ospf
@@ -142,7 +145,11 @@ def verify(ospf):
if not ospf:
return None
- verify_route_maps(ospf)
+ verify_common_route_maps(ospf)
+
+ # As we can have a default-information route-map, we need to validate it!
+ route_map_name = dict_search('default_information.originate.route_map', ospf)
+ if route_map_name: verify_route_map(route_map_name, ospf)
if 'interface' in ospf:
for interface in ospf['interface']:
diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py
index 42b6462e3..4ab7b65a3 100755
--- a/src/conf_mode/protocols_ospfv3.py
+++ b/src/conf_mode/protocols_ospfv3.py
@@ -20,7 +20,7 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
-from vyos.configverify import verify_route_maps
+from vyos.configverify import verify_common_route_maps
from vyos.template import render_to_string
from vyos.util import call
from vyos.ifconfig import Interface
@@ -45,10 +45,12 @@ def get_config(config=None):
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
+ # and route-maps for instance. They will be used in verify().
+ #
+ # XXX: one MUST always call this without the key_mangling() option! See
+ # vyos.configverify.verify_common_route_maps() for more information.
+ tmp = conf.get_config_dict(['policy'])
+ # Merge policy dict into "regular" config dict
ospfv3 = dict_merge(tmp, ospfv3)
return ospfv3
@@ -57,7 +59,7 @@ def verify(ospfv3):
if not ospfv3:
return None
- verify_route_maps(ospfv3)
+ verify_common_route_maps(ospfv3)
if 'interface' in ospfv3:
for ifname, if_config in ospfv3['interface'].items():
diff --git a/src/conf_mode/protocols_rip.py b/src/conf_mode/protocols_rip.py
index e7eafd059..b48289dec 100755
--- a/src/conf_mode/protocols_rip.py
+++ b/src/conf_mode/protocols_rip.py
@@ -20,7 +20,9 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
-from vyos.configverify import verify_route_maps
+from vyos.configverify import verify_common_route_maps
+from vyos.configverify import verify_access_list
+from vyos.configverify import verify_prefix_list
from vyos.util import call
from vyos.util import dict_search
from vyos.xml import defaults
@@ -51,10 +53,12 @@ def get_config(config=None):
rip = dict_merge(default_values, rip)
# 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
+ # and route-maps for instance. They will be used in verify().
+ #
+ # XXX: one MUST always call this without the key_mangling() option! See
+ # vyos.configverify.verify_common_route_maps() for more information.
+ tmp = conf.get_config_dict(['policy'])
+ # Merge policy dict into "regular" config dict
rip = dict_merge(tmp, rip)
return rip
@@ -63,21 +67,19 @@ def verify(rip):
if not rip:
return None
+ verify_common_route_maps(rip)
+
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!')
+ if acl_in: verify_access_list(acl_in, rip)
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 acl_out: verify_access_list(acl_out, rip)
- 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!')
+ prefix_list_in = dict_search('distribute_list.prefix-list.in', rip)
+ if prefix_list_in: verify_prefix_list(prefix_list_in, rip)
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!')
+ if prefix_list_out: verify_prefix_list(prefix_list_out, rip)
if 'interface' in rip:
for interface, interface_options in rip['interface'].items():
@@ -89,8 +91,6 @@ def verify(rip):
raise ConfigError(f'You can not have "split-horizon poison-reverse" enabled ' \
f'with "split-horizon disable" for "{interface}"!')
- verify_route_maps(rip)
-
def generate(rip):
if not rip:
rip['new_frr_config'] = ''
diff --git a/src/conf_mode/protocols_ripng.py b/src/conf_mode/protocols_ripng.py
index 140133bd0..06a9e97df 100755
--- a/src/conf_mode/protocols_ripng.py
+++ b/src/conf_mode/protocols_ripng.py
@@ -20,7 +20,9 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
-from vyos.configverify import verify_route_maps
+from vyos.configverify import verify_common_route_maps
+from vyos.configverify import verify_access_list
+from vyos.configverify import verify_prefix_list
from vyos.util import call
from vyos.util import dict_search
from vyos.xml import defaults
@@ -51,10 +53,12 @@ def get_config(config=None):
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
+ # and route-maps for instance. They will be used in verify().
+ #
+ # XXX: one MUST always call this without the key_mangling() option! See
+ # vyos.configverify.verify_common_route_maps() for more information.
+ tmp = conf.get_config_dict(['policy'])
+ # Merge policy dict into "regular" config dict
ripng = dict_merge(tmp, ripng)
return ripng
@@ -63,21 +67,19 @@ def verify(ripng):
if not ripng:
return None
+ verify_common_route_maps(ripng)
+
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!')
+ if acl_in: verify_access_list(acl_in, ripng, version='6')
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!')
+ if acl_out: verify_access_list(acl_out, ripng, version='6')
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!')
+ if prefix_list_in: verify_prefix_list(prefix_list_in, ripng, version='6')
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 prefix_list_out: verify_prefix_list(prefix_list_out, ripng, version='6')
if 'interface' in ripng:
for interface, interface_options in ripng['interface'].items():
@@ -89,8 +91,6 @@ def verify(ripng):
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'] = ''
diff --git a/src/conf_mode/protocols_static.py b/src/conf_mode/protocols_static.py
index 7ae952af8..b5b2d6641 100755
--- a/src/conf_mode/protocols_static.py
+++ b/src/conf_mode/protocols_static.py
@@ -20,7 +20,7 @@ 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_common_route_maps
from vyos.configverify import verify_vrf
from vyos.template import render_to_string
from vyos.util import call
@@ -52,7 +52,7 @@ def get_config(config=None):
return static
def verify(static):
- verify_route_maps(static)
+ verify_common_route_maps(static)
for route in ['route', 'route6']:
# if there is no route(6) key in the dictionary we can immediately
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index 414e514c5..a39da8991 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -23,14 +23,18 @@ from vyos.config import Config
from vyos.configdict import node_changed
from vyos.ifconfig import Interface
from vyos.template import render
+from vyos.template import render_to_string
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 frr
from vyos import airbag
airbag.enable()
+frr_daemon = 'zebra'
+
config_file = r'/etc/iproute2/rt_tables.d/vyos-vrf.conf'
def list_rules():
@@ -123,6 +127,7 @@ def verify(vrf):
def generate(vrf):
render(config_file, 'vrf/vrf.conf.tmpl', vrf)
+ vrf['new_frr_config'] = render_to_string('frr/vrf.frr.tmpl', vrf)
return None
def apply(vrf):
@@ -210,6 +215,22 @@ def apply(vrf):
if 1000 in [r.get('priority') for r in list_rules() if r.get('priority') == 1000]:
call(f'ip {af} rule del pref 1000')
+ # add configuration to FRR
+ frr_cfg = frr.FRRConfig()
+ frr_cfg.load_configuration(frr_daemon)
+ frr_cfg.modify_section(f'^vrf [a-zA-Z-]*$', '')
+ frr_cfg.add_before(r'(interface .*|line vty)', vrf['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 vrf['new_frr_config'] == '':
+ for a in range(5):
+ frr_cfg.commit_configuration(frr_daemon)
+
+ # Save configuration to /run/frr/config/frr.conf
+ frr.save_configuration()
+
return None
if __name__ == '__main__':
diff --git a/src/tests/test_template.py b/src/tests/test_template.py
index 7800d007f..67c0fe84a 100644
--- a/src/tests/test_template.py
+++ b/src/tests/test_template.py
@@ -14,13 +14,23 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import os
import vyos.template
+
from unittest import TestCase
class TestVyOSTemplate(TestCase):
def setUp(self):
pass
+ def test_is_interface(self):
+ for interface in ['lo', 'eth0']:
+ if os.path.exists(f'/sys/class/net/{interface}'):
+ self.assertTrue(vyos.template.is_interface(interface))
+ else:
+ self.assertFalse(vyos.template.is_interface(interface))
+ self.assertFalse(vyos.template.is_interface('non-existent'))
+
def test_is_ip(self):
self.assertTrue(vyos.template.is_ip('192.0.2.1'))
self.assertTrue(vyos.template.is_ip('2001:db8::1'))