network/roles/exit/templates/frr.conf.j2
2024-09-16 17:19:25 +02:00

468 lines
15 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
ip route 0.0.0.0/0 {{ (interfaces | selectattr('name', '==', iface_uplink) | first).custom_fields.gateway.address | ipaddr('address') }} {{ iface_uplink }}
ipv6 route ::/0 fe80::2 {{ iface_uplink }}
# 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 %}
address-family ipv4 unicast
redistribute connected route-map loopback
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
address-family ipv6 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
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 soft-reconfiguration inbound
neighbor peerlink.4 route-map peer.4->me in
neighbor peerlink.4 route-map me->peer.4 out
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
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
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 %}
address-family ipv4 unicast
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 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
address-family ipv6 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
{% for vrf in vrfs.values() | selectattr('name', 'in', inside_vrfs) %}
router bgp {{ asn.asn }} vrf {{ vrf.name }}
bgp bestpath as-path multipath-relax
address-family ipv4 unicast
redistribute connected
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
address-family ipv6 unicast
redistribute connected
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 %}
# 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 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 }} ge {{ prefix.prefix | ipaddr('prefix') }}
{% else %}
ipv6 prefix-list outside permit {{ prefix.prefix }} ge {{ prefix.prefix | ipaddr('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 }} ge {{ prefix.prefix | ipaddr('prefix') }}
{% else %}
ipv6 prefix-list office permit {{ prefix.prefix }} ge {{ prefix.prefix | ipaddr('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 }} ge {{ prefix.prefix | ipaddr('prefix') }}
{% else %}
ipv6 prefix-list {{ prefix.vrf.name }} permit {{ prefix.prefix }} ge {{ prefix.prefix | ipaddr('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 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 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 }}
call firewall->inside
route-map firewall-{{ loop.index }}->outside permit 1
set tag {{ loop.index }}
set weight {{ 100 * loop.index }}
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 nat
route-map me->peer.4 permit 121
match ipv6 address prefix-list vpn
route-map me->peer.4 permit 131
match ipv6 address prefix-list office
# 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 nat
route-map peer.4->me permit 121
match ipv6 address prefix-list vpn
route-map peer.4->me permit 131
match ipv6 address prefix-list office