Not sure if it works or if the issue has been fixed in Cumulus 5.14. Doesn’t harm so let’s leave it. See: https://docs.nvidia.com/networking-ethernet-software/cumulus-linux-514/Whats-New/rn/#4548512
		
			
				
	
	
		
			478 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Django/Jinja
		
	
	
	
	
	
			
		
		
	
	
			478 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Django/Jinja
		
	
	
	
	
	
| {% set lo_address = interfaces | selectattr('name', '==', 'lo')
 | |
|     | map(attribute='ip_addresses') | first
 | |
|     | selectattr('role') | selectattr('role.value', '==', 'loopback')
 | |
|     | map(attribute='address') %}
 | |
| {% set inside_vrfs = interfaces | selectattr('parent') | selectattr('parent.name', '==', 'bridge')
 | |
|     | selectattr('vrf') | map(attribute='vrf.name') | reject('==', 'outside') | sort | unique %}
 | |
| 
 | |
| frr defaults datacenter
 | |
| log syslog informational
 | |
| service integrated-vtysh-config
 | |
| 
 | |
| # Route to the outside world.
 | |
| vrf outside
 | |
| {# Add default routes for IPv4 and IPv6. Both are over link-local addresses. #}
 | |
| {% set uplink = interfaces | selectattr('name', '==', iface_uplink) | first %}
 | |
| {% for address in uplink.ip_addresses %}
 | |
| {% set gateway = address.address | ipmath(1) %}
 | |
| {% if address.family.value == 4 %}
 | |
|   ip route 0.0.0.0/0 {{ gateway }} {{ iface_uplink }}
 | |
| {% else %}
 | |
|   ipv6 route ::/0 {{ gateway }} {{ iface_uplink }}
 | |
| {% endif %}
 | |
| {% endfor %}
 | |
| 
 | |
| # Route installation into kernel fails (rarely) without this option.
 | |
| # It is not documented anywhere and appears to be a Cumulus extension.
 | |
| zebra nexthop proto only
 | |
| 
 | |
| router-id {{ lo_address | ipv4 | first | ipaddr('address') }}
 | |
| 
 | |
| # Default VRF.
 | |
| router bgp {{ asn.asn }}
 | |
|   bgp bestpath as-path multipath-relax
 | |
| 
 | |
|   neighbor fabric peer-group
 | |
|   neighbor fabric remote-as external
 | |
|   neighbor fabric capability extended-nexthop
 | |
| 
 | |
|   neighbor peerlink.4094 interface remote-as external
 | |
|   neighbor peerlink.4094 capability extended-nexthop
 | |
|   neighbor peerlink.4094 bfd 3 150 150
 | |
| 
 | |
| {% for iface in ifaces_fabric %}
 | |
|   neighbor {{ iface }} interface peer-group fabric
 | |
|   neighbor {{ iface }} bfd 3 150 150
 | |
| {% endfor %}
 | |
| 
 | |
| {% for family in ['ipv4', 'ipv6'] %}
 | |
|   address-family {{ family }} unicast
 | |
|     redistribute connected route-map loopback
 | |
| 
 | |
|     neighbor fabric activate
 | |
|     neighbor fabric soft-reconfiguration inbound
 | |
|     neighbor fabric route-map fabric->default in
 | |
|     neighbor fabric route-map default->fabric out
 | |
| 
 | |
|     import vrf outside
 | |
|     import vrf route-map default-import
 | |
|   exit-address-family
 | |
| 
 | |
| {% endfor %}
 | |
|   address-family l2vpn evpn
 | |
|     advertise-all-vni
 | |
|     advertise-default-gw
 | |
|     neighbor fabric activate
 | |
|     neighbor peerlink.4094 activate
 | |
|   exit-address-family
 | |
| 
 | |
| 
 | |
| # Outside VRF. Direct route to the world, everything else goes to the firewall.
 | |
| router bgp {{ asn.asn }} vrf outside
 | |
|   bgp bestpath as-path multipath-relax
 | |
| 
 | |
|   neighbor peerlink.4 interface remote-as external
 | |
|   neighbor peerlink.4 capability extended-nexthop
 | |
|   neighbor peerlink.4 bfd 3 150 150
 | |
| 
 | |
|   neighbor firewall peer-group
 | |
|   neighbor firewall remote-as external
 | |
|   neighbor firewall capability extended-nexthop
 | |
| 
 | |
| {% for iface in ifaces_firewall %}
 | |
|   neighbor {{ iface }}.4 interface peer-group firewall
 | |
|   neighbor {{ iface }}.4 bfd 3 150 150
 | |
| {% endfor %}
 | |
| 
 | |
|   address-family ipv4 unicast
 | |
|     neighbor peerlink.4 activate
 | |
|     neighbor peerlink.4 soft-reconfiguration inbound
 | |
|     neighbor peerlink.4 route-map peer.4->me in
 | |
|     neighbor peerlink.4 route-map me->peer.4 out
 | |
| 
 | |
|     neighbor firewall activate
 | |
|     neighbor firewall allowas-in 1
 | |
|     neighbor firewall default-originate
 | |
|     neighbor firewall soft-reconfiguration inbound
 | |
|     neighbor firewall route-map outside->firewall out
 | |
| {% for iface in ifaces_firewall %}
 | |
|     neighbor {{ iface }}.4 route-map firewall-{{ loop.index }}->outside in
 | |
| {% endfor %}
 | |
| 
 | |
|     redistribute static
 | |
|     redistribute connected route-map ifaces-outside
 | |
|     import vrf default
 | |
|     import vrf route-map outside-import
 | |
|   exit-address-family
 | |
| 
 | |
|   address-family ipv6 unicast
 | |
|     neighbor peerlink.4 activate
 | |
|     neighbor peerlink.4 allowas-in origin
 | |
|     neighbor peerlink.4 soft-reconfiguration inbound
 | |
|     neighbor peerlink.4 route-map peer.4->me in
 | |
|     neighbor peerlink.4 route-map me->peer.4 out
 | |
| 
 | |
|     neighbor firewall activate
 | |
|     neighbor firewall allowas-in 1
 | |
|     neighbor firewall default-originate
 | |
|     neighbor firewall soft-reconfiguration inbound
 | |
|     neighbor firewall route-map outside->firewall out
 | |
| {% for iface in ifaces_firewall %}
 | |
|     neighbor {{ iface }}.4 route-map firewall-{{ loop.index }}->outside in
 | |
| {% endfor %}
 | |
| 
 | |
|     redistribute static
 | |
|     redistribute connected route-map ifaces-outside
 | |
|     import vrf default
 | |
|     import vrf route-map outside-import
 | |
|   exit-address-family
 | |
| 
 | |
| 
 | |
| # Inside VRF. Default route via firewall. Direct routes to servers and offices.
 | |
| router bgp {{ asn.asn }} vrf inside
 | |
|   bgp bestpath as-path multipath-relax
 | |
| 
 | |
|   neighbor peerlink.2 interface remote-as external
 | |
|   neighbor peerlink.2 capability extended-nexthop
 | |
|   neighbor peerlink.2 bfd 3 150 150
 | |
| 
 | |
|   neighbor firewall peer-group
 | |
|   neighbor firewall remote-as external
 | |
|   neighbor firewall capability extended-nexthop
 | |
| 
 | |
| {% for iface in ifaces_firewall %}
 | |
|   neighbor {{ iface }}.2 interface peer-group firewall
 | |
|   neighbor {{ iface }}.2 bfd 3 150 150
 | |
| {% endfor %}
 | |
| 
 | |
| {% for family in ['ipv4', 'ipv6'] %}
 | |
|   address-family {{ family }} unicast
 | |
|     neighbor peerlink.2 activate
 | |
|     neighbor peerlink.2 soft-reconfiguration inbound
 | |
|     neighbor peerlink.2 route-map peer.2->me in
 | |
|     neighbor peerlink.2 route-map me->peer.2 out
 | |
| 
 | |
|     neighbor firewall activate
 | |
|     neighbor firewall allowas-in 1
 | |
|     neighbor firewall soft-reconfiguration inbound
 | |
|     neighbor firewall route-map inside->firewall out
 | |
| {% for iface in ifaces_firewall %}
 | |
|     neighbor {{ iface }}.2 route-map firewall-{{ loop.index }}->inside in
 | |
| {% endfor %}
 | |
| 
 | |
|     redistribute connected route-map loopback-inside
 | |
| {% for vrf in inside_vrfs %}
 | |
|     import vrf {{ vrf }}
 | |
| {% endfor %}
 | |
|     import vrf default
 | |
|     import vrf route-map inside-import
 | |
|   exit-address-family
 | |
| 
 | |
| {% endfor %}
 | |
| 
 | |
| 
 | |
| {% for vrf in vrfs.values() | selectattr('name', 'in', inside_vrfs) %}
 | |
| router bgp {{ asn.asn }} vrf {{ vrf.name }}
 | |
|   bgp bestpath as-path multipath-relax
 | |
| 
 | |
| {% for family in ['ipv4', 'ipv6'] %}
 | |
|   address-family {{ family }} unicast
 | |
|     redistribute connected route-map ifaces-{{ vrf.name }}
 | |
|     import vrf inside
 | |
| {% if vrf.custom_fields.imports %}
 | |
| {% for import in vrf.custom_fields.imports %}
 | |
|     import vrf {{ import.name }}
 | |
| {% endfor %}
 | |
|     import vrf route-map {{ vrf.name }}-import
 | |
| {% else %}
 | |
|     import vrf route-map office-import
 | |
| {% endif %}
 | |
|   exit-address-family
 | |
| 
 | |
| {% endfor %}
 | |
| {% endfor %}
 | |
| 
 | |
| # Prefix lists.
 | |
| ip prefix-list default permit 0.0.0.0/0
 | |
| ipv6 prefix-list default permit ::/0
 | |
| 
 | |
| ip prefix-list fabric permit 10.34.0.0/24 ge 32
 | |
| ipv6 prefix-list fabric permit 2001:1470:fffd:3400::/64 ge 128
 | |
| 
 | |
| # prefix list for firewalls’ own addresses
 | |
| {% for firewall in interfaces | selectattr('name', 'in', ifaces_firewall) | iface_peer %}
 | |
| {% for address in hostvars[firewall] | device_address %}
 | |
| {% if address.family.value == 4 %}
 | |
| ip prefix-list firewall permit {{ address.address }}
 | |
| {% else %}
 | |
| ipv6 prefix-list firewall permit {{ address.address }}
 | |
| {% endif %}
 | |
| {% endfor %}
 | |
| {% endfor %}
 | |
| 
 | |
| # prefix list for outside networks
 | |
| {% for prefix in vrf_prefixes | selectattr('vrf.name', '==', 'outside')
 | |
|     | sort(attribute='family.value') | sort(attribute='vlan.vid') %}
 | |
| {% if prefix.family.value == 4 %}
 | |
| ip prefix-list outside permit {{ prefix.prefix }}
 | |
| {% else %}
 | |
| ipv6 prefix-list outside permit {{ prefix.prefix }}
 | |
| {% endif %}
 | |
| {% endfor %}
 | |
| 
 | |
| # common prefix list for all inside networks
 | |
| {% for prefix in vrf_prefixes | selectattr('vrf.name', 'in', inside_vrfs)
 | |
|     | sort(attribute='family.value') | sort(attribute='vlan.vid') %}
 | |
| {% if prefix.family.value == 4 %}
 | |
| ip prefix-list office permit {{ prefix.prefix }}
 | |
| {% else %}
 | |
| ipv6 prefix-list office permit {{ prefix.prefix }}
 | |
| {% endif %}
 | |
| {% endfor %}
 | |
| 
 | |
| # individual prefix lists for each inside network
 | |
| {% for prefix in vrf_prefixes | selectattr('vrf.name', 'in', inside_vrfs)
 | |
|     | sort(attribute='family.value') | sort(attribute='vrf.name') %}
 | |
| {% if prefix.family.value == 4 %}
 | |
| ip prefix-list {{ prefix.vrf.name }} permit {{ prefix.prefix }}
 | |
| {% else %}
 | |
| ipv6 prefix-list {{ prefix.vrf.name }} permit {{ prefix.prefix }}
 | |
| {% endif %}
 | |
| {% endfor %}
 | |
| 
 | |
| {% if wg_net is defined %}
 | |
| ip prefix-list vpn permit {{ wg_net | ipaddr('subnet') }}
 | |
| {% endif %}
 | |
| {% if wg_net6 is defined %}
 | |
| ipv6 prefix-list vpn permit {{ wg_net6 | ipaddr('subnet') }}
 | |
| {% endif %}
 | |
| 
 | |
| ip prefix-list nat permit {{ wg_ip | ipaddr('host') }}
 | |
| {% for network in nat %}
 | |
| ip prefix-list nat permit {{ network }}
 | |
| {% endfor %}
 | |
| 
 | |
| {% for prefix in bgp_prefixes | sort(attribute='family.value') %}
 | |
| {% if prefix.family.value == 4 %}
 | |
| ip prefix-list dc permit {{ prefix.prefix }} ge 32
 | |
| {% else %}
 | |
| ipv6 prefix-list dc permit {{ prefix.prefix }} ge 64
 | |
| {% endif %}
 | |
| {% endfor %}
 | |
| 
 | |
| # Route maps for redistributing own IPs from various VRFs.
 | |
| route-map loopback permit 1
 | |
|   match interface lo
 | |
| route-map loopback-inside permit 1
 | |
|   match interface inside
 | |
| route-map loopback-outside permit 1
 | |
|   match interface outside
 | |
| 
 | |
| # Route maps for local interfaces for each inside VRF. These are used to
 | |
| # prevent individual tenant VRFs from redistributing routes from virtual
 | |
| # ("-v0") interfaces into the common inside VRF. These routes don’t work.
 | |
| # For each VRF, a route map ifaces-vrf is defined that only permits bridge.*
 | |
| # interfaces in that VRF. Not sure if this actually helps, but doesn’t hurt.
 | |
| # Probably related to Cumulus issues 4531952, 4548512 and/or 4548514.
 | |
| {% for vrf in vrfs.values() %}
 | |
| {% for vlan_id in vrf_prefixes | selectattr('vrf.id', '==', vrf.id) | map(attribute='vlan.vid') | sort | unique %}
 | |
| route-map ifaces-{{ vrf.name }} permit {{ loop.index }}
 | |
|   match interface bridge.{{ vlan_id }}
 | |
| {% endfor %}
 | |
| {% endfor %}
 | |
| 
 | |
| # Route maps for importing between VRFs.
 | |
| route-map default-import permit 10
 | |
|   match ip address prefix-list default
 | |
| route-map default-import permit 11
 | |
|   match ipv6 address prefix-list default
 | |
| route-map default-import permit 20
 | |
|   match ip address prefix-list office
 | |
| route-map default-import permit 21
 | |
|   match ipv6 address prefix-list office
 | |
| route-map default-import permit 30
 | |
|   match ip address prefix-list nat
 | |
| route-map default-import permit 40
 | |
|   match ip address prefix-list vpn
 | |
| route-map default-import permit 41
 | |
|   match ipv6 address prefix-list vpn
 | |
| route-map default-import permit 50
 | |
|   match ip address prefix-list outside
 | |
| route-map default-import permit 51
 | |
|   match ipv6 address prefix-list outside
 | |
| 
 | |
| route-map outside-import permit 10
 | |
|   match ip address prefix-list dc
 | |
| route-map outside-import permit 11
 | |
|   match ipv6 address prefix-list dc
 | |
| 
 | |
| route-map office-import permit 10
 | |
|   match ip address prefix-list default
 | |
| route-map office-import permit 11
 | |
|   match ipv6 address prefix-list default
 | |
| route-map office-import permit 20
 | |
|   match ip address prefix-list firewall
 | |
| route-map office-import permit 21
 | |
|   match ipv6 address prefix-list firewall
 | |
| 
 | |
| route-map inside-import permit 20
 | |
|   match ip address prefix-list office
 | |
| route-map inside-import permit 21
 | |
|   match ipv6 address prefix-list office
 | |
| 
 | |
| {% for vrf in vrfs.values() | selectattr('custom_fields.imports') %}
 | |
| route-map {{ vrf.name }}-import permit 10
 | |
|   match ip address prefix-list default
 | |
| route-map {{ vrf.name }}-import permit 11
 | |
|   match ipv6 address prefix-list default
 | |
| {% for import in vrf.custom_fields.imports %}
 | |
| route-map {{ vrf.name }}-import permit {{ 100 + 10*loop.index0 }}
 | |
|   match ip address prefix-list {{ import.name }}
 | |
| route-map {{ vrf.name }}-import permit {{ 101 + 10*loop.index0 }}
 | |
|   match ipv6 address prefix-list {{ import.name }}
 | |
| {% endfor %}
 | |
| 
 | |
| {% endfor %}
 | |
| 
 | |
| # Route maps for advertised and received routes.
 | |
| # Default VRF ↔ fabric.
 | |
| route-map default->fabric permit 10
 | |
|   match ip address prefix-list default
 | |
| route-map default->fabric permit 11
 | |
|   match ipv6 address prefix-list default
 | |
| route-map default->fabric permit 20
 | |
|   match ip address prefix-list fabric
 | |
| 
 | |
| route-map fabric->default permit 10
 | |
|   match ip address prefix-list fabric
 | |
| route-map fabric->default permit 20
 | |
|   match ip address prefix-list dc
 | |
| route-map fabric->default permit 21
 | |
|   match ipv6 address prefix-list dc
 | |
| 
 | |
| # Inside VRF ↔ firewall.
 | |
| route-map inside->firewall permit 1
 | |
|   match interface lo
 | |
| route-map inside->firewall permit 20
 | |
|   match ip address prefix-list office
 | |
| route-map inside->firewall permit 21
 | |
|   match ipv6 address prefix-list office
 | |
| 
 | |
| route-map firewall->inside permit 1
 | |
|   match ip address prefix-list fabric
 | |
| route-map firewall->inside permit 2
 | |
|   match ipv6 address prefix-list fabric
 | |
| route-map firewall->inside permit 10
 | |
|   match ip address prefix-list default
 | |
| route-map firewall->inside permit 11
 | |
|   match ipv6 address prefix-list default
 | |
| 
 | |
| # Outside VRF ↔ firewall.
 | |
| route-map outside->firewall permit 10
 | |
|   match ip address prefix-list default
 | |
| route-map outside->firewall permit 11
 | |
|   match ipv6 address prefix-list default
 | |
| 
 | |
| route-map firewall->outside permit 1
 | |
|   match ip address prefix-list fabric
 | |
| route-map firewall->outside permit 2
 | |
|   match ipv6 address prefix-list fabric
 | |
| route-map firewall->outside permit 20
 | |
|   match ip address prefix-list office
 | |
| route-map firewall->outside permit 21
 | |
|   match ipv6 address prefix-list office
 | |
| route-map firewall->outside permit 30
 | |
|   match ip address prefix-list nat
 | |
| route-map firewall->outside permit 40
 | |
|   match ip address prefix-list vpn
 | |
| route-map firewall->outside permit 41
 | |
|   match ipv6 address prefix-list vpn
 | |
| 
 | |
| # Tag routes from each firewall. Set weight for primary to 200 and secondary to 100.
 | |
| {% for firewall in ifaces_firewall %}
 | |
| route-map firewall-{{ loop.index }}->inside permit 1
 | |
|   set tag {{ loop.index }}
 | |
|   set weight {{ 100 * loop.index }}
 | |
|   set as-path exclude {{ asn.asn }} {{ hostvars[peer].asn.asn }}
 | |
|   call firewall->inside
 | |
| route-map firewall-{{ loop.index }}->outside permit 1
 | |
|   set tag {{ loop.index }}
 | |
|   set weight {{ 100 * loop.index }}
 | |
|   set as-path exclude {{ asn.asn }} {{ hostvars[peer].asn.asn }}
 | |
|   call firewall->outside
 | |
| {% endfor %}
 | |
| 
 | |
| # Backup routes over peer link are announced to the peer with BGP
 | |
| # metrics 190 and 90. These values are copied to weights by receiving
 | |
| # peer, to be used alongside local routes with weights 200 and 100.
 | |
| # These are the route maps for peerlink in the inside VRF.
 | |
| {% for firewall in ifaces_firewall %}
 | |
| {% set metric = 100 * loop.index - 10 %}
 | |
| route-map me->peer.2 permit {{ loop.index }}
 | |
|   match tag {{ loop.index }}
 | |
|   on-match goto 100
 | |
|   set metric {{ metric }}
 | |
| route-map peer.2->me permit {{ loop.index }}
 | |
|   match metric {{ metric }}
 | |
|   on-match goto 100
 | |
|   set weight {{ metric }}
 | |
| {% endfor %}
 | |
| 
 | |
| # Advertised backup routes for paths that go through the firewall
 | |
| # (default route).
 | |
| route-map me->peer.2 permit 110
 | |
|   match ip address prefix-list default
 | |
| route-map me->peer.2 permit 111
 | |
|   match ipv6 address prefix-list default
 | |
| 
 | |
| # Received backup routes (same as above).
 | |
| route-map peer.2->me permit 110
 | |
|   match ip address prefix-list default
 | |
| route-map peer.2->me permit 111
 | |
|   match ipv6 address prefix-list default
 | |
| 
 | |
| # These are the route maps for peerlink in the outside VRF.
 | |
| {% for firewall in ifaces_firewall %}
 | |
| {% set metric = 100 * loop.index - 10 %}
 | |
| route-map me->peer.4 permit {{ loop.index }}
 | |
|   match tag {{ loop.index }}
 | |
|   on-match goto 100
 | |
|   set metric {{ metric }}
 | |
| route-map peer.4->me permit {{ loop.index }}
 | |
|   match metric {{ metric }}
 | |
|   on-match goto 100
 | |
|   set weight {{ metric }}
 | |
| {% endfor %}
 | |
| 
 | |
| # Backup routes for uplink and paths that go through the firewall
 | |
| # (default route and NAT/IPv6 addresses for office networks).
 | |
| route-map me->peer.4 permit 110
 | |
|   match ip address prefix-list default
 | |
| route-map me->peer.4 permit 111
 | |
|   match ipv6 address prefix-list default
 | |
| route-map me->peer.4 permit 120
 | |
|   match ip address prefix-list office
 | |
| route-map me->peer.4 permit 121
 | |
|   match ipv6 address prefix-list office
 | |
| route-map me->peer.4 permit 130
 | |
|   match ip address prefix-list nat
 | |
| route-map me->peer.4 permit 140
 | |
|   match ip address prefix-list vpn
 | |
| route-map me->peer.4 permit 141
 | |
|   match ipv6 address prefix-list vpn
 | |
| 
 | |
| # Received backup routes (same as above).
 | |
| route-map peer.4->me permit 110
 | |
|   match ip address prefix-list default
 | |
| route-map peer.4->me permit 111
 | |
|   match ipv6 address prefix-list default
 | |
| route-map peer.4->me permit 120
 | |
|   match ip address prefix-list office
 | |
| route-map peer.4->me permit 121
 | |
|   match ipv6 address prefix-list office
 | |
| route-map peer.4->me permit 130
 | |
|   match ip address prefix-list nat
 | |
| route-map peer.4->me permit 140
 | |
|   match ip address prefix-list vpn
 | |
| route-map peer.4->me permit 141
 | |
|   match ipv6 address prefix-list vpn
 |