{% set lo_address = interfaces | selectattr('name', '==', 'lo') | map(attribute='ip_addresses') | first | selectattr('role') | selectattr('role.value', '==', 'loopback') | map(attribute='address') %} {% set my_index = inventory_hostname.split('-')[1]|int %} {% set bridge = interfaces | selectattr('type') | selectattr('type.value', '==', 'bridge') | first %} {% set bridge_vlans = vlans | selectattr('vid', 'in', bridge.tagged_vlans | map(attribute='vid')) -%} {% set inside_vlans = bridge_vlans | selectattr('role.slug', '==', 'inside') -%} 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 }} # Don’t announce anything at start until we get routes from all our peers. # Without this packets might get dropped until all routes are synced. bgp update-delay 10 # 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 route-map loopback-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 loopback-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 %} 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 vlan in inside_vlans %} import vrf {{ vlan.name }} {% 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 vlan in inside_vlans %} import vrf {{ vlan.name }} {% endfor %} import vrf default import vrf route-map inside-import exit-address-family {% for vlan in inside_vlans %} # VRF for L2 network {{ vlan.name }}. Imports gateway from inside VRF. router bgp {{ asn.asn }} vrf {{ vlan.name }} bgp bestpath as-path multipath-relax address-family ipv4 unicast redistribute connected import vrf inside import vrf route-map office-import exit-address-family address-family ipv6 unicast redistribute connected import vrf inside import vrf route-map office-import 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 {% for vlan in inside_vlans %} {% set prefixes = query('netbox.netbox.nb_lookup', 'prefixes', api_filter='vlan_id='~vlan.id, raw_data=true) %} {% for prefix in prefixes %} {% if prefix.family.value == 4 %} ip prefix-list office permit {{ prefix.prefix }} ge 24 {% else %} ipv6 prefix-list office permit {{ prefix.prefix }} ge 64 {% endif %} {% endfor %} {% 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 query('netbox.netbox.nb_lookup', 'prefixes', raw_data=true, api_filter='role=bgp') | selectattr('tenant') %} {% 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 21 match ipv6 address prefix-list office route-map default-import permit 30 match ip address prefix-list nat route-map default-import permit 31 match ipv6 address prefix-list vpn 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 # Route maps for advertised and received routes. # Inside ↔ 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 ↔ 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 ↔ 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 21 match ipv6 address prefix-list office route-map firewall->outside permit 30 match ip address prefix-list nat route-map firewall->outside permit 31 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