blob: c5668e19b820e0b9d7b279291d4fd605f9c3ec57 [file] [log] [blame]
#!/bin/bash
# Public domain, not copyrighted..
set -u
# number of bgpd instances, not more than 255 at this point. At least 3 are
# needed to connect in a ring.
NUM=7
# The NUM peers can be connected in a ring topology.
#
# This sets the proportion of other peers that each peer should be
# configured to connect to E.g., 20 means each BGP instance will peer with
# 20% of the other peers before and after it in the ring. So 10% of the
# peers prior to this instance in the ring, and 10% of the following peers.
# 100 should lead to a full-mesh, for an odd total number of peers.
#
# A value of 1 will result in each instance having at least 2 peers in the ring.
#
# A value of 0 will disable creating a ring, in which case the only peers
# configured will be those in the EXPEERS list.
PEERPROP=100
# number of routes each BGP instance should advertise
ADV=10
# First octet to use for the IPv4 advertisements. The advertisements
# will be /32s under this /8. E.g. ADVPREF=10 will mean
# 10.x.y.z/32's are advertised.
ADVPREF=10
# Base VTY port to allocate Quagga telnet vtys from. VTYBASE+ID will be
# the port.
VTYBASE=2610
# Base ASN to allocate ASNs to instances.
ASBASE=64500
PREFIX=192.168.145.
#PREFIX=3ffe:123:456::
ADDRPLEN=32
CONFBASE=/tmp
PIDBASE=/var/run/quagga
USER=quagga
GROUP=quagga
# MRAI to specify, where an implementation supports it.
MRAI=1
# Connect retry timer
CONNECTRETRY=1
# The binary locations for BGP instances.
declare -A BGP_BINS=(
[quagga]=/usr/sbin/bgpd
[bird]=/usr/sbin/bird
[birdgit]=/home/paul/code/bird/bird
[quaggagit]=/home/paul/code/quagga/bgpd/bgpd
[exabgp]=/home/paul/code/exabgp/sbin/exabgp
)
# Configuration generation functions for the BGP instances.
declare -A BGP_CONFIGGEN=(
[quagga]=quagga_config
[quaggagit]=quagga_config
[bird]=bird_config
[birdgit]=bird_config
[exabgp]=exabgp_config
)
# Launch functions for the BGP instances.
declare -A BGP_LAUNCH=(
[quagga]=quagga_launch
[quaggagit]=quagga_launch
[bird]=bird_launch
[birdgit]=bird_launch
[quaggagit]=quagga_launch
[exabgp]=exabgp_launch
)
# the instances to run, in the order they should appear in the ring
# (repeated over until there are $NUM instances). The value must exist as a
# key into the above two arrays.
declare -a BGP_INSTANCES=(
quagga
bird
quaggagit
exabgp
)
# Peers to configure, that are external to this script. One list of IPs, with
# corresponding list of their ASes.
#
# e.g.:
#EXPEERS=(192.168.147.{1..10})
#EXPEERASES=($(seq $((ASBASE+11)) $(($ASBASE+20))))
EXPEERS=()
EXPEERASES=()
############################################################################
# Can override any of the above from a supplied file with declarations
CONFWRITE=Y
if [ $# -gt 0 ] ; then
echo "multiple-bgpd.sh: sourcing config from $1"
[ -f "$1" ] && . "$1"
# keep config, if exists
[ $# -gt 1 ] && [ "$2" = "k" ] && CONFWRITE=N
fi
############################################################################
# Internal variables.
# Number of peers for each instance to peer with
PEERNUM=$(( ($NUM-1) * $PEERPROP / 100 ))
[ "$PEERNUM" -gt $(($NUM-1)) ] && PEERNUM=$(($NUM-1))
# the 'range', i.e. how many of the previous and next peers in the ring to
# connect to
PEERRANGE=$(( $PEERNUM/2 ))
[ "$PEERPROP" -gt 0 -a "$NUM" -ge 3 -a "$PEERRANGE" -le 0 ] && PEERRANGE=1
# and a convenience expansion
PEEREXP=""
if [ "$PEERRANGE" -gt 0 ]; then
PEEREXP=($(seq -${PEERRANGE} ${PEERRANGE}))
# dont need 0
unset PEEREXP[PEERRANGE]
fi
#echo ${PEEREXP[@]}
############################################################################
## helpers
# translate instance ID to its address.
id2addr () {
local ID=$1
echo ${PREFIX}${ID}
}
# return the ID of a peer, in terms of an offset on the given instance's ID.
#
# E.g., given an ID of 1 and an offset of -1, if there are 10 instances overall,
# this will return 10.
peeridoff () {
local ID=$1
local OFF=$2
echo $(( (($ID + $OFF - 1 + $NUM) % $NUM) + 1 ))
}
# return IPv4 address to advertise, for given instance ID and number.
advipaddr () {
local ID=$1
local N=$2
echo "$ADVPREF.$(( ($N >> 16) %256 )).$(( ($N >> 8) % 256 )).$(( $N % 256 ))"
}
############################################################################
# launch functions
#
# do not daemonise, so that all launched instances can be killed by killing
# the script.
#
quagga_launch () {
local ID=$1
local ASN=$2
local ADDR=$3
local BIN=$4
local CONF=$5
${BIN} -i "${PIDBASE}"/bgpd${ID}.pid \
-l ${ADDR} \
-f "${CONF}" \
-u $USER -g $GROUP \
-P $((${VTYBASE}+${ID}))
}
exabgp_launch () {
local ID=$1
local ASN=$2
local ADDR=$3
local BIN=$4
local CONF=$5
env exabgp.api.file="${PIDBASE}"/exabgp${ID}.ctl \
exabgp.daemon.pid="${PIDBASE}"/bgpd${ID}.pid \
exabgp.daemon.daemonize=false \
exabgp.tcp.bind=${ADDR} \
exabgp.log.enable=false \
exabgp.daemon.user=quagga \
${BIN} ${CONF}
}
bird_launch () {
local ID=$1
local ASN=$2
local ADDR=$3
local BIN=$4
local CONF=$5
${BIN} -P "${PIDBASE}"/bird${ID}.pid \
-c "${CONF}" \
-s "${PIDBASE}"/bird${ID}.ctl \
-f
}
#######################################################################
#
# functions to write the configuration for instances
#
exabgp_config () {
local ID=$1
local ASN=$2
local ADDR=$3
local N
local P
cat <<- EOF
group default {
local-address $ADDR;
local-as $ASN;
router-id $ADDR;
capability {
asn4 enable;
}
EOF
for N in $(seq 1 $ADV) ; do
echo " static {"
echo " route `advipaddr $ID $N`/32 {"
echo " next-hop $ADDR;"
echo " }"
echo " }"
done
for P in ${PEEREXP[@]}; do
[ "$P" -eq 0 ] && continue;
#local PID=$(( (($ID + $P - 1 + $NUM) % $NUM) + 1 ))
local PID=`peeridoff $ID $P`
#local PADDR="${PREFIX}${PID}"
local PADDR=`id2addr $PID`
local PAS=$((${ASBASE} + $PID))
echo " neighbor $PADDR {"
#echo " local-address $ADDR;"
#echo " local-as $ASN;"
#echo " graceful-restart;"
#echo " router-id $ADDR;"
echo " peer-as $PAS;"
echo " }"
done
for P in ${!EXPEERS[@]}; do
echo " neighbor ${EXPEERS[$P]} {"
echo " peer-as ${EXPEERASES[$P]};"
echo " }"
done
cat <<- EOF
}
EOF
}
quagga_config () {
local ID=$1
local ASN=$2
local ADDR=$3
local N
local P
# Edit config to suit.
cat <<- EOF
password foo
service advanced-vty
!
router bgp ${ASN}
bgp router-id ${ADDR}
!maximum-paths 32
!bgp bestpath as-path multipath-relax
EOF
for N in $(seq 1 $ADV) ; do
echo " network `advipaddr $ID $N`/32"
done
cat <<- EOF
neighbor default peer-group
neighbor default update-source ${ADDR}
neighbor default capability orf prefix-list both
!neighbor default soft-reconfiguration inbound
neighbor default advertisement-interval $MRAI
neighbor default timers connect $CONNECTRETRY
neighbor default route-map test out
EOF
for P in ${PEEREXP[@]}; do
[ "$P" -eq 0 ] && continue;
local PID=`peeridoff $ID $P`
local PADDR=`id2addr $PID`
local PAS=$((${ASBASE} + $PID))
echo " neighbor ${PADDR} remote-as ${PAS}"
echo " neighbor ${PADDR} peer-group default"
done
for P in ${!EXPEERS[@]}; do
echo " neighbor ${EXPEERS[$P]} remote-as ${EXPEERASES[$P]}"
echo " neighbor ${EXPEERS[$P]} peer-group default"
done
cat <<- EOF
!
address-family ipv6
network 3ffe:${ID}::/48
network 3ffe:${ID}:1::/48 pathlimit 1
network 3ffe:${ID}:2::/48 pathlimit 3
network 3ffe:${ID}:3::/48 pathlimit 3
neighbor default activate
neighbor default capability orf prefix-list both
neighbor default default-originate
neighbor default route-map test out
EOF
for P in ${PEEREXP[@]}; do
[ "$P" -eq 0 ] && continue;
local PID=`peeridoff $ID $P`
local PADDR=`id2addr $PID`
local PAS=$((${ASBASE} + $PID))
echo " neighbor ${PADDR} peer-group default"
done
cat <<- EOF
exit-address-family
!
! bgpd still has problems with extcommunity rt/soo
route-map test permit 10
set extcommunity rt ${ASN}:1
set extcommunity soo ${ASN}:2
set community ${ASN}:1
!
line vty
exec-timeout 0 0
!
end
EOF
}
bird_config () {
local ID=$1
local ASN=$2
local ADDR=$3
cat <<- EOF
#log "/var/log/bird.log" all;
#debug protocols all;
# Override router ID
router id ${ADDR};
listen bgp address ${ADDR};
protocol kernel { device routes; import all; }
protocol device { import all; }
function avoid_martians()
prefix set martians;
{
martians = [
224.0.0.0/4+, 240.0.0.0/4+
];
# Avoid RFC1918 and similar networks
if net ~ martians then return false;
return true;
}
filter import_filter
{
if ! (avoid_martians()) then reject;
accept;
}
filter set_comm
{
bgp_community.add ((${ASN}, 1));
accept;
}
template bgp peer_conf {
local as ${ASN};
source address ${ADDR};
import filter import_filter;
export filter set_comm;
multihop;
}
EOF
local P;
for P in ${PEEREXP[@]}; do
[ "$P" -eq 0 ] && continue;
local PID=`peeridoff $ID $P`
local PADDR=`id2addr $PID`
local PAS=$((${ASBASE} + $PID))
echo "protocol bgp from peer_conf {"
echo " neighbor ${PADDR} as ${PAS};"
echo "}"
done
for P in ${!EXPEERS[@]}; do
echo "protocol bgp from peer_conf {"
echo " neighbor ${EXPEERS[$P]} as ${EXPEERASES[$P]};"
echo "}"
done
for N in $(seq 1 $ADV) ; do
echo " network `advipaddr $ID $N`/32"
done
}
#######################################################################
for ID in $(seq 1 $NUM); do
BGP_INST=${BGP_INSTANCES[${ID} % ${#BGP_INSTANCES[@]}]}
BGPBIN=${BGP_BINS[$BGP_INST]}
CONF="${CONFBASE}"/${BGP_INST}_bgpd${ID}.conf
ASN=$(($ASBASE + ${ID}))
ADDR=`id2addr $ID`
#if [ ! -e "$CONF" ] ; then
if [ ! -e "$CONF" -o "$CONFWRITE" = "Y" ] ; then
${BGP_CONFIGGEN[$BGP_INST]} $ID $ASN $ADDR > "$CONF"
chown $USER:$GROUP "$CONF"
fi
# You may want to automatically add configure a local address
# on a loop interface.
#
# Solaris: ifconfig vni${H} plumb ${ADDR}/${ADDRPLEN} up
# Linux:
#ip address add ${ADDR}/${ADDRPLEN} dev lo 2> /dev/null
ip link add dummy${ID} type dummy 2> /dev/null
ip link set dev dummy${ID} up
ip address add ${ADDR}/${ADDRPLEN} dev dummy${ID} 2> /dev/null
${BGP_LAUNCH[$BGP_INST]} $ID $ASN $ADDR $BGPBIN $CONF &
sleep 0.1
done
echo "multiple-bgpd.sh: waiting..."
wait