#!/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