Page MenuHomeVyOS Platform

No OneTemporary

Size
80 KB
Referenced Files
None
Subscribers
None
diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl
index 96815836b..4ac2127cb 100644
--- a/data/templates/frr/bgpd.frr.tmpl
+++ b/data/templates/frr/bgpd.frr.tmpl
@@ -1,523 +1,524 @@
{### MACRO definition for recurring peer patter, this can be either fed by a ###}
{### peer-group or an individual BGP neighbor ###}
{% macro bgp_neighbor(neighbor, config, peer_group=false) %}
{% if peer_group == true %}
neighbor {{ neighbor }} peer-group
{% elif config.peer_group is defined and config.peer_group is not none %}
neighbor {{ neighbor }} peer-group {{ config.peer_group }}
{% endif %}
{% if config.remote_as is defined and config.remote_as is not none %}
neighbor {{ neighbor }} remote-as {{ config.remote_as }}
{% endif %}
{% if config.interface is defined and config.interface.remote_as is defined and config.interface.remote_as is not none %}
neighbor {{ neighbor }} interface remote-as {{ config.interface.remote_as }}
{% endif %}
{% if config.advertisement_interval is defined and config.advertisement_interval is not none %}
neighbor {{ neighbor }} advertisement-interval {{ config.advertisement_interval }}
{% endif %}
{% if config.bfd is defined %}
neighbor {{ neighbor }} bfd
{% endif %}
{% if config.capability is defined and config.capability is not none %}
{% if config.capability.dynamic is defined %}
neighbor {{ neighbor }} capability dynamic
{% endif %}
{% if config.capability.extended_nexthop is defined %}
neighbor {{ neighbor }} capability extended-nexthop
{% endif %}
{% endif %}
{% if config.description is defined and config.description is not none %}
neighbor {{ neighbor }} description {{ config.description }}
{% endif %}
{% if config.disable_capability_negotiation is defined %}
neighbor {{ neighbor }} dont-capability-negotiate
{% endif %}
{% if config.ebgp_multihop is defined and config.ebgp_multihop is not none %}
neighbor {{ neighbor }} ebgp-multihop {{ config.ebgp_multihop }}
{% endif %}
{% if config.graceful_restart is defined and config.graceful_restart is not none %}
{% if config.graceful_restart == 'enable' %}
{% set graceful_restart = 'graceful-restart' %}
{% elif config.graceful_restart == 'disable' %}
{% set graceful_restart = 'graceful-restart-disable' %}
{% elif config.graceful_restart == 'restart-helper' %}
{% set graceful_restart = 'graceful-restart-helper' %}
{% endif %}
neighbor {{ neighbor }} {{ graceful_restart }}
{% endif %}
{% if config.local_as is defined and config.local_as is not none %}
-{% for local_asn in config.local_as %}
- neighbor {{ neighbor }} local-as {{ local_asn }} {{ 'no-prepend' if config.local_as[local_asn].no_prepend is defined }}
+{% for local_asn, local_asn_config in config.local_as.items() %}
+{# There can be only one local-as value, this is checked in the Python code #}
+ neighbor {{ neighbor }} local-as {{ local_asn }} {{ 'no-prepend' if local_asn_config.no_prepend is defined }} {{ 'replace-as' if local_asn_config.replace_as is defined }}
{% endfor %}
{% endif %}
{% if config.override_capability is defined %}
neighbor {{ neighbor }} override-capability
{% endif %}
{% if config.passive is defined %}
neighbor {{ neighbor }} passive
{% endif %}
{% if config.password is defined and config.password is not none %}
neighbor {{ neighbor }} password {{ config.password }}
{% endif %}
{% if config.port is defined and config.port is not none %}
neighbor {{ neighbor }} port {{ config.port }}
{% endif %}
{% if config.shutdown is defined %}
neighbor {{ neighbor }} shutdown
{% endif %}
{% if config.solo is defined %}
neighbor {{ neighbor }} solo
{% endif %}
{% if config.strict_capability_match is defined %}
neighbor {{ neighbor }} strict-capability-match
{% endif %}
{% if config.ttl_security is defined and config.ttl_security.hops is defined and config.ttl_security.hops is not none %}
neighbor {{ neighbor }} ttl-security hops {{ config.ttl_security.hops }}
{% endif %}
{% if config.timers is defined %}
{% if config.timers.connect is defined and config.timers.connect is not none %}
neighbor {{ neighbor }} timers connect {{ config.timers.connect }}
{% endif %}
{% if config.timers.holdtime is defined and config.timers.keepalive is defined and config.timers.holdtime is not none and config.timers.keepalive is not none %}
neighbor {{ neighbor }} timers {{ config.timers.keepalive }} {{ config.timers.holdtime }}
{% endif %}
{% endif %}
{% if config.update_source is defined and config.update_source is not none %}
neighbor {{ neighbor }} update-source {{ config.update_source }}
{% endif %}
{% if config.interface is defined and config.interface is not none %}
{% if config.interface.peer_group is defined and config.interface.peer_group is not none %}
neighbor {{ neighbor }} interface peer-group {{ config.interface.peer_group }}
{% endif %}
{% if config.interface.v6only is defined and config.interface.v6only is not none %}
{% if config.interface.v6only.peer_group is defined and config.interface.v6only.peer_group is not none %}
neighbor {{ neighbor }} interface v6only peer-group {{ config.interface.v6only.peer_group }}
{% endif %}
{% if config.interface.v6only.remote_as is defined and config.interface.v6only.remote_as is not none %}
neighbor {{ neighbor }} interface v6only remote-as {{ config.interface.v6only.remote_as }}
{% endif %}
{% endif %}
{% endif %}
!
{% if config.address_family is defined and config.address_family is not none %}
{% for afi, afi_config in config.address_family.items() %}
{% if afi == 'ipv4_unicast' %}
address-family ipv4 unicast
{% elif afi == 'ipv4_multicast' %}
address-family ipv4 multicast
{% elif afi == 'ipv4_labeled_unicast' %}
address-family ipv4 labeled-unicast
{% elif afi == 'ipv4_vpn' %}
address-family ipv4 vpn
{% elif afi == 'ipv4_flowspec' %}
address-family ipv4 flowspec
{% elif afi == 'ipv6_unicast' %}
address-family ipv6 unicast
{% elif afi == 'ipv6_multicast' %}
address-family ipv6 multicast
{% elif afi == 'ipv6_labeled_unicast' %}
address-family ipv6 labeled-unicast
{% elif afi == 'ipv6_vpn' %}
address-family ipv6 vpn
{% elif afi == 'ipv6_flowspec' %}
address-family ipv6 flowspec
{% elif afi == 'l2vpn_evpn' %}
address-family l2vpn evpn
{% endif %}
{% if afi_config.addpath_tx_all is defined %}
neighbor {{ neighbor }} addpath-tx-all-paths
{% endif %}
{% if afi_config.addpath_tx_per_as is defined %}
neighbor {{ neighbor }} addpath-tx-bestpath-per-AS
{% endif %}
{% if afi_config.allowas_in is defined and afi_config.allowas_in is not none %}
neighbor {{ neighbor }} allowas-in {{ afi_config.allowas_in.number if afi_config.allowas_in.number is defined }}
{% endif %}
{% if afi_config.as_override is defined %}
neighbor {{ neighbor }} as-override
{% endif %}
{% if afi_config.remove_private_as is defined %}
neighbor {{ neighbor }} remove-private-AS
{% endif %}
{% if afi_config.route_reflector_client is defined %}
neighbor {{ neighbor }} route-reflector-client
{% endif %}
{% if afi_config.weight is defined and afi_config.weight is not none %}
neighbor {{ neighbor }} weight {{ afi_config.weight }}
{% endif %}
{% if afi_config.attribute_unchanged is defined and afi_config.attribute_unchanged is not none %}
neighbor {{ neighbor }} attribute-unchanged {{ 'as-path ' if afi_config.attribute_unchanged.as_path is defined }}{{ 'med ' if afi_config.attribute_unchanged.med is defined }}{{ 'next-hop ' if afi_config.attribute_unchanged.next_hop is defined }}
{% endif %}
{% if afi_config.capability is defined and afi_config.capability.orf is defined and afi_config.capability.orf.prefix_list is defined and afi_config.capability.orf.prefix_list.send is defined %}
neighbor {{ neighbor }} capability orf prefix-list send
{% endif %}
{% if afi_config.capability is defined and afi_config.capability.orf is defined and afi_config.capability.orf.prefix_list is defined and afi_config.capability.orf.prefix_list.receive is defined %}
neighbor {{ neighbor }} capability orf prefix-list receive
{% endif %}
{% if afi_config.default_originate is defined %}
neighbor {{ neighbor }} default-originate {{ 'route-map ' ~ afi_config.default_originate.route_map if afi_config.default_originate.route_map is defined }}
{% endif %}
{% if afi_config.distribute_list is defined and afi_config.distribute_list is not none %}
{% if afi_config.distribute_list.export is defined and afi_config.distribute_list.export is not none %}
neighbor {{ neighbor }} distribute-list {{ afi_config.distribute_list.export }} out
{% endif %}
{% if afi_config.distribute_list.import is defined and afi_config.distribute_list.import is not none %}
neighbor {{ neighbor }} distribute-list {{ afi_config.distribute_list.import }} in
{% endif %}
{% endif %}
{% if afi_config.filter_list is defined and afi_config.filter_list is not none %}
{% if afi_config.filter_list.export is defined and afi_config.filter_list.export is not none %}
neighbor {{ neighbor }} filter-list {{ afi_config.filter_list.export }} out
{% endif %}
{% if afi_config.filter_list.import is defined and afi_config.filter_list.import is not none %}
neighbor {{ neighbor }} filter-list {{ afi_config.filter_list.import }} in
{% endif %}
{% endif %}
{% if afi_config.maximum_prefix is defined and afi_config.maximum_prefix is not none %}
neighbor {{ neighbor }} maximum-prefix {{ afi_config.maximum_prefix }}
{% endif %}
{% if afi_config.maximum_prefix_out is defined and afi_config.maximum_prefix_out is not none %}
neighbor {{ neighbor }} maximum-prefix-out {{ afi_config.maximum_prefix_out }}
{% endif %}
{% if afi_config.nexthop_self is defined %}
neighbor {{ neighbor }} next-hop-self {{ 'force' if afi_config.nexthop_self.force is defined }}
{% endif %}
{% if afi_config.route_server_client is defined %}
neighbor {{ neighbor }} route-server-client
{% endif %}
{% if afi_config.route_map is defined and afi_config.route_map is not none %}
{% if afi_config.route_map.export is defined and afi_config.route_map.export is not none %}
neighbor {{ neighbor }} route-map {{ afi_config.route_map.export }} out
{% endif %}
{% if afi_config.route_map.import is defined and afi_config.route_map.import is not none %}
neighbor {{ neighbor }} route-map {{ afi_config.route_map.import }} in
{% endif %}
{% endif %}
{% if afi_config.prefix_list is defined and afi_config.prefix_list is not none %}
{% if afi_config.prefix_list.export is defined and afi_config.prefix_list.export is not none %}
neighbor {{ neighbor }} prefix-list {{ afi_config.prefix_list.export }} out
{% endif %}
{% if afi_config.prefix_list.import is defined and afi_config.prefix_list.import is not none %}
neighbor {{ neighbor }} prefix-list {{ afi_config.prefix_list.import }} in
{% endif %}
{% endif %}
{% if afi_config.soft_reconfiguration is defined and afi_config.soft_reconfiguration.inbound is defined %}
neighbor {{ neighbor }} soft-reconfiguration inbound
{% endif %}
{% if afi_config.unsuppress_map is defined and afi_config.unsuppress_map is not none %}
neighbor {{ neighbor }} unsuppress-map {{ afi_config.unsuppress_map }}
{% endif %}
{% if afi_config.disable_send_community is defined and afi_config.disable_send_community.extended is defined %}
no neighbor {{ neighbor }} send-community extended
{% endif %}
{% if afi_config.disable_send_community is defined and afi_config.disable_send_community.standard is defined %}
no neighbor {{ neighbor }} send-community standard
{% endif %}
neighbor {{ neighbor }} activate
exit-address-family
!
{% endfor %}
{% endif %}
{% endmacro %}
!
router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none }}
{% if parameters is defined and parameters.ebgp_requires_policy is defined %}
bgp ebgp-requires-policy
{% else %}
no bgp ebgp-requires-policy
{% endif %}
{% 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 %}
{% for afi, afi_config in address_family.items() %}
!
{% if afi == 'ipv4_unicast' %}
address-family ipv4 unicast
{% elif afi == 'ipv4_multicast' %}
address-family ipv4 multicast
{% elif afi == 'ipv4_labeled_unicast' %}
address-family ipv4 labeled-unicast
{% elif afi == 'ipv4_vpn' %}
address-family ipv4 vpn
{% elif afi == 'ipv4_flowspec' %}
address-family ipv4 flowspec
{% elif afi == 'ipv6_unicast' %}
address-family ipv6 unicast
{% elif afi == 'ipv6_multicast' %}
address-family ipv6 multicast
{% elif afi == 'ipv6_labeled_unicast' %}
address-family ipv6 labeled-unicast
{% elif afi == 'ipv6_vpn' %}
address-family ipv6 vpn
{% elif afi == 'ipv6_flowspec' %}
address-family ipv6 flowspec
{% elif afi == 'l2vpn_evpn' %}
address-family l2vpn evpn
{% if afi_config.rd is defined and afi_config.rd is not none %}
rd {{ afi_config.rd }}
{% endif %}
{% endif %}
{% if afi_config.aggregate_address is defined and afi_config.aggregate_address is not none %}
{% for ip in afi_config.aggregate_address %}
aggregate-address {{ ip }}{{ ' as-set' if afi_config.aggregate_address[ip].as_set is defined }}{{ ' summary-only' if afi_config.aggregate_address[ip].summary_only is defined }}
{% endfor %}
{% endif %}
{% if afi_config.maximum_paths is defined and afi_config.maximum_paths.ebgp is defined and afi_config.maximum_paths.ebgp is not none %}
maximum-paths {{ afi_config.maximum_paths.ebgp }}
{% endif %}
{% if afi_config.maximum_paths is defined and afi_config.maximum_paths.ibgp is defined and afi_config.maximum_paths.ibgp is not none %}
maximum-paths ibgp {{ afi_config.maximum_paths.ibgp }}
{% endif %}
{% if afi_config.redistribute is defined and afi_config.redistribute is not none %}
{% for protocol in afi_config.redistribute %}
{% if protocol == 'table' %}
redistribute table {{ afi_config.redistribute[protocol].table }}
{% else %}
{% set redistribution_protocol = protocol %}
{% if protocol == 'ospfv3' %}
{% set redistribution_protocol = 'ospf6' %}
{% endif %}
redistribute {{ redistribution_protocol }}{% if afi_config.redistribute[protocol].metric is defined %} metric {{ afi_config.redistribute[protocol].metric }}{% endif %}{% if afi_config.redistribute[protocol].route_map is defined %} route-map {{ afi_config.redistribute[protocol].route_map }}{% endif %}
{####### we need this blank line!! #######}
{% endif %}
{% endfor %}
{% endif %}
{% if afi_config.network is defined and afi_config.network is not none %}
{% for network in afi_config.network %}
network {{ network }}{% if afi_config.network[network].route_map is defined %} route-map {{ afi_config.network[network].route_map }}{% endif %}{% if afi_config.network[network].backdoor is defined %} backdoor{% endif %}{% if afi_config.network[network].rd is defined and afi_config.network[network].label is defined%} rd {{ afi_config.network[network].rd }} label {{ afi_config.network[network].label }}{% endif %}
{####### we need this blank line!! #######}
{% endfor %}
{% endif %}
{% if afi_config.advertise is defined and afi_config.advertise is not none %}
{% for adv_afi, adv_afi_config in afi_config.advertise.items() %}
{% if adv_afi_config.unicast is defined and adv_afi_config.unicast is not none %}
advertise {{ adv_afi }} unicast {{ 'route-map ' ~ adv_afi_config.unicast.route_map if adv_afi_config.unicast.route_map is defined }}
{% endif %}
{% endfor %}
{% endif %}
{% if afi_config.distance is defined and afi_config.distance is not none %}
{% if afi_config.distance is defined and afi_config.distance.external is defined and afi_config.distance.internal is defined and afi_config.distance.local is defined %}
distance bgp {{ afi_config.distance.external }} {{ afi_config.distance.internal }} {{ afi_config.distance.local }}
{% endif %}
{% if afi_config.distance.prefix is defined and afi_config.distance.prefix is not none %}
{% for prefix in afi_config.distance.prefix %}
distance {{ afi_config.distance.prefix[prefix].distance }} {{ prefix }}
{% endfor %}
{% endif %}
{% endif %}
{% if afi_config.export is defined and afi_config.export.vpn is defined %}
export vpn
{% endif %}
{% if afi_config.import is defined and afi_config.import is not none %}
{% if afi_config.import.vpn is defined %}
import vpn
{% endif %}
{% if afi_config.import.vrf is defined and afi_config.import.vrf is not none %}
{% for vrf in afi_config.import.vrf %}
import vrf {{ vrf }}
{% endfor %}
{% endif %}
{% endif %}
{% if afi_config.label is defined and afi_config.label.vpn is defined and afi_config.label.vpn.export is defined and afi_config.label.vpn.export is not none %}
label vpn export {{ afi_config.label.vpn.export }}
{% endif %}
{% if afi_config.local_install is defined and afi_config.local_install is not none %}
{% for interface in afi_config.local_install.interface %}
local-install {{ interface }}
{% endfor %}
{% endif %}
{% if afi_config.advertise_all_vni is defined %}
advertise-all-vni
{% endif %}
{% if afi_config.advertise_default_gw is defined %}
advertise-default-gw
{% endif %}
{% if afi_config.advertise_pip is defined and afi_config.advertise_pip is not none %}
advertise-pip ip {{ afi_config.advertise_pip }}
{% endif %}
{% if afi_config.advertise_svi_ip is defined %}
advertise-svi-ip
{% endif %}
{% if afi_config.rt_auto_derive is defined %}
autort rfc8365-compatible
{% endif %}
{% if afi_config.flooding is defined and afi_config.flooding.disable is defined %}
flooding disable
{% endif %}
{% if afi_config.flooding is defined and afi_config.flooding.head_end_replication is defined %}
flooding head-end-replication
{% endif %}
{% if afi_config.rd is defined and afi_config.rd.vpn is defined and afi_config.rd.vpn.export is defined %}
rd vpn export {{ afi_config.rd.vpn.export }}
{% endif %}
{% if afi_config.route_target is defined and afi_config.route_target is not none %}
{% if afi_config.route_target.vpn is defined and afi_config.route_target.vpn is not none %}
{% if afi_config.route_target.vpn.both is defined and afi_config.route_target.vpn.both is not none %}
route-target vpn both {{ afi_config.route_target.vpn.both }}
{% else %}
{% if afi_config.route_target.vpn.export is defined and afi_config.route_target.vpn.export is not none %}
route-target vpn export {{ afi_config.route_target.vpn.export }}
{% endif %}
{% if afi_config.route_target.vpn.import is defined and afi_config.route_target.vpn.import is not none %}
route-target vpn import {{ afi_config.route_target.vpn.import }}
{% endif %}
{% endif %}
{% endif %}
{% if afi_config.route_target.both is defined and afi_config.route_target.both is not none %}
route-target both {{ afi_config.route_target.both }}
{% else %}
{% if afi_config.route_target.export is defined and afi_config.route_target.export is not none %}
route-target export {{ afi_config.route_target.export }}
{% endif %}
{% if afi_config.route_target.import is defined and afi_config.route_target.import is not none %}
route-target import {{ afi_config.route_target.import }}
{% endif %}
{% endif %}
{% endif %}
{% if afi_config.route_map is defined and afi_config.route_map.vpn is defined and afi_config.route_map.vpn is not none %}
{% if afi_config.route_map.vpn.export is defined and afi_config.route_map.vpn.export is not none %}
route-map vpn export {{ afi_config.route_map.vpn.export }}
{% endif %}
{% if afi_config.route_map.vpn.import is defined and afi_config.route_map.vpn.import is not none %}
route-map vpn import {{ afi_config.route_map.vpn.import }}
{% endif %}
{% endif %}
{% if afi_config.vni is defined and afi_config.vni is not none %}
{% for vni, vni_config in afi_config.vni.items() %}
vni {{ vni }}
{% if vni_config.advertise_default_gw is defined %}
advertise-default-gw
{% endif %}
{% if vni_config.advertise_svi_ip is defined %}
advertise-svi-ip
{% endif %}
{% if vni_config.rd is defined and vni_config.rd is not none %}
rd {{ vni_config.rd }}
{% endif %}
{% if vni_config.route_target is defined and vni_config.route_target is not none %}
{% if vni_config.route_target.both is defined and vni_config.route_target.both is not none %}
route-target both {{ vni_config.route_target.both }}
{% endif %}
{% if vni_config.route_target.export is defined and vni_config.route_target.export is not none %}
route-target export {{ vni_config.route_target.export }}
{% endif %}
{% if vni_config.route_target.import is defined and vni_config.route_target.import is not none %}
route-target import {{ vni_config.route_target.import }}
{% endif %}
{% endif %}
exit-vni
{% endfor %}
{% endif %}
exit-address-family
{% endfor %}
{% endif %}
!
{% if peer_group is defined and peer_group is not none %}
{% for peer, config in peer_group.items() %}
{{ bgp_neighbor(peer, config, true) }}
{% endfor %}
{% endif %}
!
{% if neighbor is defined and neighbor is not none %}
{% for peer, config in neighbor.items() %}
{{ bgp_neighbor(peer, config) }}
{% endfor %}
{% endif %}
!
{% if listen is defined %}
{% if listen.limit is defined and listen.limit is not none %}
bgp listen limit {{ listen.limit }}
{% endif %}
{% for prefix, options in listen.range.items() %}
{% if options.peer_group is defined and options.peer_group is not none %}
bgp listen range {{ prefix }} peer-group {{ options.peer_group }}
{% endif %}
{% endfor %}
{% endif %}
{% if parameters is defined %}
{% if parameters.always_compare_med is defined %}
bgp always-compare-med
{% endif %}
{% if parameters.bestpath is defined and parameters.bestpath is not none %}
{% if parameters.bestpath.as_path is defined and parameters.bestpath.as_path is not none %}
{% for option in parameters.bestpath.as_path %}
{# replace is required for multipath-relax option #}
bgp bestpath as-path {{ option|replace('_', '-') }}
{% endfor %}
{% endif %}
{% if parameters.bestpath.bandwidth is defined and parameters.bestpath.bandwidth is not none %}
bgp bestpath bandwidth {{ parameters.bestpath.bandwidth }}
{% endif %}
{% if parameters.bestpath.compare_routerid is defined %}
bgp bestpath compare-routerid
{% endif %}
{% if parameters.bestpath.med is defined and parameters.bestpath.med is not none %}
bgp bestpath med {{ 'confed' if parameters.bestpath.med.confed is defined }} {{ 'missing-as-worst' if parameters.bestpath.med.missing_as_worst is defined }}
{% endif %}
{% endif %}
{% if parameters.cluster_id is defined and parameters.cluster_id is not none %}
bgp cluster-id {{ parameters.cluster_id }}
{% endif %}
{% if parameters.confederation is defined and parameters.confederation is not none %}
{% if parameters.confederation.identifier is defined and parameters.confederation.identifier is not none %}
bgp confederation identifier {{ parameters.confederation.identifier }}
{% endif %}
{% if parameters.confederation.peers is defined and parameters.confederation.peers is not none %}
bgp confederation peers {{ parameters.confederation.peers | join(' ') }}
{% endif %}
{% endif %}
{% if parameters.dampening is defined and parameters.dampening is defined and parameters.dampening.half_life is defined and parameters.dampening.half_life is not none %}
{# Doesn't work in current FRR configuration; vtysh (bgp dampening 16 751 2001 61) #}
bgp dampening {{ parameters.dampening.half_life }} {{ parameters.dampening.re_use if parameters.dampening.re_use is defined }} {{ parameters.dampening.start_suppress_time if parameters.dampening.start_suppress_time is defined }} {{ parameters.dampening.max_suppress_time if parameters.dampening.max_suppress_time is defined }}
{% endif %}
{% if parameters.default is defined and parameters.default is not none %}
{% if parameters.default.local_pref is defined and parameters.default.local_pref is not none %}
bgp default local-preference {{ parameters.default.local_pref }}
{% endif %}
{% endif %}
{% if parameters.deterministic_med is defined %}
bgp deterministic-med
{% endif %}
{% if parameters.distance is defined and parameters.distance is not none %}
{% if parameters.distance.global is defined and parameters.distance.global.external is defined and parameters.distance.global.internal is defined and parameters.distance.global.local is defined %}
distance bgp {{ parameters.distance.global.external }} {{ parameters.distance.global.internal }} {{ parameters.distance.global.local }}
{% endif %}
{% if parameters.distance.prefix is defined and parameters.distance.prefix is not none %}
{% for prefix in parameters.distance.prefix %}
distance {{ parameters.distance.prefix[prefix].distance }} {{ prefix }}
{% endfor %}
{% endif %}
{% endif %}
{% if parameters.graceful_restart is defined %}
bgp graceful-restart {{ 'stalepath-time ' ~ parameters.graceful_restart.stalepath_time if parameters.graceful_restart.stalepath_time is defined }}
{% endif %}
{% if parameters.graceful_shutdown is defined %}
bgp graceful-shutdown
{% endif %}
{% if parameters.log_neighbor_changes is defined %}
bgp log-neighbor-changes
{% endif %}
{% if parameters.network_import_check is defined %}
bgp network import-check
{% endif %}
{% if parameters.no_client_to_client_reflection is defined %}
no bgp client-to-client reflection
{% endif %}
{% if parameters.no_fast_external_failover is defined %}
no bgp fast-external-failover
{% endif %}
{% if parameters.router_id is defined and parameters.router_id is not none %}
bgp router-id {{ parameters.router_id }}
{% endif %}
{% endif %}
{% if timers is defined and timers.keepalive is defined and timers.holdtime is defined %}
timers bgp {{ timers.keepalive }} {{ timers.holdtime }}
{% endif %}
end
!
\ No newline at end of file
diff --git a/interface-definitions/include/bgp/neighbor-local-as.xml.i b/interface-definitions/include/bgp/neighbor-local-as.xml.i
index 28c6b72b6..8cf0167fd 100644
--- a/interface-definitions/include/bgp/neighbor-local-as.xml.i
+++ b/interface-definitions/include/bgp/neighbor-local-as.xml.i
@@ -1,22 +1,28 @@
<!-- include start from bgp/neighbor-local-as.xml.i -->
<tagNode name="local-as">
<properties>
- <help>Local AS number [REQUIRED]</help>
+ <help>Specify alternate ASN for this BGP process</help>
<valueHelp>
<format>u32:1-4294967294</format>
- <description>Local AS number</description>
+ <description>Autonomous System Number (ASN)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-4294967294"/>
</constraint>
</properties>
<children>
<leafNode name="no-prepend">
<properties>
- <help>Disable prepending local-as to updates from EBGP peers</help>
+ <help>Disable prepending local-as from/to updates for eBGP peers</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="replace-as">
+ <properties>
+ <help>Prepend only local-as from/to updates for eBGP peers</help>
<valueless/>
</properties>
</leafNode>
</children>
</tagNode>
<!-- include end -->
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index df9dc342b..05919abbc 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -1,759 +1,761 @@
#!/usr/bin/env python3
#
# Copyright (C) 2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.template import is_ipv6
from vyos.util import process_named_running
PROCESS_NAME = 'bgpd'
ASN = '64512'
base_path = ['protocols', 'bgp']
route_map_in = 'foo-map-in'
route_map_out = 'foo-map-out'
prefix_list_in = 'pfx-foo-in'
prefix_list_out = 'pfx-foo-out'
prefix_list_in6 = 'pfx-foo-in6'
prefix_list_out6 = 'pfx-foo-out6'
neighbor_config = {
'192.0.2.1' : {
'cap_dynamic' : '',
'cap_ext_next' : '',
'remote_as' : '100',
'adv_interv' : '400',
'passive' : '',
'password' : 'VyOS-Secure123',
'shutdown' : '',
'cap_over' : '',
'ttl_security' : '5',
'local_as' : '300',
'route_map_in' : route_map_in,
'route_map_out': route_map_out,
'no_send_comm_ext' : '',
'addpath_all' : '',
},
'192.0.2.2' : {
'remote_as' : '200',
'shutdown' : '',
'no_cap_nego' : '',
'port' : '667',
'cap_strict' : '',
'pfx_list_in' : prefix_list_in,
'pfx_list_out' : prefix_list_out,
'no_send_comm_std' : '',
},
'192.0.2.3' : {
'description' : 'foo bar baz',
'remote_as' : '200',
'passive' : '',
'multi_hop' : '5',
'update_src' : 'lo',
},
'2001:db8::1' : {
'cap_dynamic' : '',
'cap_ext_next' : '',
'remote_as' : '123',
'adv_interv' : '400',
'passive' : '',
'password' : 'VyOS-Secure123',
'shutdown' : '',
'cap_over' : '',
'ttl_security' : '5',
'local_as' : '300',
'solo' : '',
'route_map_in' : route_map_in,
'route_map_out': route_map_out,
'no_send_comm_std' : '',
'addpath_per_as' : '',
},
'2001:db8::2' : {
'remote_as' : '456',
'shutdown' : '',
'no_cap_nego' : '',
'port' : '667',
'cap_strict' : '',
'pfx_list_in' : prefix_list_in6,
'pfx_list_out' : prefix_list_out6,
'no_send_comm_ext' : '',
},
}
peer_group_config = {
'foo' : {
'remote_as' : '100',
'passive' : '',
'password' : 'VyOS-Secure123',
'shutdown' : '',
'cap_over' : '',
'ttl_security': '5',
},
'bar' : {
'description' : 'foo peer bar group',
'remote_as' : '200',
'shutdown' : '',
'no_cap_nego' : '',
'local_as' : '300',
'pfx_list_in' : prefix_list_in,
'pfx_list_out' : prefix_list_out,
'no_send_comm_ext' : '',
},
'baz' : {
'cap_dynamic' : '',
'cap_ext_next' : '',
'remote_as' : '200',
'passive' : '',
'multi_hop' : '5',
'update_src' : 'lo',
'route_map_in' : route_map_in,
'route_map_out': route_map_out,
},
}
class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
def setUp(self):
self.cli_set(['policy', 'route-map', route_map_in, 'rule', '10', 'action', 'permit'])
self.cli_set(['policy', 'route-map', route_map_out, 'rule', '10', 'action', 'permit'])
self.cli_set(['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'action', 'permit'])
self.cli_set(['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'prefix', '192.0.2.0/25'])
self.cli_set(['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'action', 'permit'])
self.cli_set(['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'prefix', '192.0.2.128/25'])
self.cli_set(['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'action', 'permit'])
self.cli_set(['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'prefix', '2001:db8:1000::/64'])
self.cli_set(['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'action', 'deny'])
self.cli_set(['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'prefix', '2001:db8:2000::/64'])
self.cli_set(base_path + ['local-as', ASN])
def tearDown(self):
self.cli_delete(['policy'])
self.cli_delete(['vrf'])
self.cli_delete(base_path)
self.cli_commit()
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
def verify_frr_config(self, peer, peer_config, frrconfig):
# recurring patterns to verify for both a simple neighbor and a peer-group
if 'cap_dynamic' in peer_config:
self.assertIn(f' neighbor {peer} capability dynamic', frrconfig)
if 'cap_ext_next' in peer_config:
self.assertIn(f' neighbor {peer} capability extended-nexthop', frrconfig)
if 'description' in peer_config:
self.assertIn(f' neighbor {peer} description {peer_config["description"]}', frrconfig)
if 'no_cap_nego' in peer_config:
self.assertIn(f' neighbor {peer} dont-capability-negotiate', frrconfig)
if 'multi_hop' in peer_config:
self.assertIn(f' neighbor {peer} ebgp-multihop {peer_config["multi_hop"]}', frrconfig)
if 'local_as' in peer_config:
- self.assertIn(f' neighbor {peer} local-as {peer_config["local_as"]}', frrconfig)
+ self.assertIn(f' neighbor {peer} local-as {peer_config["local_as"]} no-prepend replace-as', frrconfig)
if 'cap_over' in peer_config:
self.assertIn(f' neighbor {peer} override-capability', frrconfig)
if 'passive' in peer_config:
self.assertIn(f' neighbor {peer} passive', frrconfig)
if 'password' in peer_config:
self.assertIn(f' neighbor {peer} password {peer_config["password"]}', frrconfig)
if 'remote_as' in peer_config:
self.assertIn(f' neighbor {peer} remote-as {peer_config["remote_as"]}', frrconfig)
if 'solo' in peer_config:
self.assertIn(f' neighbor {peer} solo', frrconfig)
if 'shutdown' in peer_config:
self.assertIn(f' neighbor {peer} shutdown', frrconfig)
if 'ttl_security' in peer_config:
self.assertIn(f' neighbor {peer} ttl-security hops {peer_config["ttl_security"]}', frrconfig)
if 'update_src' in peer_config:
self.assertIn(f' neighbor {peer} update-source {peer_config["update_src"]}', frrconfig)
if 'route_map_in' in peer_config:
self.assertIn(f' neighbor {peer} route-map {peer_config["route_map_in"]} in', frrconfig)
if 'route_map_out' in peer_config:
self.assertIn(f' neighbor {peer} route-map {peer_config["route_map_out"]} out', frrconfig)
if 'pfx_list_in' in peer_config:
self.assertIn(f' neighbor {peer} prefix-list {peer_config["pfx_list_in"]} in', frrconfig)
if 'pfx_list_out' in peer_config:
self.assertIn(f' neighbor {peer} prefix-list {peer_config["pfx_list_out"]} out', frrconfig)
if 'no_send_comm_std' in peer_config:
self.assertIn(f' no neighbor {peer} send-community', frrconfig)
if 'no_send_comm_ext' in peer_config:
self.assertIn(f' no neighbor {peer} send-community extended', frrconfig)
if 'addpath_all' in peer_config:
self.assertIn(f' neighbor {peer} addpath-tx-all-paths', frrconfig)
if 'addpath_per_as' in peer_config:
self.assertIn(f' neighbor {peer} addpath-tx-bestpath-per-AS', frrconfig)
def test_bgp_01_simple(self):
router_id = '127.0.0.1'
local_pref = '500'
stalepath_time = '60'
max_path_v4 = '2'
max_path_v4ibgp = '4'
max_path_v6 = '8'
max_path_v6ibgp = '16'
self.cli_set(base_path + ['parameters', 'router-id', router_id])
self.cli_set(base_path + ['parameters', 'log-neighbor-changes'])
# Local AS number MUST be defined - 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])
# Default local preference (higher = more preferred, default value is 100)
self.cli_set(base_path + ['parameters', 'default', 'local-pref', local_pref])
# Deactivate IPv4 unicast for a peer by default
self.cli_set(base_path + ['parameters', 'default', 'no-ipv4-unicast'])
self.cli_set(base_path + ['parameters', 'graceful-restart', 'stalepath-time', stalepath_time])
self.cli_set(base_path + ['parameters', 'graceful-shutdown'])
self.cli_set(base_path + ['parameters', 'ebgp-requires-policy'])
self.cli_set(base_path + ['parameters', 'bestpath', 'as-path', 'multipath-relax'])
self.cli_set(base_path + ['parameters', 'bestpath', 'bandwidth', 'default-weight-for-missing'])
self.cli_set(base_path + ['parameters', 'bestpath', 'compare-routerid'])
# AFI maximum path support
self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ebgp', max_path_v4])
self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ibgp', max_path_v4ibgp])
self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'maximum-paths', 'ebgp', max_path_v6])
self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'maximum-paths', 'ibgp', max_path_v6ibgp])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' bgp router-id {router_id}', frrconfig)
self.assertIn(f' bgp log-neighbor-changes', frrconfig)
self.assertIn(f' bgp default local-preference {local_pref}', frrconfig)
self.assertIn(f' no bgp default ipv4-unicast', frrconfig)
self.assertIn(f' bgp graceful-restart stalepath-time {stalepath_time}', frrconfig)
self.assertIn(f' bgp graceful-shutdown', frrconfig)
self.assertIn(f' bgp bestpath as-path multipath-relax', frrconfig)
self.assertIn(f' bgp bestpath bandwidth default-weight-for-missing', frrconfig)
self.assertIn(f' bgp bestpath compare-routerid', frrconfig)
self.assertNotIn(f'bgp ebgp-requires-policy', frrconfig)
afiv4_config = self.getFRRconfig(' address-family ipv4 unicast')
self.assertIn(f' maximum-paths {max_path_v4}', afiv4_config)
self.assertIn(f' maximum-paths ibgp {max_path_v4ibgp}', afiv4_config)
afiv6_config = self.getFRRconfig(' address-family ipv6 unicast')
self.assertIn(f' maximum-paths {max_path_v6}', afiv6_config)
self.assertIn(f' maximum-paths ibgp {max_path_v6ibgp}', afiv6_config)
def test_bgp_02_neighbors(self):
# 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():
afi = 'ipv4-unicast'
if is_ipv6(peer):
afi = 'ipv6-unicast'
if 'adv_interv' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'advertisement-interval', peer_config["adv_interv"]])
if 'cap_dynamic' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'capability', 'dynamic'])
if 'cap_ext_next' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'capability', 'extended-nexthop'])
if 'description' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'description', peer_config["description"]])
if 'no_cap_nego' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'disable-capability-negotiation'])
if 'multi_hop' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'ebgp-multihop', peer_config["multi_hop"]])
if 'local_as' in peer_config:
- self.cli_set(base_path + ['neighbor', peer, 'local-as', peer_config["local_as"]])
+ self.cli_set(base_path + ['neighbor', peer, 'local-as', peer_config["local_as"], 'no-prepend'])
+ self.cli_set(base_path + ['neighbor', peer, 'local-as', peer_config["local_as"], 'replace-as'])
if 'cap_over' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'override-capability'])
if 'passive' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'passive'])
if 'password' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'password', peer_config["password"]])
if 'port' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'port', peer_config["port"]])
if 'remote_as' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'remote-as', peer_config["remote_as"]])
if 'cap_strict' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'strict-capability-match'])
if 'shutdown' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'shutdown'])
if 'solo' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'solo'])
if 'ttl_security' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'ttl-security', 'hops', peer_config["ttl_security"]])
if 'update_src' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'update-source', peer_config["update_src"]])
if 'route_map_in' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'route-map', 'import', peer_config["route_map_in"]])
if 'route_map_out' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'route-map', 'export', peer_config["route_map_out"]])
if 'pfx_list_in' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'prefix-list', 'import', peer_config["pfx_list_in"]])
if 'pfx_list_out' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'prefix-list', 'export', peer_config["pfx_list_out"]])
if 'no_send_comm_std' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'disable-send-community', 'standard'])
if 'no_send_comm_ext' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'disable-send-community', 'extended'])
if 'addpath_all' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-all'])
if 'addpath_per_as' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-per-as'])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}')
self.assertIn(f'router bgp {ASN}', frrconfig)
for peer, peer_config in neighbor_config.items():
if 'adv_interv' in peer_config:
self.assertIn(f' neighbor {peer} advertisement-interval {peer_config["adv_interv"]}', frrconfig)
if 'port' in peer_config:
self.assertIn(f' neighbor {peer} port {peer_config["port"]}', frrconfig)
if 'cap_strict' in peer_config:
self.assertIn(f' neighbor {peer} strict-capability-match', frrconfig)
self.verify_frr_config(peer, peer_config, frrconfig)
def test_bgp_03_peer_groups(self):
# Test out individual peer-group configuration items
for peer_group, config in peer_group_config.items():
if 'cap_dynamic' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'capability', 'dynamic'])
if 'cap_ext_next' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'capability', 'extended-nexthop'])
if 'description' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'description', config["description"]])
if 'no_cap_nego' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'disable-capability-negotiation'])
if 'multi_hop' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'ebgp-multihop', config["multi_hop"]])
if 'local_as' in config:
- self.cli_set(base_path + ['peer-group', peer_group, 'local-as', config["local_as"]])
+ self.cli_set(base_path + ['peer-group', peer_group, 'local-as', config["local_as"], 'no-prepend'])
+ self.cli_set(base_path + ['peer-group', peer_group, 'local-as', config["local_as"], 'replace-as'])
if 'cap_over' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'override-capability'])
if 'passive' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'passive'])
if 'password' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'password', config["password"]])
if 'remote_as' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'remote-as', config["remote_as"]])
if 'shutdown' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'shutdown'])
if 'ttl_security' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'ttl-security', 'hops', config["ttl_security"]])
if 'update_src' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'update-source', config["update_src"]])
if 'route_map_in' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'route-map', 'import', config["route_map_in"]])
if 'route_map_out' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'route-map', 'export', config["route_map_out"]])
if 'pfx_list_in' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'prefix-list', 'import', config["pfx_list_in"]])
if 'pfx_list_out' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'prefix-list', 'export', config["pfx_list_out"]])
if 'no_send_comm_std' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'disable-send-community', 'standard'])
if 'no_send_comm_ext' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'disable-send-community', 'extended'])
if 'addpath_all' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-all'])
if 'addpath_per_as' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-per-as'])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}')
self.assertIn(f'router bgp {ASN}', frrconfig)
for peer, peer_config in peer_group_config.items():
self.assertIn(f' neighbor {peer_group} peer-group', frrconfig)
self.verify_frr_config(peer, peer_config, frrconfig)
def test_bgp_04_afi_ipv4(self):
networks = {
'10.0.0.0/8' : {
'as_set' : '',
},
'100.64.0.0/10' : {
'as_set' : '',
},
'192.168.0.0/16' : {
'summary_only' : '',
},
}
# We want to redistribute ...
redistributes = ['connected', 'isis', 'kernel', 'ospf', 'rip', 'static']
for redistribute in redistributes:
self.cli_set(base_path + ['address-family', 'ipv4-unicast',
'redistribute', redistribute])
for network, network_config in networks.items():
self.cli_set(base_path + ['address-family', 'ipv4-unicast',
'network', network])
if 'as_set' in network_config:
self.cli_set(base_path + ['address-family', 'ipv4-unicast',
'aggregate-address', network, 'as-set'])
if 'summary_only' in network_config:
self.cli_set(base_path + ['address-family', 'ipv4-unicast',
'aggregate-address', network, 'summary-only'])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' address-family ipv4 unicast', frrconfig)
for redistribute in redistributes:
self.assertIn(f' redistribute {redistribute}', frrconfig)
for network, network_config in networks.items():
self.assertIn(f' network {network}', frrconfig)
if 'as_set' in network_config:
self.assertIn(f' aggregate-address {network} as-set', frrconfig)
if 'summary_only' in network_config:
self.assertIn(f' aggregate-address {network} summary-only', frrconfig)
def test_bgp_05_afi_ipv6(self):
networks = {
'2001:db8:100::/48' : {
},
'2001:db8:200::/48' : {
},
'2001:db8:300::/48' : {
'summary_only' : '',
},
}
# We want to redistribute ...
redistributes = ['connected', 'kernel', 'ospfv3', 'ripng', 'static']
for redistribute in redistributes:
self.cli_set(base_path + ['address-family', 'ipv6-unicast',
'redistribute', redistribute])
for network, network_config in networks.items():
self.cli_set(base_path + ['address-family', 'ipv6-unicast',
'network', network])
if 'summary_only' in network_config:
self.cli_set(base_path + ['address-family', 'ipv6-unicast',
'aggregate-address', network, 'summary-only'])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' address-family ipv6 unicast', frrconfig)
# T2100: By default ebgp-requires-policy is disabled to keep VyOS
# 1.3 and 1.2 backwards compatibility
self.assertIn(f' no bgp ebgp-requires-policy', frrconfig)
for redistribute in redistributes:
# FRR calls this OSPF6
if redistribute == 'ospfv3':
redistribute = 'ospf6'
self.assertIn(f' redistribute {redistribute}', frrconfig)
for network, network_config in networks.items():
self.assertIn(f' network {network}', frrconfig)
if 'as_set' in network_config:
self.assertIn(f' aggregate-address {network} summary-only', frrconfig)
def test_bgp_06_listen_range(self):
# Implemented via T1875
limit = '64'
listen_ranges = ['192.0.2.0/25', '192.0.2.128/25']
peer_group = 'listenfoobar'
self.cli_set(base_path + ['listen', 'limit', limit])
for prefix in listen_ranges:
self.cli_set(base_path + ['listen', 'range', prefix])
# check validate() - peer-group must be defined for range/prefix
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['listen', 'range', prefix, 'peer-group', peer_group])
# check validate() - peer-group does yet not exist!
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['peer-group', peer_group, 'remote-as', ASN])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' neighbor {peer_group} peer-group', frrconfig)
self.assertIn(f' neighbor {peer_group} remote-as {ASN}', frrconfig)
self.assertIn(f' bgp listen limit {limit}', frrconfig)
for prefix in listen_ranges:
self.assertIn(f' bgp listen range {prefix} peer-group {peer_group}', frrconfig)
def test_bgp_07_l2vpn_evpn(self):
vnis = ['10010', '10020', '10030']
neighbors = ['192.0.2.10', '192.0.2.20', '192.0.2.30']
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-all-vni'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-default-gw'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-svi-ip'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'flooding', 'disable'])
for vni in vnis:
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'vni', vni, 'advertise-default-gw'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'vni', vni, 'advertise-svi-ip'])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' address-family l2vpn evpn', frrconfig)
self.assertIn(f' advertise-all-vni', frrconfig)
self.assertIn(f' advertise-default-gw', frrconfig)
self.assertIn(f' advertise-svi-ip', frrconfig)
self.assertIn(f' flooding disable', frrconfig)
for vni in vnis:
vniconfig = self.getFRRconfig(f' vni {vni}')
self.assertIn(f'vni {vni}', vniconfig)
self.assertIn(f' advertise-default-gw', vniconfig)
self.assertIn(f' advertise-svi-ip', vniconfig)
def test_bgp_08_zebra_route_map(self):
# Implemented because of T3328
self.cli_set(base_path + ['route-map', route_map_in])
# commit changes
self.cli_commit()
# Verify FRR configuration
zebra_route_map = f'ip protocol bgp route-map {route_map_in}'
frrconfig = self.getFRRconfig(zebra_route_map)
self.assertIn(zebra_route_map, frrconfig)
# Remove the route-map again
self.cli_delete(base_path + ['route-map'])
# commit changes
self.cli_commit()
# Verify FRR configuration
frrconfig = self.getFRRconfig(zebra_route_map)
self.assertNotIn(zebra_route_map, frrconfig)
def test_bgp_09_distance_and_flowspec(self):
distance_external = '25'
distance_internal = '30'
distance_local = '35'
distance_v4_prefix = '169.254.0.0/32'
distance_v6_prefix = '2001::/128'
distance_prefix_value = '110'
distance_families = ['ipv4-unicast', 'ipv6-unicast','ipv4-multicast', 'ipv6-multicast']
verify_families = ['ipv4 unicast', 'ipv6 unicast','ipv4 multicast', 'ipv6 multicast']
flowspec_families = ['address-family ipv4 flowspec', 'address-family ipv6 flowspec']
flowspec_int = 'lo'
# Per family distance support
for family in distance_families:
self.cli_set(base_path + ['address-family', family, 'distance', 'external', distance_external])
self.cli_set(base_path + ['address-family', family, 'distance', 'internal', distance_internal])
self.cli_set(base_path + ['address-family', family, 'distance', 'local', distance_local])
if 'ipv4' in family:
self.cli_set(base_path + ['address-family', family, 'distance',
'prefix', distance_v4_prefix, 'distance', distance_prefix_value])
if 'ipv6' in family:
self.cli_set(base_path + ['address-family', family, 'distance',
'prefix', distance_v6_prefix, 'distance', distance_prefix_value])
# IPv4 flowspec interface check
self.cli_set(base_path + ['address-family', 'ipv4-flowspec', 'local-install', 'interface', flowspec_int])
# IPv6 flowspec interface check
self.cli_set(base_path + ['address-family', 'ipv6-flowspec', 'local-install', 'interface', flowspec_int])
# Commit changes
self.cli_commit()
# Verify FRR distances configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}')
self.assertIn(f'router bgp {ASN}', frrconfig)
for family in verify_families:
self.assertIn(f'address-family {family}', frrconfig)
self.assertIn(f'distance bgp {distance_external} {distance_internal} {distance_local}', frrconfig)
if 'ipv4' in family:
self.assertIn(f'distance {distance_prefix_value} {distance_v4_prefix}', frrconfig)
if 'ipv6' in family:
self.assertIn(f'distance {distance_prefix_value} {distance_v6_prefix}', frrconfig)
# Verify FRR flowspec configuration
for family in flowspec_families:
self.assertIn(f'{family}', frrconfig)
self.assertIn(f'local-install {flowspec_int}', frrconfig)
def test_bgp_10_vrf_simple(self):
router_id = '127.0.0.3'
vrfs = ['red', 'green', 'blue']
# It is safe to assume that when the basic VRF test works, all
# other BGP related features work, as we entirely inherit the CLI
# templates and Jinja2 FRR template.
table = '1000'
self.cli_set(base_path + ['local-as', ASN])
# testing only one AFI is sufficient as it's generic code
for vrf in vrfs:
vrf_base = ['vrf', 'name', vrf]
self.cli_set(vrf_base + ['table', table])
self.cli_set(vrf_base + ['protocols', 'bgp', 'local-as', ASN])
self.cli_set(vrf_base + ['protocols', 'bgp', 'parameters', 'router-id', router_id])
self.cli_set(vrf_base + ['protocols', 'bgp', 'route-map', route_map_in])
table = str(int(table) + 1000)
# import VRF routes do main RIB
self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'import', 'vrf', vrf])
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' address-family ipv6 unicast', frrconfig)
for vrf in vrfs:
self.assertIn(f' import vrf {vrf}', frrconfig)
# Verify FRR bgpd configuration
frr_vrf_config = self.getFRRconfig(f'router bgp {ASN} vrf {vrf}')
self.assertIn(f'router bgp {ASN} vrf {vrf}', frr_vrf_config)
self.assertIn(f' bgp router-id {router_id}', frr_vrf_config)
# XXX: Currently this is not working as FRR() class does not support
# route-maps for multiple vrfs because the modify_section() only works
# on lines and not text blocks.
#
# vrfconfig = self.getFRRconfig(f'vrf {vrf}')
# zebra_route_map = f' ip protocol bgp route-map {route_map_in}'
# self.assertIn(zebra_route_map, vrfconfig)
def test_bgp_11_confederation(self):
router_id = '127.10.10.2'
confed_id = str(int(ASN) + 1)
confed_asns = '10 20 30 40'
self.cli_set(base_path + ['local-as', ASN])
self.cli_set(base_path + ['parameters', 'router-id', router_id])
self.cli_set(base_path + ['parameters', 'confederation', 'identifier', confed_id])
for asn in confed_asns.split():
self.cli_set(base_path + ['parameters', 'confederation', 'peers', asn])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' bgp router-id {router_id}', frrconfig)
self.assertIn(f' bgp confederation identifier {confed_id}', frrconfig)
self.assertIn(f' bgp confederation peers {confed_asns}', frrconfig)
def test_bgp_12_v6_link_local(self):
remote_asn = str(int(ASN) + 10)
interface = 'eth0'
self.cli_set(base_path + ['local-as', ASN])
self.cli_set(base_path + ['neighbor', interface, 'address-family', 'ipv6-unicast'])
self.cli_set(base_path + ['neighbor', interface, 'interface', 'v6only', 'remote-as', remote_asn])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' neighbor {interface} interface v6only remote-as {remote_asn}', frrconfig)
self.assertIn(f' address-family ipv6 unicast', frrconfig)
self.assertIn(f' neighbor {interface} activate', frrconfig)
self.assertIn(f' exit-address-family', frrconfig)
def test_bgp_13_vpn(self):
remote_asn = str(int(ASN) + 150)
neighbor = '192.0.2.55'
vrf_name = 'red'
label = 'auto'
rd = f'{neighbor}:{ASN}'
rt_export = f'{neighbor}:1002 1.2.3.4:567'
rt_import = f'{neighbor}:1003 500:100'
self.cli_set(base_path + ['local-as', ASN])
# testing only one AFI is sufficient as it's generic code
for afi in ['ipv4-unicast', 'ipv6-unicast']:
self.cli_set(base_path + ['address-family', afi, 'export', 'vpn'])
self.cli_set(base_path + ['address-family', afi, 'import', 'vpn'])
self.cli_set(base_path + ['address-family', afi, 'label', 'vpn', 'export', label])
self.cli_set(base_path + ['address-family', afi, 'rd', 'vpn', 'export', rd])
self.cli_set(base_path + ['address-family', afi, 'route-map', 'vpn', 'export', route_map_out])
self.cli_set(base_path + ['address-family', afi, 'route-map', 'vpn', 'import', route_map_in])
self.cli_set(base_path + ['address-family', afi, 'route-target', 'vpn', 'export', rt_export])
self.cli_set(base_path + ['address-family', afi, 'route-target', 'vpn', 'import', rt_import])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}')
self.assertIn(f'router bgp {ASN}', frrconfig)
for afi in ['ipv4', 'ipv6']:
afi_config = self.getFRRconfig(f' address-family {afi} unicast', endsection='exit-address-family', daemon='bgpd')
self.assertIn(f'address-family {afi} unicast', afi_config)
self.assertIn(f' export vpn', afi_config)
self.assertIn(f' import vpn', afi_config)
self.assertIn(f' label vpn export {label}', afi_config)
self.assertIn(f' rd vpn export {rd}', afi_config)
self.assertIn(f' route-map vpn export {route_map_out}', afi_config)
self.assertIn(f' route-map vpn import {route_map_in}', afi_config)
self.assertIn(f' rt vpn export {rt_export}', afi_config)
self.assertIn(f' rt vpn import {rt_import}', afi_config)
self.assertIn(f' exit-address-family', afi_config)
if __name__ == '__main__':
unittest.main(verbosity=2)
\ No newline at end of file
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index 7d05eed9f..e24fcef14 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -1,318 +1,318 @@
#!/usr/bin/env python3
#
# Copyright (C) 2020-2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from sys import exit
from sys import argv
from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configverify import verify_prefix_list
from vyos.configverify import verify_route_map
from vyos.configverify import verify_vrf
from vyos.template import is_ip
from vyos.template import is_interface
from vyos.template import render_to_string
from vyos.util import dict_search
from vyos.validate import is_addr_assigned
from vyos import ConfigError
from vyos import frr
from vyos import airbag
airbag.enable()
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
vrf = None
if len(argv) > 1:
vrf = argv[1]
base_path = ['protocols', 'bgp']
# eqivalent of the C foo ? 'a' : 'b' statement
base = vrf and ['vrf', 'name', vrf, 'protocols', 'bgp'] or base_path
bgp = conf.get_config_dict(base, key_mangling=('-', '_'),
get_first_key=True, no_tag_node_value_mangle=True)
# Assign the name of our VRF context. This MUST be done before the return
# statement below, else on deletion we will delete the default instance
# instead of the VRF instance.
if vrf: bgp.update({'vrf' : vrf})
if not conf.exists(base):
bgp.update({'deleted' : ''})
if not vrf:
# We are running in the default VRF context, thus we can not delete
# our main BGP instance if there are dependent BGP VRF instances.
bgp['dependent_vrfs'] = conf.get_config_dict(['vrf', 'name'],
key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)
return bgp
# 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
def verify_remote_as(peer_config, bgp_config):
if 'remote_as' in peer_config:
return peer_config['remote_as']
if 'peer_group' in peer_config:
peer_group_name = peer_config['peer_group']
tmp = dict_search(f'peer_group.{peer_group_name}.remote_as', bgp_config)
if tmp: return tmp
if 'interface' in peer_config:
if 'remote_as' in peer_config['interface']:
return peer_config['interface']['remote_as']
if 'peer_group' in peer_config['interface']:
peer_group_name = peer_config['interface']['peer_group']
tmp = dict_search(f'peer_group.{peer_group_name}.remote_as', bgp_config)
if tmp: return tmp
if 'v6only' in peer_config['interface']:
if 'remote_as' in peer_config['interface']['v6only']:
return peer_config['interface']['v6only']['remote_as']
return None
def verify(bgp):
if not bgp or 'deleted' in bgp:
if 'dependent_vrfs' in bgp:
for vrf, vrf_options in bgp['dependent_vrfs'].items():
if dict_search('protocols.bgp', vrf_options) != None:
raise ConfigError('Cannot delete default BGP instance, ' \
'dependent VRF instance(s) exist!')
return None
if 'local_as' not in bgp:
raise ConfigError('BGP local-as number must be defined!')
# Common verification for both peer-group and neighbor statements
for neighbor in ['neighbor', 'peer_group']:
# bail out early if there is no neighbor or peer-group statement
# this also saves one indention level
if neighbor not in bgp:
continue
for peer, peer_config in bgp[neighbor].items():
# Only regular "neighbor" statement can have a peer-group set
# Check if the configure peer-group exists
if 'peer_group' in peer_config:
peer_group = peer_config['peer_group']
if 'peer_group' not in bgp or peer_group not in bgp['peer_group']:
raise ConfigError(f'Specified peer-group "{peer_group}" for '\
f'neighbor "{neighbor}" does not exist!')
if 'local_as' in peer_config:
if len(peer_config['local_as']) > 1:
- raise ConfigError('Only one local-as number may be specified!')
+ raise ConfigError(f'Only one local-as number can be specified for peer "{peer}"!')
# Neighbor local-as override can not be the same as the local-as
# we use for this BGP instane!
asn = list(peer_config['local_as'].keys())[0]
if asn == bgp['local_as']:
raise ConfigError('Cannot have local-as same as BGP AS number')
# ttl-security and ebgp-multihop can't be used in the same configration
if 'ebgp_multihop' in peer_config and 'ttl_security' in peer_config:
raise ConfigError('You can\'t set both ebgp-multihop and ttl-security hops')
# Check if neighbor has both override capability and strict capability match configured at the same time.
if 'override_capability' in peer_config and 'strict_capability_match' in peer_config:
raise ConfigError(f'Neighbor "{peer}" cannot have both override-capability and strict-capability-match configured at the same time!')
# Check spaces in the password
if 'password' in peer_config and ' ' in peer_config['password']:
raise ConfigError('You can\'t use spaces in the password')
# Some checks can/must only be done on a neighbor and not a peer-group
if neighbor == 'neighbor':
# remote-as must be either set explicitly for the neighbor
# or for the entire peer-group
if not verify_remote_as(peer_config, bgp):
raise ConfigError(f'Neighbor "{peer}" remote-as must be set!')
# Only checks for ipv4 and ipv6 neighbors
# Check if neighbor address is assigned as system interface address
if is_ip(peer) and is_addr_assigned(peer):
raise ConfigError(f'Can 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', 'ipv4_multicast', 'ipv4_labeled_unicast', 'ipv4_flowspec',
'ipv6_unicast', 'ipv6_multicast', 'ipv6_labeled_unicast', 'ipv6_flowspec',
'l2vpn_evpn']:
# Bail out early if address family is not configured
if 'address_family' not in peer_config or afi not in peer_config['address_family']:
continue
# Check if neighbor has both ipv4 unicast and ipv4 labeled unicast configured at the same time.
if 'ipv4_unicast' in peer_config['address_family'] and 'ipv4_labeled_unicast' in peer_config['address_family']:
raise ConfigError(f'Neighbor "{peer}" cannot have both ipv4-unicast and ipv4-labeled-unicast configured at the same time!')
# Check if neighbor has both ipv6 unicast and ipv6 labeled unicast configured at the same time.
if 'ipv6_unicast' in peer_config['address_family'] and 'ipv6_labeled_unicast' in peer_config['address_family']:
raise ConfigError(f'Neighbor "{peer}" cannot have both ipv6-unicast and ipv6-labeled-unicast configured at the same time!')
afi_config = peer_config['address_family'][afi]
# Validate if configured Prefix list exists
if 'prefix_list' in afi_config:
for tmp in ['import', 'export']:
if tmp not in afi_config['prefix_list']:
# bail out early
continue
if afi == 'ipv4_unicast':
verify_prefix_list(afi_config['prefix_list'][tmp], bgp)
elif afi == 'ipv6_unicast':
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']:
verify_route_map(afi_config['route_map'][tmp], bgp)
if 'route_reflector_client' in afi_config:
if 'remote_as' in peer_config and peer_config['remote_as'] != 'internal' and peer_config['remote_as'] != bgp['local_as']:
raise ConfigError('route-reflector-client only supported for iBGP peers')
else:
if 'peer_group' in peer_config:
peer_group_as = dict_search(f'peer_group.{peer_group}.remote_as', bgp)
if peer_group_as != None and peer_group_as != 'internal' and peer_group_as != bgp['local_as']:
raise ConfigError('route-reflector-client only supported for iBGP peers')
# Throw an error if a peer group is not configured for allow range
for prefix in dict_search('listen.range', bgp) or []:
# we can not use dict_search() here as prefix contains dots ...
if 'peer_group' not in bgp['listen']['range'][prefix]:
raise ConfigError(f'Listen range for prefix "{prefix}" has no peer group configured.')
peer_group = bgp['listen']['range'][prefix]['peer_group']
if 'peer_group' not in bgp or peer_group not in bgp['peer_group']:
raise ConfigError(f'Peer-group "{peer_group}" for listen range "{prefix}" does not exist!')
if not verify_remote_as(bgp['listen']['range'][prefix], bgp):
raise ConfigError(f'Peer-group "{peer_group}" requires remote-as to be set!')
# Throw an error if the global administrative distance parameters aren't all filled out.
if dict_search('parameters.distance.global', bgp) != None:
for key in ['external', 'internal', 'local']:
if dict_search(f'parameters.distance.global.{key}', bgp) == None:
raise ConfigError('Missing mandatory configuration option for '\
f'global administrative distance {key}!')
# Address Family specific validation
if 'address_family' in bgp:
for afi, afi_config in bgp['address_family'].items():
if 'distance' in afi_config:
# Throw an error if the address family specific administrative
# distance parameters aren't all filled out.
for key in ['external', 'internal', 'local']:
if key not in afi_config['distance']:
raise ConfigError('Missing mandatory configuration option for '\
f'{afi} administrative distance {key}!')
if afi in ['ipv4_unicast', 'ipv6_unicast']:
if 'import' in afi_config and 'vrf' in afi_config['import']:
# Check if VRF exists
verify_vrf(afi_config['import']['vrf'])
# FRR error: please unconfigure vpn to vrf commands before
# using import vrf commands
if 'vpn' in afi_config['import'] or dict_search('export.vpn', afi_config) != None:
raise ConfigError('Please unconfigure VPN to VRF commands before '\
'using "import vrf" commands!')
# Verify that the export/import route-maps do exist
for export_import in ['export', 'import']:
tmp = dict_search(f'route_map.vpn.{export_import}', afi_config)
if tmp: verify_route_map(tmp, bgp)
if afi in ['l2vpn_evpn'] and 'vrf' not in bgp:
# Some L2VPN EVPN AFI options are only supported under VRF
if 'vni' in afi_config:
for vni, vni_config in afi_config['vni'].items():
if 'rd' in vni_config:
raise ConfigError('VNI route-distinguisher is only supported under EVPN VRF')
if 'route_target' in vni_config:
raise ConfigError('VNI route-target is only supported under EVPN VRF')
return None
def generate(bgp):
if not bgp or 'deleted' in bgp:
bgp['frr_bgpd_config'] = ''
bgp['frr_zebra_config'] = ''
return None
bgp['protocol'] = 'bgp' # required for frr/vrf.route-map.frr.tmpl
bgp['frr_zebra_config'] = render_to_string('frr/vrf.route-map.frr.tmpl', bgp)
bgp['frr_bgpd_config'] = render_to_string('frr/bgpd.frr.tmpl', bgp)
return None
def apply(bgp):
bgp_daemon = 'bgpd'
zebra_daemon = 'zebra'
# Save original configuration prior to starting any commit actions
frr_cfg = frr.FRRConfig()
# 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'(\s+)?ip protocol bgp route-map [-a-zA-Z0-9.]+$', '', '(\s|!)')
frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bgp['frr_zebra_config'])
frr_cfg.commit_configuration(zebra_daemon)
# Generate empty helper string which can be ammended to FRR commands, it
# will be either empty (default VRF) or contain the "vrf <name" statement
vrf = ''
if 'vrf' in bgp:
vrf = ' vrf ' + bgp['vrf']
frr_cfg.load_configuration(bgp_daemon)
frr_cfg.modify_section(f'^router bgp \d+{vrf}$', '')
frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bgp['frr_bgpd_config'])
frr_cfg.commit_configuration(bgp_daemon)
# Save configuration to /run/frr/config/frr.conf
frr.save_configuration()
return None
if __name__ == '__main__':
try:
c = get_config()
verify(c)
generate(c)
apply(c)
except ConfigError as e:
print(e)
exit(1)

File Metadata

Mime Type
text/x-diff
Expires
Sat, Sep 26, 9:38 AM (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4284815
Default Alt Text
(80 KB)

Event Timeline