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) {
|
2024-07-31 20:48:08 -04:00
|
|
|
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;
|
2024-07-31 20:48:08 -04:00
|
|
|
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) {
|
2024-07-31 20:48:08 -04:00
|
|
|
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 {
|
2024-07-31 20:48:08 -04:00
|
|
|
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) {
|
2024-07-31 20:48:08 -04:00
|
|
|
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 {
|
2024-07-31 20:48:08 -04:00
|
|
|
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) {
|
2024-07-31 20:48:08 -04:00
|
|
|
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 {
|
2024-07-31 20:48:08 -04:00
|
|
|
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.
|
2024-07-31 20:48:08 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|