Skip to content

Instantly share code, notes, and snippets.

@borouhin
Last active May 7, 2022 09:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save borouhin/880a38d055a53024cd47ecae68f6f9f5 to your computer and use it in GitHub Desktop.
Save borouhin/880a38d055a53024cd47ecae68f6f9f5 to your computer and use it in GitHub Desktop.
Make Bird 2 config for serving RKN blocked routes
#!/bin/bash
# Requires libnet-cidr-lite-perl installed
# Best practices
set -Eeuo pipefail
# Local WAN Interface (using interface, not IP, so that we need not change the script on WAN IP changes)
WAN_IFACE='eth0'
# Local AS
LOCAL_AS='64999'
# Peers receiving full non-summarized prefix list. Space delimited, in 'IP/AS' format
PEERS_32='XX.XX.XX.XX/65000'
# Peers receiving prefix list summarized to /24 subnets. Space delimited, in 'IP/AS' format
PEERS_24='XX.XX.XX.XX/65000'
# URLs to download prefix lists from, space delimited. Should contain one 'IP/MASK' entry for each line ('/32' for single IPs is optional)
LIST_URLS='https://antifilter.download/list/ip.lst https://antifilter.download/list/ipresolve.lst https://antifilter.download/list/subnet.lst'
# Individual items to add to prefix lists. Can be single IPv4 IPs, IPv4 subnets in 'IP/MASK' format, autonomous systems ('ASXXX') or FQDNs
MANUAL_ITEMS='dav.mailbox.org login.mailbox.org help.mailbox.org rutracker.org AS32934 AS13414'
# Temporary file path (deleted after each run). Make sure the path is writable
TMP_FILE='/tmp/routes.lst'
# BIRD configuration file path
CONF_FILE='/etc/bird/bird.conf'
# birdc executable
BIRDC='/usr/sbin/birdc'
# Getting local WAN IPv4 address
IP_ADDRESS=`ip address show dev $WAN_IFACE | grep 'inet ' | sed 's/\s*inet \([0-9.]*\)\/.*/\1/'`
if [[ ! $IP_ADDRESS =~ [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} ]] ; then
>&2 echo "Error getting IPv4 address of interface $WAN_IFACE"
exit 1
fi
# Downloading lists from URLs
if [[ -e $TMP_FILE ]] ; then
rm $TMP_FILE
fi
IFS=' '
# Curl sends diagnostic data to stderr, so to use this script in crontab, we need extra options and temporary files
INDEX=1
for url in $LIST_URLS ; do
curl -s -S -L $url -o $TMP_FILE.$INDEX
if [ $? != '0' ] ; then
>&2 echo "Error retrieving IP list $url"
exit 2
fi
# For single IPs - add /32 mask
cat $TMP_FILE.$INDEX | sed 's/\(^[^/]*$\)/\1\/32/' >>$TMP_FILE
rm $TMP_FILE.$INDEX
INDEX=$((INDEX+1))
done
# Adding manual items. Errors should (hopefully) be dismissed silently
IFS=' '
for item in $MANUAL_ITEMS ; do
# Subnets, adding as is
if [[ $item =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,3}$ ]] ; then
echo "$item" >>$TMP_FILE
# Single IPs, adding with '/32' mask
elif [[ $item =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] ; then
echo "$item/32" >>$TMP_FILE
# Autonomous systems, add all address ranges within
elif [[ $item =~ ^AS[0-9]{1,5}$ ]] ; then
whois -h whois.radb.net -- "-i origin $item" | grep 'route:' | awk '{print $2;}' >>$TMP_FILE
# Hostnames, resolving A DNS records and adding a single IP for each
else
RESOLVED_LINES=`host -4 -t A $item`
if [ $? == '0' ] ; then
IFS=$'\n'
for line in $RESOLVED_LINES ; do
RESOLVED_IP=`echo $line | sed 's/^[^0-9]*\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\)/\1/;t;d'`
if [[ $RESOLVED_IP != "" ]] ; then
echo "$RESOLVED_IP/32" >>$TMP_FILE
fi
done
fi
fi
done
# Summarizing IPs using Perl Net::CIDR::Lite module (lightning fast)
#
# For full prefix list - just compact it summarizing overlapping or adjacent prefixes, no extra IPs added
cat $TMP_FILE | perl -MNet::CIDR::Lite -E 'my $cidr=Net::CIDR::Lite->new; foreach $line(<>) { $cidr->add($line); } foreach my $item($cidr->list) { print "$item\n"; }' >$TMP_FILE.sum32
ERROR_1=$?
# For summarized prefix list - first broaden all masks to at least /24, then process the same way
cat $TMP_FILE | sed 's/\(.*\/\)3[0-2]$/\124/' | sed 's/\(.*\/\)2[5-9]$/\124/' >$TMP_FILE.raw24
ERROR_2=$?
cat $TMP_FILE.raw24 | perl -MNet::CIDR::Lite -E 'my $cidr=Net::CIDR::Lite->new; foreach $line(<>) { $cidr->add($line); } foreach my $item($cidr->list) { print "$item\n"; }' >$TMP_FILE.sum24
ERROR_3=$?
rm $TMP_FILE.raw24
rm $TMP_FILE
if [[ ($ERROR_1 != '0') || ($ERROR_2 != '0') || ($ERROR_3 != '0') || (! -e $TMP_FILE.sum32) || (! -e $TMP_FILE.sum24) ]] ; then
>&2 echo "Error summarizing IPs ($ERROR_1 $ERROR_2 $ERROR_3)"
rm $TMP_FILE.sum32 2>/dev/null
rm $TMP_FILE.sum24 2>/dev/null
exit 3
fi
# Config file header
cat > $CONF_FILE.new <<- EOM
log syslog all;
router id $IP_ADDRESS;
protocol device {
scan time 10;
}
protocol kernel {
ipv4 {
export where proto = "static_bgp_32";
import none;
};
persist;
learn;
}
protocol static static_bgp_32 {
ipv4;
EOM
# Filling full prefix list
IFS=$'\n'
for line in `cat $TMP_FILE.sum32` ; do
echo " route $line blackhole;" >>$CONF_FILE.new
done
rm $TMP_FILE.sum32
cat >> $CONF_FILE.new <<- EOM
}
protocol static static_bgp_24 {
ipv4;
EOM
# Filling summarized prefix list
IFS=$'\n'
for line in `cat $TMP_FILE.sum24` ; do
echo " route $line blackhole;" >>$CONF_FILE.new
done
rm $TMP_FILE.sum24
# Peer definition templates
cat >> $CONF_FILE.new <<- EOM
}
template bgp PEER {
local as $LOCAL_AS;
multihop 255;
hold time 240;
keepalive time 30;
rs client;
}
template bgp PEER_32 from PEER {
ipv4 {
import none;
export where proto = "static_bgp_32";
};
}
template bgp PEER_24 from PEER {
ipv4 {
import none;
export where proto = "static_bgp_24";
};
}
EOM
# Peers receiving full list
IFS=' '
for peer in $PEERS_32; do
cat >> $CONF_FILE.new <<- EOM
protocol bgp from PEER_32 {
neighbor `echo $peer | awk -F'/' '{print $1" as "$2";";}'`
}
EOM
done
# Peers receiving summarized list
IFS=' '
for peer in $PEERS_24; do
cat >> $CONF_FILE.new <<- EOM
protocol bgp from PEER_24 {
neighbor `echo $peer | awk -F'/' '{print $1" as "$2";";}'`
}
EOM
done
# Reconfiguring BIRD. Reverting to previous config on error
cp $CONF_FILE $CONF_FILE.old
mv $CONF_FILE.new $CONF_FILE
$BIRDC configure
if [ $? != '0' ] ; then
>&2 echo "Error reconfiguring BIRD. Reverting to old config"
mv $CONF_FILE.old $CONF_FILE
$BIRDC configure
exit 4
else
rm $CONF_FILE.old
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment