# {{ 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 %}