From 0070ef25c4e1994933d71529aa7608fb45b395e4 Mon Sep 17 00:00:00 2001 From: Alarig Le Lay Date: Thu, 17 Oct 2024 15:28:02 +0200 Subject: [PATCH] First code sync Signed-off-by: Alarig Le Lay --- generate-irr-filter.sh | 215 +++++++++++++++++++++++++++++++++++++++++ update-check-import.sh | 53 ++++++++++ update-irr-filters.sh | 41 ++++++++ 3 files changed, 309 insertions(+) create mode 100755 generate-irr-filter.sh create mode 100755 update-check-import.sh create mode 100755 update-irr-filters.sh diff --git a/generate-irr-filter.sh b/generate-irr-filter.sh new file mode 100755 index 0000000..1f88ea4 --- /dev/null +++ b/generate-irr-filter.sh @@ -0,0 +1,215 @@ +#!/bin/sh + +set -e + +# for iconv below +export LC_ALL=en_US.utf8 + +# 1st argument must be an ASN registered to peeringdb +# 2nd argument must be the IPv4 peering address +# 3rd argument must be the IPv6 peering address + +if [ ! -f /usr/local/etc/bird-peers-update.conf ]; then + printf "Write /usr/local/etc/bird-peers-update.conf with at least " + printf "PDB_API_KEY, IXP_ASN and BIRD_CONF_PATH vars\n" + exit 1 +else + . /usr/local/etc/bird-peers-update.conf +fi + +PEER_AS=$1 +IPv4=$2 +IPv6=$3 + +if [ -z "${PEER_AS}" -o -z "${IPv4}" -o -z "${IPv6}" ]; then + echo "1st argument must be an ASN registered to peeringdb" + echo "2nd argument must be the IPv4 peering address" + echo "3rd argument must be the IPv6 peering address" + exit 1 +fi + +CACHE_DIR=/var/local/cache/peeringdb + +if [ ! -d "${CACHE_DIR}" ]; then + mkdir -p "${CACHE_DIR}" +fi + +# re-fetch data if it's too old +if [ -z "$(find ${CACHE_DIR} -mmin -720 -name ${PEER_AS}.json)" ]; then + curl -s -H 'Authorization: Api-Key '"${PDB_API_KEY}" \ + -X GET "https://www.peeringdb.com/api/net?asn=${PEER_AS}" \ + > "${CACHE_DIR}/${PEER_AS}.json" +fi + +# test if the data is valid, -e will exit the script +jq -r '.data[] | .irr_as_set' "${CACHE_DIR}/${PEER_AS}.json" 1>/dev/null + +IRRASSET=$(jq -r '.data[] | .irr_as_set' "${CACHE_DIR}/${PEER_AS}.json") +MAXPREF4=$(jq -r '.data[] | .info_prefixes4' "${CACHE_DIR}/${PEER_AS}.json") +MAXPREF6=$(jq -r '.data[] | .info_prefixes6' "${CACHE_DIR}/${PEER_AS}.json") +AS_NAME=$(jq -r '.data[] | .name' "${CACHE_DIR}/${PEER_AS}.json") + +if [ -z "${IRRASSET}" ]; then + IRRASSET="AS${PEER_AS}" +fi + +# Remove diacritic first as potential question marks will remove afterward +AS_NAME="$(echo ${AS_NAME} | iconv -f utf-8 -t ascii//TRANSLIT)" +AS_NAME="$(echo ${AS_NAME} | tr '[:punct:]' '_')" +AS_NAME="$(echo ${AS_NAME} | tr '[:space:]' '_')" +AS_NAME="$(echo ${AS_NAME} | tr '[:lower:]' '[:upper:]')" +AS_NAME="$(echo ${AS_NAME} | sed -E 's/^_+//;s/_+$//;s/_+/_/g')" + +IPv4_PFL="$(bgpq4 -4 -b -A -l AS${PEER_AS}_v4 -R 24 ${IRRASSET})" +IPv6_PFL="$(bgpq4 -6 -b -A -l AS${PEER_AS}_v6 -R 48 ${IRRASSET})" + +if [ -z "${MAXPREF4}" ] || [ ${MAXPREF4} -eq 0 ]; then + MAXPREF4=$(echo "${IPv4_PFL}" | wc -l) +fi +if [ "${MAXPREF4}" -lt 10 ]; then + MAXPREF4=10 +fi +if [ -z "${MAXPREF6}" ] || [ ${MAXPREF6} -eq 0 ]; then + MAXPREF6=$(echo "${IPv6_PFL}" | wc -l) +fi +if [ "${MAXPREF6}" -lt 10 ]; then + MAXPREF6=10 +fi + +# do we really trust peeringdb? we may want to limit maxpref + +mkdir -p "${BIRD_CONF_PATH}"/functions + +# AS16 - AS32 from whois AS47214 +if [ "${PEER_AS}" -gt 65535 ]; then + # la t'es bien + eval $(whois AS47214 | awk '/AS32 to AS16/ { print "AS"$5"="$7";" }' | sed -E 's/AS([0-9]+)/\1/g') + eval COMMU_PEER_AS=\$AS${PEER_AS} +else + COMMU_PEER_AS=${PEER_AS} +fi + +echo "# generated by $0 $@ +function check_import_${PEER_AS}_ipv4(int peeras; ip nexthop) +prefix set AS${PEER_AS}_v4; +{ + ${IPv4_PFL} + + # check if the announced prefix is in the bgpq set + if net !~ AS${PEER_AS}_v4 then return false; + + # reject the route if the first as is not the peer + # or if the nexthop is not the peer as well + # we also check the rpki status there + return check_ipv4(peeras, nexthop); +} + +filter bgp_filters_${PEER_AS}_out { + if ! (source = RTS_BGP) then reject; + if (${IXP_ASN},${COMMU_PEER_AS}) ~ bgp_community then accept; + if (0,${COMMU_PEER_AS}) ~ bgp_community then reject; + if (0,${IXP_ASN}) ~ bgp_community then reject; + if (${IXP_ASN},${IXP_ASN}) ~ bgp_community then accept; + + else accept; +} +" > "${BIRD_CONF_PATH}/functions/${PEER_AS}_v4.conf" + +mkdir -p "${BIRD_CONF_PATH}"/peers + +# bird does not support dots in the filters name, it must be dashes +DASH_IPv4="$(echo ${IPv4} | tr '.' '_')" + +if [ -f "/usr/local/etc/bird-override/bgp_${PEER_AS}_${AS_NAME}_${DASH_IPv4}.conf" ]; then + BIRD_OVERRIDE_AFI_V4="include \"/usr/local/etc/bird-override/bgp_${PEER_AS}_${AS_NAME}_${DASH_IPv4}.conf\";" +else + BIRD_OVERRIDE_AFI_V4="" +fi + +echo "# generated by $0 $@ +filter bgp_filters_${DASH_IPv4}_in_ipv4 { + if (check_import_${PEER_AS}_ipv4(${PEER_AS}, ${IPv4})) then { + # Here we can set localpref or remove a prefix, for example + accept; + } + else { + reject \"Prefix \", net, \" filtered IN from ${IPv4}\"; + } +} + +protocol bgp bgp_${PEER_AS}_${AS_NAME}_${DASH_IPv4} from PEERS_IPv4 { + description \"${AS_NAME} ${IRRASSET}\"; + neighbor ${IPv4} as ${PEER_AS}; + ${BIRD_OVERRIDE_AFI_V4} + + ipv4 { + receive limit ${MAXPREF4} action restart; + import limit ${MAXPREF4} action disable; + import filter bgp_filters_${DASH_IPv4}_in_ipv4; + export filter bgp_filters_${PEER_AS}_out; + }; +} +" > "${BIRD_CONF_PATH}/peers/${PEER_AS}_${IPv4}.conf" + +echo "# generated by $0 $@ +function check_import_${PEER_AS}_ipv6(int peeras; ip nexthop) +prefix set AS${PEER_AS}_v6; +{ + ${IPv6_PFL} + + # check if the announced prefix is in the bgpq set + if net !~ AS${PEER_AS}_v6 then return false; + + # reject the route if the first as is not the peer + # or if the nexthop is not the peer as well + # we also check the rpki status there + return check_ipv6(peeras, nexthop); +} +" > "${BIRD_CONF_PATH}/functions/${PEER_AS}_v6.conf" + +# bird does not support semicollons in the filters name, it must be dashes +DASH_IPv6="$(echo ${IPv6} | tr ':' '_')" + +if [ -f "/usr/local/etc/bird-override/bgp_${PEER_AS}_${AS_NAME}_${DASH_IPv6}.conf" ]; then + BIRD_OVERRIDE_AFI_V6="include \"/usr/local/etc/bird-override/bgp_${PEER_AS}_${AS_NAME}_${DASH_IPv6}.conf\";" +else + BIRD_OVERRIDE_AFI_V6="" +fi + +echo "# generated by $0 $@ +filter bgp_filters_${DASH_IPv6}_in_ipv6 { + if (check_import_${PEER_AS}_ipv6(${PEER_AS}, ${IPv6})) then { + # Here we can set localpref or remove a prefix, for example + accept; + } + else { + reject \"Prefix \", net, \" filtered IN from ${IPv6}\"; + } +} + +protocol bgp bgp_${PEER_AS}_${AS_NAME}_${DASH_IPv6} from PEERS_IPv6 { + description \"${AS_NAME} ${IRRASSET}\"; + neighbor ${IPv6} as ${PEER_AS}; + ${BIRD_OVERRIDE_AFI_V6} + + ipv6 { + receive limit ${MAXPREF6} action restart; + import limit ${MAXPREF6} action disable; + import filter bgp_filters_${DASH_IPv6}_in_ipv6; + export filter bgp_filters_${PEER_AS}_out; + }; +} +" > "${BIRD_CONF_PATH}/peers/${PEER_AS}_${IPv6}.conf" + +echo "# generated by $0 $@ +function check_import_${PEER_AS}_ipv6(int peeras; ip nexthop) +prefix set AS${PEER_AS}_v6; +{ + ${IPv6_PFL} + + if net !~ AS${PEER_AS}_v6 then return false; + + return check_ipv6(peeras, nexthop); +} +" > "${BIRD_CONF_PATH}/functions/${PEER_AS}_v6.conf" + diff --git a/update-check-import.sh b/update-check-import.sh new file mode 100755 index 0000000..8ac8f39 --- /dev/null +++ b/update-check-import.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +set -e + +if [ ! -f /usr/local/etc/bird-peers-update.conf ]; then + printf "Write /usr/local/etc/bird-peers-update.conf with at least " + printf "PDB_API_KEY and BIRD_CONF_PATH vars\n" + exit 1 +else + . /usr/local/etc/bird-peers-update.conf +fi + +# comma list +NEVER_VIA_RS_LIST="$( + curl -H 'Authorization: Api-Key '"${PDB_API_KEY}" \ + -s 'https://www.peeringdb.com/api/net?info_never_via_route_servers=1' | \ + jq '.data[].asn' | sort -n | tr '\n' ',' | sed 's/,/, /g;s/, $//' +)" + +# bird set +NEVER_VIA_RS_SET="$( + printf "[ ${NEVER_VIA_RS_LIST} ]" +)" + + +if [ -n "${NEVER_VIA_RS_SET}" ]; then +printf "# generated by $0 $@ +function check_import(int peeras; ip nexthop) + int set reserved_asn; + int set never_via_rs; +{ + reserved_asn = [ 0, 64297..131071, 4200000000..4294967294, 4294967295 ]; + + never_via_rs = ${NEVER_VIA_RS_SET}; + + # Check that the next AS is our neighbour's. + # Same for next-hop + if bgp_path.first != peeras then return false; + if bgp_next_hop != nexthop then return false; + + # AS_PATH too long (8 because a member could re-annonce its clients, and + # the clients of it clients, and prepend on the IXP) + if bgp_path.len > 8 then return false; + + # Don't accept if path contains a reserved AS + # Disabled because it removes legit prefixes + if bgp_path ~ reserved_asn then return false; + if bgp_path ~ never_via_rs then return false; + + return true; +} +" > "${BIRD_CONF_PATH}"/check_import.conf +fi diff --git a/update-irr-filters.sh b/update-irr-filters.sh new file mode 100755 index 0000000..92eb871 --- /dev/null +++ b/update-irr-filters.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +# Update the 'never on rs' from peeringdb +/usr/local/sbin/update-check-import.sh + +# Genereate the filters and peers using data from peeringdb +/usr/local/sbin/generate-irr-filter.sh 16347 193.34.197.130 2001:7f8:6d::1:6347:1 +#/usr/local/sbin/generate-irr-filter.sh 200780 193.34.197.148 2001:7f8:6d::20:0780:1 # Appliwave a degage +/usr/local/sbin/generate-irr-filter.sh 199917 193.34.197.157 2001:7f8:6d::19:9917:1 +/usr/local/sbin/generate-irr-filter.sh 209823 193.34.197.145 2001:7f8:6d::20:9823:1 +/usr/local/sbin/generate-irr-filter.sh 35625 193.34.197.129 2001:7f8:6d::3:5625:1 +#/usr/local/sbin/generate-irr-filter.sh 30781 193.34.197.154 2001:7f8:6d::3:0781:1 # FreePro pas sur les RS +/usr/local/sbin/generate-irr-filter.sh 35360 193.34.197.144 2001:7f8:6d::3:5360:1 +/usr/local/sbin/generate-irr-filter.sh 34019 193.34.197.156 2001:7f8:6d::3:4019:1 +/usr/local/sbin/generate-irr-filter.sh 29075 193.34.197.140 2001:7f8:6d::2:9075:1 +/usr/local/sbin/generate-irr-filter.sh 25091 193.34.197.153 2001:7f8:6d::2:5091:1 +/usr/local/sbin/generate-irr-filter.sh 8298 193.34.197.143 2001:7f8:6d::8298:1 +/usr/local/sbin/generate-irr-filter.sh 2027 193.34.197.151 2001:7f8:6d::2027:1 +/usr/local/sbin/generate-irr-filter.sh 9036 193.34.197.132 2001:7f8:6d::1:9036:1 +/usr/local/sbin/generate-irr-filter.sh 199758 193.34.197.146 2001:7f8:6d::19:9758:1 +/usr/local/sbin/generate-irr-filter.sh 16276 193.34.197.142 2001:7f8:6d::1:6276:1 +/usr/local/sbin/generate-irr-filter.sh 2200 193.34.197.158 2001:7f8:6d::2200:1 +/usr/local/sbin/generate-irr-filter.sh 62119 193.34.197.162 2001:7f8:6d::6:2119:1 +/usr/local/sbin/generate-irr-filter.sh 206002 193.34.197.163 2001:7f8:6d::20:6002:1 +/usr/local/sbin/generate-irr-filter.sh 31235 193.34.197.133 2001:7f8:6d::3:1235:1 +/usr/local/sbin/generate-irr-filter.sh 210879 193.34.197.161 2001:7f8:6d::21:0879:1 +/usr/local/sbin/generate-irr-filter.sh 197922 193.34.197.131 2001:7f8:6d::19:7922:1 +/usr/local/sbin/generate-irr-filter.sh 210165 193.34.197.152 2001:7f8:6d::21:165:1 +/usr/local/sbin/generate-irr-filter.sh 35661 193.34.197.155 2001:7f8:6d::3:5661:1 +/usr/local/sbin/generate-irr-filter.sh 8218 193.34.197.141 2001:7f8:6d::8218:1 +/usr/local/sbin/generate-irr-filter.sh 6461 193.34.197.149 2001:7f8:6d::6461:1 +/usr/local/sbin/generate-irr-filter.sh 44097 193.34.197.134 2001:7f8:6d::4:4097:1 +/usr/local/sbin/generate-irr-filter.sh 208627 193.34.197.135 2001:7f8:6d::20:8627:1 +/usr/local/sbin/generate-irr-filter.sh 207910 193.34.197.136 2001:7f8:6d::20:7910:1 +/usr/local/sbin/generate-irr-filter.sh 205068 193.34.197.137 2001:7f8:6d::20:5068:1 + +# Check if the config is valid and reload +STATUS=$(birdc configure | grep -v BIRD) +if [ -z "$(echo ${STATUS} | grep 'Reconfigured')" ]; then + echo ${STATUS} +fi