bird-filter/filter_bgp.conf

334 lines
10 KiB
Plaintext
Raw Normal View History

2023-09-16 22:29:30 -04:00
roa4 table rpki4;
roa6 table rpki6;
attribute int export_downstream;
protocol static default_v4 {
ipv4 {};
route 0.0.0.0/0 reject;
}
protocol static default_v6 {
ipv6 {};
route ::/0 reject;
}
# FIXME: Change this to your ASN.
define MY_ASN = 64500;
define LC_IXP_ID = 1;
define LC_PEER_ASN = 2;
define LC_INFO = 3;
define LC_NO_EXPORT = 10;
define LC_PREPEND_1 = 11;
define LC_PREPEND_2 = 12;
define LC_PREPEND_3 = 13;
define LC_DOWNSTREAM_START = 10;
define LC_DOWNSTREAM_END = 13;
# FIXME: define your IXPs here:
# define IXP_EXAMPLE1 = 100;
# define IXP_EXAMPLE2 = 101;
define INFO_PEER = 100;
define INFO_IXP_RS = 101;
define INFO_TRANSIT = 102;
define INFO_DOWNSTREAM = 103;
define IPV4_BOGON = [
0.0.0.0/8+, # RFC 1122 'this' network
10.0.0.0/8+, # RFC 1918 private space
100.64.0.0/10+, # RFC 6598 Carrier grade nat space
127.0.0.0/8+, # RFC 1122 localhost
169.254.0.0/16+, # RFC 3927 link local
172.16.0.0/12+, # RFC 1918 private space
192.0.2.0/24+, # RFC 5737 TEST-NET-1
192.88.99.0/24+, # RFC 7526 6to4 anycast relay
192.168.0.0/16+, # RFC 1918 private space
198.18.0.0/15+, # RFC 2544 benchmarking
198.51.100.0/24+, # RFC 5737 TEST-NET-2
203.0.113.0/24+, # RFC 5737 TEST-NET-3
224.0.0.0/4+, # multicast
240.0.0.0/4+ # reserved
];
define IPV6_BOGON = [
::/0, # Default
::/96, # IPv4-compatible IPv6 address - deprecated by RFC4291
::/128, # Unspecified address
::1/128, # Local host loopback address
::ffff:0.0.0.0/96+, # IPv4-mapped addresses
::224.0.0.0/100+, # Compatible address (IPv4 format)
::127.0.0.0/104+, # Compatible address (IPv4 format)
::0.0.0.0/104+, # Compatible address (IPv4 format)
::255.0.0.0/104+, # Compatible address (IPv4 format)
0000::/8+, # Pool used for unspecified, loopback and embedded IPv4 addresses
0100::/8+, # RFC 6666 - reserved for Discard-Only Address Block
0200::/7+, # OSI NSAP-mapped prefix set (RFC4548) - deprecated by RFC4048
0400::/6+, # RFC 4291 - Reserved by IETF
0800::/5+, # RFC 4291 - Reserved by IETF
1000::/4+, # RFC 4291 - Reserved by IETF
2001:10::/28+, # RFC 4843 - Deprecated (previously ORCHID)
2001:20::/28+, # RFC 7343 - ORCHIDv2
2001:db8::/32+, # Reserved by IANA for special purposes and documentation
2002:e000::/20+, # Invalid 6to4 packets (IPv4 multicast)
2002:7f00::/24+, # Invalid 6to4 packets (IPv4 loopback)
2002:0000::/24+, # Invalid 6to4 packets (IPv4 default)
2002:ff00::/24+, # Invalid 6to4 packets
2002:0a00::/24+, # Invalid 6to4 packets (IPv4 private 10.0.0.0/8 network)
2002:ac10::/28+, # Invalid 6to4 packets (IPv4 private 172.16.0.0/12 network)
2002:c0a8::/32+, # Invalid 6to4 packets (IPv4 private 192.168.0.0/16 network)
3ffe::/16+, # Former 6bone, now decommissioned
4000::/3+, # RFC 4291 - Reserved by IETF
5f00::/8+, # RFC 5156 - used for the 6bone but was returned
6000::/3+, # RFC 4291 - Reserved by IETF
8000::/3+, # RFC 4291 - Reserved by IETF
a000::/3+, # RFC 4291 - Reserved by IETF
c000::/3+, # RFC 4291 - Reserved by IETF
e000::/4+, # RFC 4291 - Reserved by IETF
f000::/5+, # RFC 4291 - Reserved by IETF
f800::/6+, # RFC 4291 - Reserved by IETF
fc00::/7+, # Unicast Unique Local Addresses (ULA) - RFC 4193
fe80::/10+, # Link-local Unicast
fec0::/10+, # Site-local Unicast - deprecated by RFC 3879 (replaced by ULA)
ff00::/8+ # Multicast
];
define ASN_BOGON = [
0, # RFC 7607
23456, # RFC 4893 AS_TRANS
64496..64511, # RFC 5398 and documentation/example ASNs
64512..65534, # RFC 6996 Private 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
];
function ip_bogon() {
case net.type {
NET_IP4: return net ~ IPV4_BOGON;
NET_IP6: return net ~ IPV6_BOGON;
else: return true;
}
}
function rpki_invalid() {
case net.type {
NET_IP4: return roa_check(rpki4, net, bgp_path.last) = ROA_INVALID;
NET_IP6: return roa_check(rpki6, net, bgp_path.last) = ROA_INVALID;
else: return false;
}
}
function is_default_route() {
case net.type {
NET_IP4: return net = 0.0.0.0/0;
NET_IP6: return net = ::/0;
else: return false;
}
}
function bad_prefix_len() {
case net.type {
NET_IP4: return net.len > 24;
NET_IP6: return net.len > 48;
else: return false;
}
}
function clean_own_communities() {
bgp_large_community.delete([(MY_ASN, *, *)]);
}
function honour_graceful_shutdown() {
# RFC 8326: Graceful BGP Session Shutdown
if (65535, 0) ~ bgp_community then bgp_local_pref = 0;
}
function handle_prepend(int dest_asn) {
if (MY_ASN, LC_PREPEND_1, dest_asn) ~ bgp_large_community then {
bgp_path.prepend(MY_ASN);
}
if (MY_ASN, LC_PREPEND_2, dest_asn) ~ bgp_large_community then {
bgp_path.prepend(MY_ASN);
bgp_path.prepend(MY_ASN);
}
if (MY_ASN, LC_PREPEND_3, dest_asn) ~ bgp_large_community then {
bgp_path.prepend(MY_ASN);
bgp_path.prepend(MY_ASN);
bgp_path.prepend(MY_ASN);
}
}
function import_safe(bool allow_default) {
if is_default_route() then {
if allow_default then return true;
print proto, ": ", net, ": unexpected default route";
return false;
}
if ip_bogon() then {
print proto, ": ", net, ": bogon prefix";
return false;
}
if bgp_path ~ ASN_BOGON then {
print proto, ": ", net, ": bogon in AS path: ", bgp_path;
return false;
}
if bgp_path.len > 50 then {
print proto, ": ", net, ": AS path too long: ", bgp_path;
return false;
}
if bad_prefix_len() then {
print proto, ": ", net, ": invalid prefix length";
return false;
}
if rpki_invalid() then {
2024-10-29 21:36:28 -04:00
print proto, ": ", net, ": invalid RPKI origin: ", bgp_path.last;
return false;
}
2023-09-16 22:29:30 -04:00
export_downstream = 1;
honour_graceful_shutdown();
return true;
}
function import_peer_trusted(int peer_asn) {
clean_own_communities();
bgp_large_community.add((MY_ASN, LC_INFO, INFO_PEER));
bgp_large_community.add((MY_ASN, LC_PEER_ASN, peer_asn));
return import_safe(false);
}
function import_peer(int peer_asn; prefix set prefixes; int set as_set) {
if net !~ prefixes then {
print proto, ": ", net, ": prefix not in as-set for peer AS", peer_asn;
return false;
}
2023-09-16 22:29:30 -04:00
for int path_asn in bgp_path do {
if path_asn !~ as_set then {
print proto, ": ", net, ": AS", path_asn, " not in as-set for peer AS", peer_asn;
return false;
}
2023-09-16 22:29:30 -04:00
}
return import_peer_trusted(peer_asn);
}
function import_ixp_trusted(int ixp_id) {
clean_own_communities();
bgp_large_community.add((MY_ASN, LC_INFO, INFO_IXP_RS));
bgp_large_community.add((MY_ASN, LC_IXP_ID, ixp_id));
return import_safe(false);
}
function import_ixp(int ixp_id; prefix set prefixes; int set as_set) {
if net !~ prefixes then {
print proto, ": ", net, ": prefix not in as-set for IXP";
return false;
}
2023-09-16 22:29:30 -04:00
for int path_asn in bgp_path do {
if path_asn !~ as_set then {
print proto, ": ", net, ": not in as-set for IXP";
return false;
}
2023-09-16 22:29:30 -04:00
}
return import_ixp_trusted(ixp_id);
}
function import_transit(int transit_asn; bool default_route) {
clean_own_communities();
bgp_large_community.add((MY_ASN, LC_INFO, INFO_TRANSIT));
bgp_large_community.add((MY_ASN, LC_PEER_ASN, transit_asn));
return import_safe(default_route);
}
function import_downstream(int downstream_asn; prefix set prefixes; int set as_set) {
if net !~ prefixes then {
print proto, ": ", net, ": prefix not in as-set for downstream AS", downstream_asn;
return false;
}
2023-09-16 22:29:30 -04:00
for int path_asn in bgp_path do {
if path_asn !~ as_set then {
print proto, ": ", net, ": not in as-set for downstream AS", downstream_asn;
return false;
}
2023-09-16 22:29:30 -04:00
}
# If they don't want to export this to us, then we won't take it at all.
if (MY_ASN, LC_NO_EXPORT, MY_ASN) ~ bgp_large_community then {
print proto, ": ", net, ": rejected by no-export to AS", MY_ASN;
return false;
}
2023-09-16 22:29:30 -04:00
bgp_large_community.delete([
2023-09-26 21:01:41 -04:00
(MY_ASN, 0..LC_DOWNSTREAM_START-1, *),
(MY_ASN, LC_DOWNSTREAM_END+1..0xFFFFFFFF, *)
2023-09-16 22:29:30 -04:00
]);
2023-09-26 21:01:41 -04:00
bgp_large_community.add((MY_ASN, LC_INFO, INFO_DOWNSTREAM));
bgp_large_community.add((MY_ASN, LC_PEER_ASN, downstream_asn));
2023-09-16 22:29:30 -04:00
return import_safe(false);
}
function export_to_downstream() {
return (defined(export_downstream) && export_downstream = 1) ||
(source = RTS_STATIC && !bad_prefix_len() && (
proto = "node_v4" ||
proto = "node_v6" ||
proto = "node_v4_anycast" ||
proto = "node_v6_anycast"
));
}
function export_monitoring() {
return export_to_downstream();
}
function export_cone(int dest_asn) {
if (MY_ASN, LC_NO_EXPORT, dest_asn) ~ bgp_large_community then return false;
handle_prepend(dest_asn);
if (MY_ASN, LC_INFO, INFO_DOWNSTREAM) ~ bgp_large_community then return true;
case net.type {
NET_IP4: return source = RTS_STATIC && proto = "node_v4";
NET_IP6: return source = RTS_STATIC && proto = "node_v6";
else: return false;
}
}
function export_anycast() {
case net.type {
NET_IP4: return source = RTS_STATIC && proto = "node_v4_anycast";
NET_IP6: return source = RTS_STATIC && proto = "node_v6_anycast";
else: return false;
}
}
function export_default() {
case net.type {
NET_IP4: return source = RTS_STATIC && proto = "default_v4";
NET_IP6: return source = RTS_STATIC && proto = "default_v6";
else: return false;
}
}