routing/roles/bird/templates/bird.conf.j2
2024-09-15 09:42:20 +02:00

262 lines
7.9 KiB
Django/Jinja

# {{ ansible_managed }}
router id {{ bird_router_id }};
protocol device {
scan time 10;
}
protocol kernel {
scan time 5;
ipv6 {
import none;
export filter {
{% if bird_prefsrc is defined %}
krt_prefsrc = {{ bird_prefsrc }};
{% endif %}
if source = RTS_BGP then accept;
if source = RTS_STATIC then accept;
if source = RTS_DEVICE then reject;
accept;
};
};
}
protocol kernel {
scan time 5;
ipv4 {
import none;
export filter {
{% if bird_prefsrc_v4 is defined %}
krt_prefsrc = {{ bird_prefsrc_v4 }};
{% endif %}
if source = RTS_BGP then accept;
if source = RTS_STATIC then accept;
if source = RTS_DEVICE then reject;
accept;
};
};
}
protocol static {
{% for net in bird_bgp_advertised_networks %}
route {{ net }} reject;
{% endfor %}
ipv6 {
import all;
export none;
};
}
protocol static {
{% for net in bird_bgp_advertised_networks_v4 | default([]) %}
route {{ net }} reject;
{% endfor %}
ipv4 {
import all;
export none;
};
}
{%if bird_ospf_interfaces | default([]) | length > 0%}
############################################
# _|_| _|_|_| _|_|_| _|_|_|_| #
# _| _| _| _| _| _| #
# _| _| _|_| _|_|_| _|_|_| #
# _| _| _| _| _| #
# _|_| _|_|_| _| _| #
############################################
# Filter to export all direct and bgp routes into OSPF
filter export_all_OSPF {
if ( source = RTS_DEVICE ) then accept;
if ( source = RTS_STATIC_DEVICE ) then accept;
if ( source = RTS_STATIC ) then accept;
if ( source = RTS_BGP ) then accept;
reject;
}
protocol ospf v3 ospf6 {
ipv6 {
{% if bird_ospf_export_all | default(false) %}
export filter export_all_OSPF;
{% endif %}
};
area 0 {
{%for iface in bird_ospf_interfaces%}
interface "{{ iface.name }}" {
{%if iface.stub | default(false)%}
stub;
{%endif%}
};
{%endfor%}
};
}
{%endif%}
{% if bird_bgp_neighbors | default([]) | length > 0%}
###################################
# _|_|_| _|_|_| _|_|_| #
# _| _| _| _| _| #
# _|_|_| _| _|_| _|_|_| #
# _| _| _| _| _| #
# _|_|_| _|_|_| _| #
###################################
define OWNNETSET = [{{ bird_bgp_advertised_networks | join(', ') }}];
define OWNNETSET_v4 = [{{ bird_bgp_advertised_networks_v4 | default([]) | join(', ') }}];
define OWNASN = {{ bird_bgp_local_asn }};
function is_self_net() {
if net ~ OWNNETSET then return true;
if net ~ OWNNETSET_v4 then return true;
return false;
}
function has_private_asn() {
# Check if the AS path contains any private ASN
if (bgp_path ~ [64512..65534]) || (bgp_path ~ [4200000000..4294967294]) then {
print "Rejecting route with private ASN in AS path: ", bgp_path;
return true;
}
return false;
}
function is_reserved_net() {
# ULA
if net ~ fc00::/7 then return true;
# Link-local
if net ~ fe80::/10 then return true;
# Documentation
if net ~ 2001:db8::/32 then return true;
# Discard
if net ~ 100::/64 then return true;
# Orchid
if net ~ 2001:20::/28 then return true;
# IPv4
if net ~ 0.0.0.0/8 then return true;
if net ~ 10.0.0.0/8 then return true;
if net ~ 100.64.0.0/10 then return true;
if net ~ 127.0.0.0/8 then return true;
if net ~ 169.254.0.0/16 then return true;
if net ~ 172.16.0.0/12 then return true;
if net ~ 192.0.0.0/24 then return true;
if net ~ 192.0.2.0/24 then return true;
if net ~ 192.88.99.0/24 then return true;
if net ~ 192.168.0.0/16 then return true;
if net ~ 198.18.0.0/15 then return true;
if net ~ 198.51.100.0/24 then return true;
if net ~ 203.0.113.0/24 then return true;
if net ~ 224.0.0.0/4 then return true;
if net ~ 233.252.0.0/24 then return true;
if net ~ 240.0.0.0/4 then return true;
if net ~ 255.255.255.255/32 then return true;
return false;
}
{%for peer in bird_bgp_neighbors%}
# {{ peer.name }}
{% set peer_name = peer.name | lower | regex_replace('[\s\.\-\$%&/()=]', '_') %}
filter {{ peer_name }}_BGP_IMPORT {
# don't re-import networks announced by ourself
if is_self_net() then reject;
{% if not (peer.allow_reserved_networks | default(bird_bgp_allow_reserved_networks | default(false))) %}
# don't import reserved networks
if is_reserved_net() then reject;
{% endif %}
# never accept routes more specific than /{{ peer.min_pref_len | default(bird_bgp_min_pref_len | default(48)) }}
if net.len > {{ peer.min_pref_len | default(bird_bgp_min_pref_len | default(48)) }} then reject;
{% if not (peer.allow_private_asn | default(bird_bgp_allow_private_asn | default(false))) %}
# prevent importing paths with private ASNs
if has_private_asn() then reject;
{% endif %}
{% if (peer.max_as_path_length is defined) or (bird_bgp_max_as_path_length is defined) %}
if bgp_path.len > {{ peer.max_as_path_length | default(bird_bgp_max_as_path_length) }} then reject;
{% endif %}
{% if (peer.restrict_prefixes | default([]) | length > 0) %}
if net !~ [{{ peer.restrict_prefixes | join(', ') }}] then reject;
{% endif %}
accept;
}
# export rules for {{ peer_name }}
filter {{ peer_name }}_ALLOWED_BGP {
{% for n in range(peer.as_prepend | default(1)) %}
bgp_path.prepend({{ peer.local_asn | default('OWNASN') }});
{% endfor %}
{% if not (peer.allow_reserved_networks | default(bird_bgp_allow_reserved_networks | default(false))) %}
# don't export reserved networks
if is_reserved_net() then reject;
{% endif %}
# never send routes more specific than /{{ peer.min_pref_len | default(bird_bgp_min_pref_len | default(48)) }}
if net.len > {{ peer.min_pref_len | default(bird_bgp_min_pref_len | default(48)) }} then reject;
{% if not (peer.allow_private_asn | default(bird_bgp_allow_private_asn | default(false))) %}
# prevent exposing paths with private ASNs
if has_private_asn() then reject;
{% endif %}
if is_self_net() then accept;
{% if (peer.redistribute_learned | default(false)) %}
# redistribute all other routes learned from BGP
if source = RTS_BGP then {
{% if not (peer.redistribute_reserved | default(false)) %}
# don't redistribute reserved networks
if is_reserved_net() then reject;
{% endif %}
{% for n in range(peer.redistribute_prepend | default(0)) %}
bgp_path.prepend({{ peer.local_asn | default('OWNASN') }});
{% endfor %}
accept;
}
{% endif %}
reject;
}
protocol bgp {{ peer_name }} {
local {{ peer.local_ip }} as {{ peer.local_asn | default('OWNASN') }};
neighbor {{ peer.neighbor_ip }} as {{ peer.neighbor_asn }};
description "{{ peer.description | default(peer.name) }}";
{% if peer.password is defined %}
password "{{ peer.password }}";
{% endif %}
{% if peer.metric is defined %}
path metric {{ peer.metric }};
{% endif %}
{% if peer.ipv4 | default(false) == true %}
ipv4 {
import filter {{ peer_name }}_BGP_IMPORT;
export filter {{ peer_name }}_ALLOWED_BGP;
{% if peer.import_limit | default(1000) != false %}
import limit {{ peer.import_limit | default(1000) }} action block;
{% endif %}
next hop {{ ('address ' + peer.next_hop) if peer.next_hop is defined else 'self' }};
};
{% else %}
ipv6 {
import filter {{ peer_name }}_BGP_IMPORT;
export filter {{ peer_name }}_ALLOWED_BGP;
{% if peer.import_limit | default(1000) != false %}
import limit {{ peer.import_limit | default(1000) }} action block;
{% endif %}
next hop {{ ('address ' + peer.next_hop) if peer.next_hop is defined else 'self' }};
};
{% endif %}
}
{%endfor%}
# TODO: roa with routinator...
# roa4 table dn42_roa;
# protocol static {
# roa4 { table dn42_roa; };
# include "/etc/bird/roa_dn42.conf";
# };
{% endif %}