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 return allow_default; if ip_bogon() then return false; if bgp_path ~ ASN_BOGON then return false; if bgp_path.len > 50 then return false; if bad_prefix_len() then return false; if rpki_invalid() then return false; 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 return false; for int path_asn in bgp_path do { if path_asn !~ as_set then return false; } 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 return false; for int path_asn in bgp_path do { if path_asn !~ as_set then return false; } 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 return false; for int path_asn in bgp_path do { if path_asn !~ as_set then return false; } # 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 return false; bgp_large_community.delete([ (MY_ASN, 0..LC_DOWNSTREAM_START-1, *), (MY_ASN, LC_DOWNSTREAM_END+1..0xFFFFFFFF, *) ]); bgp_large_community.add((MY_ASN, LC_INFO, INFO_DOWNSTREAM)); bgp_large_community.add((MY_ASN, LC_PEER_ASN, downstream_asn)); 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; } }