Files
2025-07-30 00:02:25 +02:00

433 lines
10 KiB
Plaintext

#############################
## !!! Ansible Managed !!! ##
#############################
# Configure logging
log syslog all;
log "/var/log/bird.log" { debug, trace, info, remote, warning, error, auth, fatal, bug };
# Set router ID. It is a unique identification of your router, usually one of
# IPv4 addresses of the router. It is recommended to configure it explicitly.
# router id 198.51.100.1; # TODO: set router id
# Turn on global debugging of all protocols (all messages or just selected classes)
# debug protocols all;
debug protocols { events, states };
# Turn on internal watchdog
watchdog warning 5 s;
watchdog timeout 30 s;
### START CONSTANTS ###
define MYASN = 64001;
define MYIPv6 = 2001:db8:2:2::11;
define MYNETSET6 = [
];
define BOGON_ASNS = [
0, # RFC 7607
23456, # RFC 4893 AS_TRANS
64496..64511, # RFC 5398 and documentation/example ASNs
65535, # RFC 7300 Last 16 bit ASN
65536..65551, # RFC 5398 and documentation/example ASNs
65552..131071, # RFC IANA reserved ASNs
4200000000..4294967294, # RFC 6996 Private ASNs
4294967295 # RFC 7300 Last 32 bit ASN
];
define TRANSIT_ASNS = [
174, # Cogent
701, # UUNET
1299, # Telia
2914, # NTT Ltd.
3257, # GTT Backbone
3320, # Deutsche Telekom AG (DTAG)
3356, # Level3
3491, # PCCW
4134, # Chinanet
5511, # Orange opentransit
6453, # Tata Communications
6461, # Zayo Bandwidth
6762, # Seabone / Telecom Italia
6830, # Liberty Global
7018 # AT&T
];
define BOGON_PREFIXES_V6 = [
::/8+, # RFC 4291 IPv4-compatible, loopback, et al
0100::/64+, # RFC 6666 Discard-Only
2001:2::/48+, # RFC 5180 BMWG
2001:10::/28+, # RFC 4843 ORCHID
3fff::/20+, # RFC 9637 documentation
2002::/16+, # RFC 7526 6to4 anycast relay
3ffe::/16+, # RFC 3701 old 6bone
5f00::/16+, # RFC 9602 SRv6
fc00::/7+, # RFC 4193 unique local unicast
fe80::/10+, # RFC 4291 link local unicast
fec0::/10+, # RFC 3879 old site local unicast
ff00::/8+ # RFC 4291 multicast
];
define IXP_LANS = [
2001:7f8:10f::/64+
];
### END CONSTANTS ###
### START RPKI ###
roa6 table r6;
### END RPKI ###
### START PROTOCOLS ###
protocol device {
scan time 10;
}
# Static route for our network
protocol static {
# Create routes for our network without specifying a next hop
ipv6 {
import filter {
# Import static routes into bird routing table
bgp_large_community.add((MYASN, 3, 1)); # Route originated internally
accept;
};
export none; # Do not export routes to this proto
};
}
# Internal ospf
protocol ospf v3 {
ipv6 {
import all; # import all routes learned from neighbors
export none; # do not export anything to ospf (for now), ospf will figure things out itself
};
area 0.0.0.0 {
};
}
### END PROTOCOLS ###
### START FUNCTIONS ###
# Is this network within our own network? Can match more specific prefixes, for example a /64 will return true if it is within our /48
function is_self_net_v6()
{
return net ~ MYNETSET6;
}
# source: https://bgpfilterguide.nlnog.net/guides/reject_invalids/#bird
# slightly modified
# Check if ROA has status ROA_VALID
function check_rpki_rov_valid()
{
if roa_check(r6, net, bgp_path.last) = ROA_VALID then {
return true;
}
return false;
}
# Check if ROA has status ROA_INVALID
function reject_rpki_rov_invalid()
{
if roa_check(r6, net, bgp_path.last) = ROA_INVALID then {
}
}
# source: https://bgpfilterguide.nlnog.net/guides/bogon_asns/#bird
function reject_bogon_asns()
int set bogon_asns;
{
bogon_asns = BOGON_ASNS;
if ( bgp_path ~ bogon_asns ) then {
print "Reject: bogon AS_PATH: ", net, " ", bgp_path;
bgp_large_community.add((MYASN, 7, 2));
reject;
}
}
#source: https://bgpfilterguide.nlnog.net/guides/bogon_prefixes/#bird-1
function reject_bogon_prefixes_v6()
prefix set bogon_prefixes;
{
bogon_prefixes = BOGON_PREFIXES_V6;
if (net ~ bogon_prefixes) then {
print "Reject: Bogon prefix: ", net, " ", bgp_path;
bgp_large_community.add((MYASN, 7, 3));
reject;
}
}
# source: https://bgpfilterguide.nlnog.net/guides/long_paths/#bird
function reject_long_aspaths()
{
if ( bgp_path.len > 100 ) then {
print "Reject: Too long AS path: ", net, " ", bgp_path;
bgp_large_community.add((MYASN, 7, 4));
reject;
}
}
# source: https://bgpfilterguide.nlnog.net/guides/small_prefixes/#bird
# slightly modified
function reject_small_prefixes_v6()
{
if ( net.len > 48 ) then {
print "Reject: Too small prefix: ", net, " ", bgp_path;
bgp_large_community.add((MYASN, 7, 5));
reject;
}
}
# source: custom
function reject_default_route_v6()
{
if ( net = ::/0 ) then {
print "Reject: Default route: ", net, " ", bgp_path;
bgp_large_community.add((MYASN, 7, 7));
reject;
}
}
function reject_own_prefixes_v6()
{
if is_self_net_v6() then {
print "Reject: Own prefix: ", net, " ", bgp_path;
bgp_large_community.add((MYASN, 7, 1));
reject;
}
}
# source: https://bgpfilterguide.nlnog.net/guides/no_transit_leaks/
# BEWARE: check that peers do not provide transit to tier 1's
function reject_transit_paths()
int set transit_asns;
{
transit_asns = TRANSIT_ASNS;
if (bgp_path ~ transit_asns) then {
print "Reject: Transit ASNs found on IXP: ", net, " ", bgp_path;
bgp_large_community.add((MYASN, 7, 8));
reject;
}
}
# source: https://bgpfilterguide.nlnog.net/guides/graceful_shutdown/#bird
function honor_graceful_shutdown()
{
if (65535, 0) ~ bgp_community then {
bgp_local_pref = 0;
}
}
# source: https://bgpfilterguide.nlnog.net/guides/many_communities/#bird
function strip_too_many_communities()
{
if ( ( bgp_community.len + bgp_ext_community.len + bgp_large_community.len ) >= 100 ) then {
print "Too many communities, stripped communities: ", net, " ", bgp_path;
bgp_community.empty;
bgp_ext_community.empty;
bgp_large_community.empty;
}
}
function scrub_own_communities()
{
bgp_large_community.delete([(MYASN, *, *)]);
}
function reject_ixp_lan()
{
if ( net ~ IXP_LANS ) then {
bgp_large_community.add((MYASN, 7, 10));
print "Reject: Prefix within IXP LAN: ", net, " ", bgp_path;
reject;
}
}
### END FUNCTIONS ###
### START FILTERS ###
filter f_customer_in_v6 {
scrub_own_communities();
bgp_large_community.add((MYASN, 3, 2)); # Route learned from a customer
# TODO
accept;
}
filter f_customer_out_v6 {
if source !~ [RTS_STATIC, RTS_BGP] then reject;
if ((MYASN, 3, 1) ~ bgp_large_community) then accept; # Internal
if ((MYASN, 3, 2) ~ bgp_large_community) then accept; # Customer
if ((MYASN, 3, 3) ~ bgp_large_community) then accept; # Peering
if ((MYASN, 3, 4) ~ bgp_large_community) then accept; # Transit
reject;
}
filter f_peer_PEERNAME_in_v6 {
scrub_own_communities();
bgp_large_community.add((MYASN, 3, 3)); # Route learned from a peering partner
# TODO
accept;
}
filter f_peer_PEERNAME_out_v6 {
if source !~ [RTS_STATIC, RTS_BGP] then reject;
if ((MYASN, 3, 1) ~ bgp_large_community) then accept; # Internal
if ((MYASN, 3, 2) ~ bgp_large_community) then accept; # Customer
reject;
}
filter f_transit_in_v6 {
scrub_own_communities();
reject_rpki_rov_invalid();
reject_bogon_asns();
reject_long_aspaths();
reject_bogon_prefixes_v6();
reject_small_prefixes_v6();
reject_ixp_lan();
reject_default_route_v6();
honor_graceful_shutdown();
strip_too_many_communities();
bgp_large_community.add((MYASN, 3, 4)); # Route learned from a transit provider
if check_rpki_rov_valid() then {
bgp_large_community.add((MYASN, 6, 1)); # Valid matching ROA exists
bgp_large_community.add((MYASN, 6, 3)); # Accepted because of valid ROA
# local pref 120???
accept;
} else {
# local pref 80???
}
if true then {
bgp_large_community.add((MYASN, 6, 4)); # Valid matching IRR entry exists
accept;
} else {
bgp_large_community.add((MYASN, 7, 9)); # No valid IRR entry
reject;
}
}
filter f_transit_out_v6 {
if source !~ [RTS_STATIC, RTS_BGP] then reject;
if ((MYASN, 3, 1) ~ bgp_large_community) then accept; # Internal
if ((MYASN, 3, 2) ~ bgp_large_community) then accept; # Customer
reject;
}
### END FILTERS ###
### START TEMPLATES ###
template bgp internal_peer {
local as MYASN;
hold time 90;
ipv6 {
next hop self; # for internal routes the next hop is always the neighboring router
import filter { # Import all routes learned by peers
accept;
};
export filter {
if source = RTS_BGP then accept;
reject;
};
};
}
template bgp customer {
local as MYASN;
hold time 90;
ipv6 {
import filter f_customer_in_v6;
export filter f_customer_out_v6;
import keep filtered;
};
}
template bgp peer {
local as MYASN;
hold time 90;
ipv6 {
import filter f_peer_PEERNAME_in_v6;
export filter f_peer_PEERNAME_out_v6;
import keep filtered;
};
}
template bgp transit {
local as MYASN;
hold time 90;
ipv6 {
export filter f_transit_out_v6;
import filter f_transit_in_v6;
import keep filtered;
};
}
template bgp rs_peer {
local as MYASN;
hold time 90;
rs client;
ipv6 {
export filter {
if source = RTS_BGP then accept;
reject;
};
import all;
import keep filtered;
};
}
### END TEMPLATES ###
### START INTERNAL PEERS ###
protocol bgp 'internal_frys-ix2' from internal_peer {
neighbor 2001:db8:2:2::12 as 64001;
}
### END INTERNAL PEERS ###
### START TRANSITS ###
### END TRANSITS ###
### START PEERS ###
### END PEERS ###
### START CUSTOMERS ###
### END CUSTOMERS ###
### START ROUTE SERVER PEERS ###
protocol bgp 'rs_peer_AS65000_amsrtr1' from rs_peer {
neighbor 2001:db8:2:2::100 as 65000;
}
protocol bgp 'rs_peer_AS64513_peer2' from rs_peer {
neighbor 2001:db8:2:2::102 as 64513;
}
protocol bgp 'rs_peer_AS64514_peer3' from rs_peer {
neighbor 2001:db8:2:2::103 as 64514;
}
protocol bgp 'rs_peer_AS64515_peer4' from rs_peer {
neighbor 2001:db8:2:2::104 as 64515;
}
protocol bgp 'rs_peer_AS64516_peer5' from rs_peer {
neighbor 2001:db8:2:2::105 as 64516;
}
### END ROUTE SERVER PEERS ###