Timotej Lazar
5a9f0ac26a
For some reason routes with own ASN are not imported into default VRF. Maybe also others. These routes forward packets through the firewalls. As long as both exits are up this is not a problem, because routes going to peer exit don’t include this exit’s own ASN. If the peer goes down, all remaining routes sent by firewalls have our own ASN and are not imported into default VRF, so L3 servers lose connectivity to internal networks. If the exit strips own ASN from received routes, importing works OK. We strip both our and peer’s ASNs to keep path lengths the same. This has involved an indecent amount of poking knobs and knobbing pokes and it might cause other issues elsewhere.
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
|
|
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 }}
|
|
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
|