summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--data/configd-include.json1
-rw-r--r--data/templates/frr/policy.frr.tmpl282
-rw-r--r--interface-definitions/include/policy-list-description.xml.i11
-rw-r--r--interface-definitions/include/policy-list-rule-description.xml.i11
-rw-r--r--interface-definitions/include/policy/action.xml.i (renamed from interface-definitions/include/policy-list-action.xml.i)0
-rw-r--r--interface-definitions/include/policy/description.xml.i11
-rw-r--r--interface-definitions/include/policy/host.xml.i14
-rw-r--r--interface-definitions/include/policy/inverse-mask.xml.i14
-rw-r--r--interface-definitions/include/policy/network.xml.i14
-rw-r--r--interface-definitions/policy.xml.in162
-rw-r--r--python/vyos/template.py15
-rwxr-xr-xsmoketest/scripts/cli/test_policy.py9
-rwxr-xr-xsrc/conf_mode/policy.py98
14 files changed, 451 insertions, 193 deletions
diff --git a/Makefile b/Makefile
index b157de71c..ccdb212c2 100644
--- a/Makefile
+++ b/Makefile
@@ -39,12 +39,10 @@ interface_definitions: $(config_xml_obj)
# XXX: delete top level node.def's that now live in other packages
rm -f $(TMPL_DIR)/firewall/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
- rm -rf $(TMPL_DIR)/npolicy
# XXX: test if there are empty node.def files - this is not allowed as these
# could mask help strings or mandatory priority statements
diff --git a/data/configd-include.json b/data/configd-include.json
index 4959e5020..f241d0cb6 100644
--- a/data/configd-include.json
+++ b/data/configd-include.json
@@ -30,6 +30,7 @@
"nat.py",
"nat66.py",
"ntp.py",
+"policy.py",
"policy-local-route.py",
"protocols_bfd.py",
"protocols_bgp.py",
diff --git a/data/templates/frr/policy.frr.tmpl b/data/templates/frr/policy.frr.tmpl
new file mode 100644
index 000000000..2d02efa52
--- /dev/null
+++ b/data/templates/frr/policy.frr.tmpl
@@ -0,0 +1,282 @@
+!
+{% if policy.access_list is defined and policy.access_list is not none %}
+{% for acl, acl_config in policy.access_list.items() | natural_sort %}
+{% if acl_config.description is defined and acl_config.description is not none %}
+access-list {{ acl }} remark {{ acl_config.description }}
+{% endif %}
+{% if acl_config.rule is defined and acl_config.rule is not none %}
+{% for rule, rule_config in acl_config.rule.items() | natural_sort %}
+{% set ip = '' %}
+{% set src = '' %}
+{% set src_mask = '' %}
+{% if rule_config.source is defined and rule_config.source.any is defined %}
+{% set src = 'any' %}
+{% elif rule_config.source is defined and rule_config.source.host is defined and rule_config.source.host is not none %}
+{% set src = 'host ' + rule_config.source.host %}
+{% elif rule_config.source is defined and rule_config.source.network is defined and rule_config.source.network is not none %}
+{% set src = rule_config.source.network %}
+{% set src_mask = rule_config.source.inverse_mask %}
+{% endif %}
+{% set dst = '' %}
+{% set dst_mask = '' %}
+{% if (acl|int >= 100 and acl|int <= 199) or (acl|int >= 2000 and acl|int <= 2699) %}
+{% set ip = 'ip' %}
+{% set dst = 'any' %}
+{% if rule_config.destination is defined and rule_config.destination.any is defined %}
+{% set dst = 'any' %}
+{% elif rule_config.destination is defined and rule_config.destination.host is defined and rule_config.destination.host is not none %}
+{% set dst = 'host ' + rule_config.destination.host %}
+{% elif rule_config.destination is defined and rule_config.destination.network is defined and rule_config.destination.network is not none %}
+{% set dst = rule_config.destination.network %}
+{% set dst_mask = rule_config.destination.inverse_mask %}
+{% endif %}
+{% endif %}
+access-list {{ acl }} seq {{ rule }} {{ rule_config.action }} {{ ip }} {{ src }} {{ src_mask }} {{ dst }} {{ dst_mask }}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
+!
+{% if policy.access_list6 is defined and policy.access_list6 is not none %}
+{% for acl, acl_config in policy.access_list6.items() | natural_sort %}
+{% if acl_config.description is defined and acl_config.description is not none %}
+ipv6 access-list {{ acl }} remark {{ acl_config.description }}
+{% endif %}
+{% if acl_config.rule is defined and acl_config.rule is not none %}
+{% for rule, rule_config in acl_config.rule.items() | natural_sort %}
+{% set src = '' %}
+{% if rule_config.source is defined and rule_config.source.any is defined %}
+{% set src = 'any' %}
+{% elif rule_config.source is defined and rule_config.source.network is defined and rule_config.source.network is not none %}
+{% set src = rule_config.source.network %}
+{% endif %}
+ipv6 access-list {{ acl }} seq {{ rule }} {{ rule_config.action }} {{ src }} {{ 'exact-match' if rule_config.source.exact_match is defined }}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
+!
+{% if policy.as_path_list is defined and policy.as_path_list is not none %}
+{% for acl, acl_config in policy.as_path_list.items() | natural_sort %}
+{% if acl_config.rule is defined and acl_config.rule is not none %}
+{% for rule, rule_config in acl_config.rule.items() | natural_sort %}
+bgp as-path access-list {{ acl }} {{ rule_config.action }} {{ rule_config.regex }}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
+!
+{% if policy.community_list is defined and policy.community_list is not none %}
+{% for list, list_config in policy.community_list.items() | natural_sort %}
+{% if list_config.rule is defined and list_config.rule is not none %}
+{% for rule, rule_config in list_config.rule.items() | natural_sort %}
+{# by default, if casting to int fails it returns 0 #}
+{% if list|int != 0 %}
+bgp community-list {{ list }} seq {{ rule }} {{ rule_config.action }} {{ rule_config.regex }}
+{% else %}
+bgp community-list expanded {{ list }} seq {{ rule }} {{ rule_config.action }} {{ rule_config.regex }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
+!
+{% if policy.extcommunity_list is defined and policy.extcommunity_list is not none %}
+{% for list, list_config in policy.extcommunity_list.items() | natural_sort %}
+{% if list_config.rule is defined and list_config.rule is not none %}
+{% for rule, rule_config in list_config.rule.items() | natural_sort %}
+{# by default, if casting to int fails it returns 0 #}
+{% if list|int != 0 %}
+bgp extcommunity-list {{ list }} seq {{ rule }} {{ rule_config.action }} {{ rule_config.regex }}
+{% else %}
+bgp extcommunity-list expanded {{ list }} seq {{ rule }} {{ rule_config.action }} {{ rule_config.regex }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
+!
+{% if policy.large_community_list is defined and policy.large_community_list is not none %}
+{% for list, list_config in policy.large_community_list.items() | natural_sort %}
+{% if list_config.rule is defined and list_config.rule is not none %}
+{% for rule, rule_config in list_config.rule.items() | natural_sort %}
+{# by default, if casting to int fails it returns 0 #}
+{% if list|int != 0 %}
+bgp large-community-list {{ list }} seq {{ rule }} {{ rule_config.action }} {{ rule_config.regex }}
+{% else %}
+bgp large-community-list expanded {{ list }} seq {{ rule }} {{ rule_config.action }} {{ rule_config.regex }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
+!
+{% if policy.prefix_list is defined and policy.prefix_list is not none %}
+{% for prefix_list, prefix_list_config in policy.prefix_list.items() | natural_sort %}
+{% if prefix_list_config.description is defined and prefix_list_config.description is not none %}
+ip prefix-list {{ prefix_list }} description {{ prefix_list_config.description }}
+{% endif %}
+{% if prefix_list_config.rule is defined and prefix_list_config.rule is not none %}
+{% for rule, rule_config in prefix_list_config.rule.items() | natural_sort %}
+ip prefix-list {{ prefix_list }} seq {{ rule }} {{ rule_config.action }} {{ rule_config.prefix }} {{ 'ge ' + rule_config.ge if rule_config.ge is defined }} {{ 'le ' + rule_config.le if rule_config.le is defined }}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
+!
+{% if policy.prefix_list6 is defined and policy.prefix_list6 is not none %}
+{% for prefix_list, prefix_list_config in policy.prefix_list6.items() | natural_sort %}
+{% if prefix_list_config.description is defined and prefix_list_config.description is not none %}
+ipv6 prefix-list {{ prefix_list }} description {{ prefix_list_config.description }}
+{% endif %}
+{% if prefix_list_config.rule is defined and prefix_list_config.rule is not none %}
+{% for rule, rule_config in prefix_list_config.rule.items() | natural_sort %}
+ipv6 prefix-list {{ prefix_list }} seq {{ rule }} {{ rule_config.action }} {{ rule_config.prefix }} {{ 'ge ' + rule_config.ge if rule_config.ge is defined }} {{ 'le ' + rule_config.le if rule_config.le is defined }}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
+!
+{% if policy.route_map is defined and policy.route_map is not none %}
+{% for route_map, route_map_config in policy.route_map.items() | natural_sort %}
+{% if route_map_config.rule is defined and route_map_config.rule is not none %}
+{% for rule, rule_config in route_map_config.rule.items() | natural_sort %}
+route-map {{ route_map }} {{ rule_config.action }} {{ rule }}
+{% if rule_config.call is defined and rule_config.call is not none %}
+ call {{ rule_config.call }}
+{% endif %}
+{% if rule_config.continue is defined and rule_config.continue is not none %}
+ on-match goto {{ rule_config.continue }}
+{% endif %}
+{% if rule_config.description is defined and rule_config.description is not none %}
+ description {{ rule_config.description }}
+{% endif %}
+{% if rule_config.match is defined and rule_config.match is not none %}
+{% if rule_config.match.as_path is defined and rule_config.match.as_path is not none %}
+ match as-path {{ rule_config.match.as_path }}
+{% endif %}
+{% if rule_config.match.community is defined and rule_config.match.community.community_list is defined and rule_config.match.community.community_list is not none %}
+ match community {{ rule_config.match.community.community_list }} {{ 'exact-match' if rule_config.match.community.exact_match is defined }}
+{% endif %}
+{% if rule_config.match.extcommunity is defined and rule_config.match.extcommunity is not none %}
+ match extcommunity {{ rule_config.match.extcommunity }}
+{% endif %}
+{% if rule_config.match.interface is defined and rule_config.match.interface is not none %}
+ match interface {{ rule_config.match.interface }}
+{% endif %}
+{% if rule_config.match.ip is defined and rule_config.match.ip.address is defined and rule_config.match.ip.address.access_list is defined and rule_config.match.ip.address.access_list is not none %}
+ match ip address {{ rule_config.match.ip.address.access_list }}
+{% endif %}
+{% if rule_config.match.ip is defined and rule_config.match.ip.address is defined and rule_config.match.ip.address.prefix_list is defined and rule_config.match.ip.address.prefix_list is not none %}
+ match ip address prefix-list {{ rule_config.match.ip.address.prefix_list }}
+{% endif %}
+{% if rule_config.match.ip is defined and rule_config.match.ip.nexthop is defined and rule_config.match.ip.nexthop.access_list is defined and rule_config.match.ip.nexthop.access_list is not none %}
+ match ip next-hop {{ rule_config.match.ip.nexthop.access_list }}
+{% endif %}
+{% if rule_config.match.ip is defined and rule_config.match.ip.nexthop is defined and rule_config.match.ip.nexthop.prefix_list is defined and rule_config.match.ip.nexthop.prefix_list is not none %}
+ match ip next-hop prefix-list {{ rule_config.match.ip.nexthop.prefix_list }}
+{% endif %}
+{% if rule_config.match.ip is defined and rule_config.match.ip.route_source is defined and rule_config.match.ip.route_source.access_list is defined and rule_config.match.ip.route_source.access_list is not none %}
+ match ip route-source {{ rule_config.match.ip.route_source.access_list }}
+{% endif %}
+{% if rule_config.match.ip is defined and rule_config.match.ip.route_source is defined and rule_config.match.ip.route_source.prefix_list is defined and rule_config.match.ip.route_source.prefix_list is not none %}
+ match ip route-source prefix-list {{ rule_config.match.ip.route_source.prefix_list }}
+{% endif %}
+{% if rule_config.match.ipv6 is defined and rule_config.match.ipv6.address is defined and rule_config.match.ipv6.address.access_list is defined and rule_config.match.ipv6.address.access_list is not none %}
+ match ipv6 address {{ rule_config.match.ipv6.address.access_list }}
+{% endif %}
+{% if rule_config.match.ipv6 is defined and rule_config.match.ipv6.address is defined and rule_config.match.ipv6.address.prefix_list is defined and rule_config.match.ipv6.address.prefix_list is not none %}
+ match ipv6 address prefix-list {{ rule_config.match.ipv6.address.prefix_list }}
+{% endif %}
+{% if rule_config.match.ipv6 is defined and rule_config.match.ipv6.nexthop is defined and rule_config.match.ipv6.nexthop is not none %}
+ match ipv6 next-hop {{ rule_config.match.ipv6.nexthop }}
+{% endif %}
+{% if rule_config.match.large_community is defined and rule_config.match.large_community.large_community_list is defined and rule_config.match.large_community.large_community_list is not none %}
+ match large-community {{ rule_config.match.large_community.large_community_list }}
+{% endif %}
+{% if rule_config.match.local_preference is defined and rule_config.match.local_preference is not none %}
+ match local-preference {{ rule_config.match.local_preference }}
+{% endif %}
+{% if rule_config.match.metric is defined and rule_config.match.metric is not none %}
+ match metric {{ rule_config.match.metric }}
+{% endif %}
+{% if rule_config.match.origin is defined and rule_config.match.origin is not none %}
+ match origin {{ rule_config.match.origin }}
+{% endif %}
+{% if rule_config.match.peer is defined and rule_config.match.peer is not none %}
+ match peer {{ rule_config.match.peer }}
+{% endif %}
+{% if rule_config.match.rpki is defined and rule_config.match.rpki is not none %}
+ match rpki {{ rule_config.match.rpki }}
+{% endif %}
+{% if rule_config.match.tag is defined and rule_config.match.tag is not none %}
+ match tag {{ rule_config.match.tag }}
+{% endif %}
+{% endif %}
+{% if rule_config.on_match is defined and rule_config.on_match is not none %}
+{% if rule_config.on_match.next is defined %}
+ on-match next
+{% endif %}
+{% if rule_config.on_match.goto is defined and rule_config.on_match.goto is not none %}
+ on-match goto {{ rule_config.on_match.goto }}
+{% endif %}
+{% endif %}
+{% if rule_config.set is defined and rule_config.set is not none %}
+{% if rule_config.set.aggregator is defined and rule_config.set.aggregator.as is defined and rule_config.set.aggregator.ip is defined %}
+ set aggregator as {{ rule_config.set.aggregator.as }} {{ rule_config.set.aggregator.ip }}
+{% endif %}
+{% if rule_config.set.as_path_exclude is defined and rule_config.set.as_path_exclude is not none %}
+ set as-path exclude {{ rule_config.set.as_path_exclude }}
+{% endif %}
+{% if rule_config.set.as_path_prepend is defined and rule_config.set.as_path_prepend is not none %}
+ set as-path prepend {{ rule_config.set.as_path_prepend }}
+{% endif %}
+{% if rule_config.set.atomic_aggregate is defined %}
+ set atomic-aggregate
+{% endif %}
+{% if rule_config.set.distance is defined and rule_config.set.distance is not none %}
+ set distance {{ rule_config.set.distance }}
+{% endif %}
+{% if rule_config.set.ip_next_hop is defined and rule_config.set.ip_next_hop is not none %}
+ set ip next-hop {{ rule_config.set.ip_next_hop }}
+{% endif %}
+{% if rule_config.set.ipv6_next_hop is defined and rule_config.set.ipv6_next_hop.global is defined and rule_config.set.ipv6_next_hop.global is not none %}
+ set ipv6 next-hop global {{ rule_config.set.ipv6_next_hop.global }}
+{% endif %}
+{% if rule_config.set.ipv6_next_hop is defined and rule_config.set.ipv6_next_hop.local is defined and rule_config.set.ipv6_next_hop.local is not none %}
+ set ipv6 next-hop local {{ rule_config.set.ipv6_next_hop.local }}
+{% endif %}
+{% if rule_config.set.large_community is defined and rule_config.set.large_community is not none %}
+ set large-community {{ rule_config.set.large_community }}
+{% endif %}
+{% if rule_config.set.local_preference is defined and rule_config.set.local_preference is not none %}
+ set local-preference {{ rule_config.set.local_preference }}
+{% endif %}
+{% if rule_config.set.metric is defined and rule_config.set.metric is not none %}
+ set metric {{ rule_config.set.metric }}
+{% endif %}
+{% if rule_config.set.metric_type is defined and rule_config.set.metric_type is not none %}
+ set metric-type {{ rule_config.set.metric_type }}
+{% endif %}
+{% if rule_config.set.origin is defined and rule_config.set.origin is not none %}
+ set origin {{ rule_config.set.origin }}
+{% endif %}
+{% if rule_config.set.originator_id is defined and rule_config.set.originator_id is not none %}
+ set originator-id {{ rule_config.set.originator_id }}
+{% endif %}
+{% if rule_config.set.src is defined and rule_config.set.src is not none %}
+ set src {{ rule_config.set.src }}
+{% endif %}
+{% if rule_config.set.tag is defined and rule_config.set.tag is not none %}
+ set tag {{ rule_config.set.tag }}
+{% endif %}
+{% if rule_config.set.weight is defined and rule_config.set.weight is not none %}
+ set weight {{ rule_config.set.weight }}
+{% endif %}
+{% endif %}
+{% endfor %}
+!
+{% endif %}
+{% endfor %}
+{% endif %}
+!
diff --git a/interface-definitions/include/policy-list-description.xml.i b/interface-definitions/include/policy-list-description.xml.i
deleted file mode 100644
index a50278729..000000000
--- a/interface-definitions/include/policy-list-description.xml.i
+++ /dev/null
@@ -1,11 +0,0 @@
-<!-- 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
deleted file mode 100644
index e22fb7c28..000000000
--- a/interface-definitions/include/policy-list-rule-description.xml.i
+++ /dev/null
@@ -1,11 +0,0 @@
-<!-- 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/policy-list-action.xml.i b/interface-definitions/include/policy/action.xml.i
index fddbd5a98..fddbd5a98 100644
--- a/interface-definitions/include/policy-list-action.xml.i
+++ b/interface-definitions/include/policy/action.xml.i
diff --git a/interface-definitions/include/policy/description.xml.i b/interface-definitions/include/policy/description.xml.i
new file mode 100644
index 000000000..15a77cd66
--- /dev/null
+++ b/interface-definitions/include/policy/description.xml.i
@@ -0,0 +1,11 @@
+<!-- included start from policy/description.xml.i -->
+<leafNode name="description">
+ <properties>
+ <help>Description</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Description</description>
+ </valueHelp>
+ </properties>
+</leafNode>
+<!-- included end -->
diff --git a/interface-definitions/include/policy/host.xml.i b/interface-definitions/include/policy/host.xml.i
new file mode 100644
index 000000000..ac017c630
--- /dev/null
+++ b/interface-definitions/include/policy/host.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from policy/host.xml.i -->
+<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-address"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/policy/inverse-mask.xml.i b/interface-definitions/include/policy/inverse-mask.xml.i
new file mode 100644
index 000000000..cec69a81b
--- /dev/null
+++ b/interface-definitions/include/policy/inverse-mask.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from policy/inverse-mask.xml.i -->
+<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>
+<!-- include end -->
diff --git a/interface-definitions/include/policy/network.xml.i b/interface-definitions/include/policy/network.xml.i
new file mode 100644
index 000000000..f2aea6be8
--- /dev/null
+++ b/interface-definitions/include/policy/network.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from policy/network.xml.i -->
+<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="ipv4-address"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in
index 7cf20d3de..3a769dea1 100644
--- a/interface-definitions/policy.xml.in
+++ b/interface-definitions/policy.xml.in
@@ -1,15 +1,15 @@
<?xml version="1.0"?>
-<!-- Policy access|prefix|route-map lists -->
<interfaceDefinition>
- <node name="npolicy" owner="${vyos_conf_scripts_dir}/policy.py">
+ <node name="policy" owner="${vyos_conf_scripts_dir}/policy.py">
<properties>
+ <priority>470</priority>
<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>
@@ -28,7 +28,7 @@
</valueHelp>
</properties>
<children>
- #include <include/policy-list-description.xml.i>
+ #include <include/policy/description.xml.i>
<tagNode name="rule">
<properties>
<help>Rule for this access-list</help>
@@ -41,8 +41,8 @@
</constraint>
</properties>
<children>
- #include <include/policy-list-action.xml.i>
- #include <include/policy-list-rule-description.xml.i>
+ #include <include/policy/action.xml.i>
+ #include <include/policy/description.xml.i>
<node name="destination">
<properties>
<help>Destination network or address</help>
@@ -54,42 +54,9 @@
<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>
+ #include <include/policy/host.xml.i>
+ #include <include/policy/inverse-mask.xml.i>
+ #include <include/policy/network.xml.i>
</children>
</node>
<node name="source">
@@ -103,42 +70,9 @@
<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>
+ #include <include/policy/host.xml.i>
+ #include <include/policy/inverse-mask.xml.i>
+ #include <include/policy/network.xml.i>
</children>
</node>
</children>
@@ -148,14 +82,13 @@
<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>
+ #include <include/policy/description.xml.i>
<tagNode name="rule">
<properties>
<help>Rule for this access-list6</help>
@@ -168,8 +101,8 @@
</constraint>
</properties>
<children>
- #include <include/policy-list-action.xml.i>
- #include <include/policy-list-rule-description.xml.i>
+ #include <include/policy/action.xml.i>
+ #include <include/policy/description.xml.i>
<node name="source">
<properties>
<help>Source IPv6 network to match</help>
@@ -208,14 +141,13 @@
<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>
+ #include <include/policy/description.xml.i>
<tagNode name="rule">
<properties>
<help>Rule for this as-path-list</help>
@@ -228,8 +160,8 @@
</constraint>
</properties>
<children>
- #include <include/policy-list-action.xml.i>
- #include <include/policy-list-rule-description.xml.i>
+ #include <include/policy/action.xml.i>
+ #include <include/policy/description.xml.i>
<leafNode name="regex">
<properties>
<help>Regular expression to match against an AS path</help>
@@ -246,14 +178,13 @@
<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>
+ #include <include/policy/description.xml.i>
<tagNode name="rule">
<properties>
<help>Rule for this BGP community list</help>
@@ -266,8 +197,8 @@
</constraint>
</properties>
<children>
- #include <include/policy-list-action.xml.i>
- #include <include/policy-list-rule-description.xml.i>
+ #include <include/policy/action.xml.i>
+ #include <include/policy/description.xml.i>
<leafNode name="regex">
<properties>
<help>Regular expression to match against a community list</help>
@@ -291,7 +222,7 @@
</valueHelp>
</properties>
<children>
- #include <include/policy-list-description.xml.i>
+ #include <include/policy/description.xml.i>
<tagNode name="rule">
<properties>
<help>Rule for this BGP extended community list</help>
@@ -304,8 +235,8 @@
</constraint>
</properties>
<children>
- #include <include/policy-list-action.xml.i>
- #include <include/policy-list-rule-description.xml.i>
+ #include <include/policy/action.xml.i>
+ #include <include/policy/description.xml.i>
<leafNode name="regex">
<properties>
<help>Regular expression to match against an extended community list</help>
@@ -330,14 +261,13 @@
<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>
+ #include <include/policy/description.xml.i>
<tagNode name="rule">
<properties>
<help>Rule for this BGP extended community list</help>
@@ -350,8 +280,8 @@
</constraint>
</properties>
<children>
- #include <include/policy-list-action.xml.i>
- #include <include/policy-list-rule-description.xml.i>
+ #include <include/policy/action.xml.i>
+ #include <include/policy/description.xml.i>
<leafNode name="regex">
<properties>
<help>Regular expression to match against a large community list</help>
@@ -368,14 +298,13 @@
<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>
+ #include <include/policy/description.xml.i>
<tagNode name="rule">
<properties>
<help>Rule for this prefix-list</help>
@@ -388,8 +317,8 @@
</constraint>
</properties>
<children>
- #include <include/policy-list-action.xml.i>
- #include <include/policy-list-rule-description.xml.i>
+ #include <include/policy/action.xml.i>
+ #include <include/policy/description.xml.i>
<leafNode name="ge">
<properties>
<help>Prefix length to match a netmask greater than or equal to it</help>
@@ -433,14 +362,13 @@
<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>
+ #include <include/policy/description.xml.i>
<tagNode name="rule">
<properties>
<help>Rule for this prefix-list6</help>
@@ -453,8 +381,8 @@
</constraint>
</properties>
<children>
- #include <include/policy-list-action.xml.i>
- #include <include/policy-list-rule-description.xml.i>
+ #include <include/policy/action.xml.i>
+ #include <include/policy/description.xml.i>
<leafNode name="ge">
<properties>
<help>Prefix length to match a netmask greater than or equal to it</help>
@@ -498,14 +426,17 @@
<tagNode name="route-map">
<properties>
<help>IP route-map</help>
- <priority>470</priority>
<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>
<children>
- #include <include/policy-list-description.xml.i>
+ #include <include/policy/description.xml.i>
<tagNode name="rule">
<properties>
<help>Rule for this route-map</help>
@@ -518,7 +449,7 @@
</constraint>
</properties>
<children>
- #include <include/policy-list-action.xml.i>
+ #include <include/policy/action.xml.i>
<leafNode name="call">
<properties>
<help>Call another route-map on match</help>
@@ -540,7 +471,7 @@
</valueHelp>
</properties>
</leafNode>
- #include <include/policy-list-rule-description.xml.i>
+ #include <include/policy/description.xml.i>
<node name="match">
<properties>
<help>Route parameters to match</help>
@@ -759,11 +690,11 @@
<properties>
<help>IPv6 next-hop of route to match</help>
<valueHelp>
- <format>ipv4</format>
- <description>Peer IP address</description>
+ <format>ipv6</format>
+ <description>Nexthop IPv6 address</description>
</valueHelp>
<constraint>
- <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
</constraint>
</properties>
</leafNode>
@@ -962,6 +893,7 @@
<leafNode name="atomic-aggregate">
<properties>
<help>Border Gateway Protocol (BGP) atomic aggregate attribute</help>
+ <valueless/>
</properties>
</leafNode>
<leafNode name="bgp-extcommunity-rt">
@@ -1198,7 +1130,7 @@
<description>Orignator IP address</description>
</valueHelp>
<constraint>
- <validator name="ipv4-host"/>
+ <validator name="ipv4-address"/>
</constraint>
</properties>
</leafNode>
@@ -1214,8 +1146,8 @@
<description>IPv6 address</description>
</valueHelp>
<constraint>
- <validator name="ipv4-host"/>
- <validator name="ipv6-host"/>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
</constraint>
</properties>
</leafNode>
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 7810f5edd..3fbb33acb 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -346,3 +346,18 @@ def get_dhcp_router(interface):
if 'option routers' in line:
(_, _, address) = line.split()
return address.rstrip(';')
+
+@register_filter('natural_sort')
+def natural_sort(iterable):
+ import re
+ from jinja2.runtime import Undefined
+
+ if isinstance(iterable, Undefined) or iterable is None:
+ return list()
+
+ def convert(text):
+ return int(text) if text.isdigit() else text.lower()
+ def alphanum_key(key):
+ return [convert(c) for c in re.split('([0-9]+)', str(key))]
+
+ return sorted(iterable, key=alphanum_key)
diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py
index 57c1bb088..59425b789 100755
--- a/smoketest/scripts/cli/test_policy.py
+++ b/smoketest/scripts/cli/test_policy.py
@@ -767,9 +767,8 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
'10' : {
'action' : 'deny',
'set' : {
-# Disabled b/c of https://phabricator.vyos.net/T3479
-# 'aggregator-as' : '1234567890',
-# 'aggregator-ip' : '10.255.255.0',
+ 'aggregator-as' : '1234567890',
+ 'aggregator-ip' : '10.255.255.0',
'as-path-exclude' : '1234',
'as-path-prepend' : '1234567890 987654321',
'atomic-aggregate' : '',
@@ -1036,12 +1035,12 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
tmp += 'atomic-aggregate'
elif 'distance' in rule_config['set']:
tmp += 'distance ' + rule_config['set']['distance']
+ elif 'ip-next-hop' in rule_config['set']:
+ tmp += 'ip next-hop ' + rule_config['set']['ip-next-hop']
elif 'ipv6-next-hop-global' in rule_config['set']:
tmp += 'ipv6 next-hop global ' + rule_config['set']['ipv6-next-hop-global']
elif 'ipv6-next-hop-local' in rule_config['set']:
tmp += 'ipv6 next-hop local ' + rule_config['set']['ipv6-next-hop-local']
- elif 'ip-next-hop' in rule_config['set']:
- tmp += 'ip next-hop ' + rule_config['set']['ip-next-hop']
elif 'large-community' in rule_config['set']:
tmp += 'large-community ' + rule_config['set']['large-community']
elif 'local-preference' in rule_config['set']:
diff --git a/src/conf_mode/policy.py b/src/conf_mode/policy.py
index 94a020e7b..f2e9010e3 100755
--- a/src/conf_mode/policy.py
+++ b/src/conf_mode/policy.py
@@ -20,47 +20,46 @@ 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)
+ base = ['policy']
+ policy = conf.get_config_dict(base, key_mangling=('-', '_'),
+ no_tag_node_value_mangle=True)
return policy
def verify(policy):
if not policy:
return None
+ if 'access-list' in policy:
+ for acl, acl_config in policy['access-list'].items():
+ if 'rule' not in acl_config:
+ continue
+
+ for rule, rule_config in acl_config['rule'].items():
+ if 'source' not in rule_config:
+ raise ConfigError(f'Source must be specified for rule {rule} '\
+ f'for access-list {acl}!')
+ if 'action' not in rule_config:
+ raise ConfigError(f'Action must be specified for rule {rule} '\
+ f'for access-list {acl}!')
+
+ if int(acl) not in range(100, 200) or int(acl) not in range(2000, 2700):
+ if 'destination' not in rule_config:
+ raise ConfigError(f'Destination must be specified for rule {rule} '\
+ f'for access-list {acl}!')
+
return None
def generate(policy):
@@ -68,41 +67,42 @@ def generate(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')
-
+ policy['new_frr_config'] = render_to_string('frr/policy.frr.tmpl', policy)
return None
def apply(policy):
+ bgp_daemon = 'bgpd'
+ zebra_daemon = 'zebra'
+
# 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)
+ frr_cfg = frr.FRRConfig()
+
+ # The route-map used for the FIB (zebra) is part of the zebra daemon
+ frr_cfg.load_configuration(bgp_daemon)
+ frr_cfg.modify_section(r'^bgp as-path access-list .*')
+ frr_cfg.modify_section(r'^bgp community-list .*')
+ frr_cfg.modify_section(r'^bgp extcommunity-list .*')
+ frr_cfg.modify_section(r'^bgp large-community-list .*')
+ frr_cfg.add_before('^line vty', policy['new_frr_config'])
+ frr_cfg.commit_configuration(bgp_daemon)
+
+ # The route-map used for the FIB (zebra) is part of the zebra daemon
+ frr_cfg.load_configuration(zebra_daemon)
+ frr_cfg.modify_section(r'^access-list .*')
+ frr_cfg.modify_section(r'^ipv6 access-list .*')
+ frr_cfg.modify_section(r'^ip prefix-list .*')
+ frr_cfg.modify_section(r'^ipv6 prefix-list .*')
+ frr_cfg.add_before('^line vty', policy['new_frr_config'])
+ frr_cfg.commit_configuration(zebra_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)
+ if policy['new_frr_config'] == '':
+ for a in range(5):
+ frr_cfg.commit_configuration(zebra_daemon)
+ # Save configuration to /run/frr/config/frr.conf
+ frr.save_configuration()
return None