464 lines
11 KiB
Plaintext
464 lines
11 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 = 65000;
|
|
define MYIPv6 = 2001:DB8:5000::1;
|
|
define MYNETSET6 = [
|
|
2001:DB8:5000::/48+
|
|
];
|
|
|
|
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;
|
|
}
|
|
|
|
# Do not export routes to kernel if route server
|
|
# IPv6 kernel proto, export learned routes to kernel
|
|
protocol kernel {
|
|
ipv6 {
|
|
import none;
|
|
export filter {
|
|
if source = RTS_STATIC then reject; # Do not import our full network into the kernel, we only announce these to peers transits and sometimes customers.
|
|
krt_prefsrc = MYIPv6; # prefer the loopback IP for outgoing connections.
|
|
accept;
|
|
};
|
|
};
|
|
}
|
|
|
|
# Static route for our network
|
|
protocol static {
|
|
# Create routes for our network without specifying a next hop
|
|
route 2001:DB8:5000::/48 reject;
|
|
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 {
|
|
# desc: Loopback interface
|
|
interface "loop0" {
|
|
cost 100;
|
|
stub yes;
|
|
};
|
|
# desc: To wiertr1
|
|
interface "eth1" {
|
|
cost 100;
|
|
type pointopoint;
|
|
};
|
|
# desc: To server lan
|
|
interface "eth2" {
|
|
cost 100;
|
|
stub yes;
|
|
};
|
|
# desc: To amsrtr1
|
|
interface "eth4" {
|
|
cost 100;
|
|
type pointopoint;
|
|
};
|
|
};
|
|
}
|
|
|
|
### 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_wiertr1' from internal_peer {
|
|
neighbor 2001:DB8:5000::2 as 65000;
|
|
}
|
|
protocol bgp 'internal_amsrtr1' from internal_peer {
|
|
neighbor 2001:DB8:5000::3 as 65000;
|
|
}
|
|
### END INTERNAL PEERS ###
|
|
|
|
### START TRANSITS ###
|
|
protocol bgp 'transit_AS64519_servperso1' from transit {
|
|
neighbor 2001:DB8:0001:0001::1 as 64519;
|
|
}
|
|
### END TRANSITS ###
|
|
|
|
### START PEERS ###
|
|
protocol bgp 'peer_AS64002_locix1' from peer {
|
|
neighbor 2001:db8:2:1::11 as 64002;
|
|
}
|
|
protocol bgp 'peer_AS64002_locix2' from peer {
|
|
neighbor 2001:db8:2:1::12 as 64002;
|
|
}
|
|
### END PEERS ###
|
|
|
|
### START CUSTOMERS ###
|
|
### END CUSTOMERS ###
|
|
|
|
### START ROUTE SERVER PEERS ###
|
|
### END ROUTE SERVER PEERS ###
|