{% set addrs = interfaces | selectattr('name', '==', 'lo') | map(attribute='ip_addresses') | first | selectattr('role') %} {% set loopback = addrs | selectattr('role.value', '==', 'loopback') | map(attribute='address') -%} frr defaults datacenter service integrated-vtysh-config log syslog # Without this frr and kernel ECMP routes sometimes get desynced when a link is # lost and found. Maybe related to https://github.com/FRRouting/frr/issues/12239. zebra nexthop-group keep 1 router-id {{ loopback | ipv4 | first | ipaddr('address') }} bfd profile fast receive-interval 150 transmit-interval 150 # Default VRF has two connections to each exit, one for inside and one # for outside networks. The efault route is received from the outside # peers and distributed back to inside peers. Routes to office # networks and NAT IPs are distributed to outside peers. router bgp {{ asn.asn }} # Allow multipathing through different ASs with equal path length. bgp bestpath as-path multipath-relax # NAT IPs are not on any interface so disable checking for it. no bgp network import-check {% for group in ['inside', 'outside'] %} neighbor {{ group }} peer-group neighbor {{ group }} remote-as external neighbor {{ group }} capability extended-nexthop {% endfor %} {% for iface in interfaces | selectattr('name', 'match', '^lan') %} neighbor {{ iface.name }}.2 interface peer-group inside neighbor {{ iface.name }}.2 bfd profile fast neighbor {{ iface.name }}.4 interface peer-group outside neighbor {{ iface.name }}.4 bfd profile fast {% endfor %} address-family ipv4 unicast {% for network in nat %} network {{ network }} {% endfor %} redistribute connected route-map loopback maximum-paths 16 neighbor outside soft-reconfiguration inbound neighbor outside route-map outside->default in neighbor outside route-map default->outside out neighbor inside allowas-in origin neighbor inside default-originate neighbor inside soft-reconfiguration inbound neighbor inside route-map inside->default in neighbor inside route-map default->inside out exit-address-family address-family ipv6 unicast redistribute connected route-map loopback maximum-paths 16 neighbor outside activate neighbor outside soft-reconfiguration inbound neighbor outside route-map outside->default in neighbor outside route-map default->outside out neighbor inside activate neighbor inside allowas-in origin neighbor inside default-originate neighbor inside soft-reconfiguration inbound neighbor inside route-map inside->default in neighbor inside route-map default->inside out exit-address-family # 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 {% for prefix in vrf_prefixes | rejectattr('vrf.name', '==', 'outside') | sort(attribute='family.value') %} {% if prefix.family.value == 4 %} ip prefix-list office permit {{ prefix.prefix }} ge {{ prefix.prefix | ipaddr('prefix') }} {% elif prefix.family.value == 6 %} ipv6 prefix-list office 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 %} {% for network in nat %} ip prefix-list nat permit {{ network }} {% endfor %} {# TODO WG endpoint should probably be in a separate prefix-list. #} ip prefix-list nat permit {{ wg_ip }} route-map loopback permit 1 match interface lo route-map loopback permit 2 match interface wg # Get routes to offices from inside peers. route-map inside->default permit 10 match ip address prefix-list fabric route-map inside->default permit 20 match ip address prefix-list office route-map inside->default permit 21 match ipv6 address prefix-list office # Send default route and VPN network to inside peers. route-map default->inside permit 1 match interface lo route-map default->inside permit 20 match ip address prefix-list default route-map default->inside permit 21 match ipv6 address prefix-list default # I don’t think these /need/ to be announced separately since we are sending the default route anyway. #route-map default->inside permit 30 # match ip address prefix-list vpn #route-map default->inside permit 31 # match ipv6 address prefix-list vpn # Get default route from outside peers. route-map outside->default permit 10 match ip address prefix-list default route-map outside->default permit 11 match ipv6 address prefix-list default # Send inside and NAT addresses to outside peers so inbound packets go through the firewall. route-map default->outside permit 1 match interface lo route-map default->outside permit 20 match ip address prefix-list office route-map default->outside permit 21 match ipv6 address prefix-list office route-map default->outside permit 30 match ip address prefix-list nat route-map default->outside permit 40 match ip address prefix-list vpn route-map default->outside permit 41 match ipv6 address prefix-list vpn