Paul Jakma | 3a02d1f | 2007-11-01 14:29:11 +0000 | [diff] [blame] | 1 | #!/bin/bash |
| 2 | |
| 3 | # Public domain, not copyrighted.. |
| 4 | |
Paul Jakma | 366bb4a | 2016-05-17 13:59:55 +0100 | [diff] [blame^] | 5 | set -u |
| 6 | |
| 7 | # number of bgpd instances, not more than 255 at this point. At least 3 are |
| 8 | # needed to connect in a ring. |
| 9 | NUM=7 |
| 10 | |
| 11 | # The NUM peers can be connected in a ring topology. |
| 12 | # |
| 13 | # This sets the proportion of other peers that each peer should be |
| 14 | # configured to connect to E.g., 20 means each BGP instance will peer with |
| 15 | # 20% of the other peers before and after it in the ring. So 10% of the |
| 16 | # peers prior to this instance in the ring, and 10% of the following peers. |
| 17 | # 100 should lead to a full-mesh, for an odd total number of peers. |
| 18 | # |
| 19 | # A value of 1 will result in each instance having at least 2 peers in the ring. |
| 20 | # |
| 21 | # A value of 0 will disable creating a ring, in which case the only peers |
| 22 | # configured will be those in the EXPEERS list. |
| 23 | PEERPROP=100 |
| 24 | |
| 25 | # number of routes each BGP instance should advertise |
| 26 | ADV=10 |
| 27 | # First octet to use for the IPv4 advertisements. The advertisements |
| 28 | # will be /32s under this /8. E.g. ADVPREF=10 will mean |
| 29 | # 10.x.y.z/32's are advertised. |
| 30 | ADVPREF=10 |
| 31 | |
| 32 | # Base VTY port to allocate Quagga telnet vtys from. VTYBASE+ID will be |
| 33 | # the port. |
Paul Jakma | 3a02d1f | 2007-11-01 14:29:11 +0000 | [diff] [blame] | 34 | VTYBASE=2610 |
Paul Jakma | 366bb4a | 2016-05-17 13:59:55 +0100 | [diff] [blame^] | 35 | # Base ASN to allocate ASNs to instances. |
| 36 | ASBASE=64500 |
Paul Jakma | f04b0e6 | 2008-07-21 18:43:04 +0000 | [diff] [blame] | 37 | PREFIX=192.168.145. |
| 38 | #PREFIX=3ffe:123:456:: |
| 39 | ADDRPLEN=32 |
Paul Jakma | 0e7c124 | 2008-06-01 14:26:48 +0000 | [diff] [blame] | 40 | CONFBASE=/tmp |
| 41 | PIDBASE=/var/run/quagga |
Paul Jakma | 366bb4a | 2016-05-17 13:59:55 +0100 | [diff] [blame^] | 42 | USER=quagga |
| 43 | GROUP=quagga |
Paul Jakma | 3a02d1f | 2007-11-01 14:29:11 +0000 | [diff] [blame] | 44 | |
Paul Jakma | 366bb4a | 2016-05-17 13:59:55 +0100 | [diff] [blame^] | 45 | # MRAI to specify, where an implementation supports it. |
| 46 | MRAI=1 |
| 47 | # Connect retry timer |
| 48 | CONNECTRETRY=1 |
| 49 | |
| 50 | # The binary locations for BGP instances. |
| 51 | declare -A BGP_BINS=( |
| 52 | [quagga]=/usr/sbin/bgpd |
| 53 | [bird]=/usr/sbin/bird |
| 54 | [birdgit]=/home/paul/code/bird/bird |
| 55 | [quaggagit]=/home/paul/code/quagga/bgpd/bgpd |
| 56 | [exabgp]=/home/paul/code/exabgp/sbin/exabgp |
| 57 | ) |
| 58 | |
| 59 | # Configuration generation functions for the BGP instances. |
| 60 | declare -A BGP_CONFIGGEN=( |
| 61 | [quagga]=quagga_config |
| 62 | [quaggagit]=quagga_config |
| 63 | [bird]=bird_config |
| 64 | [birdgit]=bird_config |
| 65 | [exabgp]=exabgp_config |
| 66 | ) |
| 67 | |
| 68 | # Launch functions for the BGP instances. |
| 69 | declare -A BGP_LAUNCH=( |
| 70 | [quagga]=quagga_launch |
| 71 | [quaggagit]=quagga_launch |
| 72 | [bird]=bird_launch |
| 73 | [birdgit]=bird_launch |
| 74 | [quaggagit]=quagga_launch |
| 75 | [exabgp]=exabgp_launch |
| 76 | ) |
| 77 | |
| 78 | # the instances to run, in the order they should appear in the ring |
| 79 | # (repeated over until there are $NUM instances). The value must exist as a |
| 80 | # key into the above two arrays. |
| 81 | declare -a BGP_INSTANCES=( |
| 82 | quagga |
| 83 | bird |
| 84 | quaggagit |
| 85 | exabgp |
| 86 | ) |
| 87 | |
| 88 | # Peers to configure, that are external to this script. One list of IPs, with |
| 89 | # corresponding list of their ASes. |
| 90 | # |
| 91 | # e.g.: |
| 92 | #EXPEERS=(192.168.147.{1..10}) |
| 93 | #EXPEERASES=($(seq $((ASBASE+11)) $(($ASBASE+20)))) |
| 94 | |
| 95 | EXPEERS=() |
| 96 | EXPEERASES=() |
| 97 | |
| 98 | ############################################################################ |
| 99 | # Can override any of the above from a supplied file with declarations |
| 100 | CONFWRITE=Y |
| 101 | if [ $# -gt 0 ] ; then |
| 102 | echo "multiple-bgpd.sh: sourcing config from $1" |
| 103 | [ -f "$1" ] && . "$1" |
Paul Jakma | 3a02d1f | 2007-11-01 14:29:11 +0000 | [diff] [blame] | 104 | |
Paul Jakma | 366bb4a | 2016-05-17 13:59:55 +0100 | [diff] [blame^] | 105 | # keep config, if exists |
| 106 | [ $# -gt 1 ] && [ "$2" = "k" ] && CONFWRITE=N |
| 107 | fi |
| 108 | |
| 109 | ############################################################################ |
| 110 | # Internal variables. |
| 111 | |
| 112 | # Number of peers for each instance to peer with |
| 113 | PEERNUM=$(( ($NUM-1) * $PEERPROP / 100 )) |
| 114 | [ "$PEERNUM" -gt $(($NUM-1)) ] && PEERNUM=$(($NUM-1)) |
| 115 | |
| 116 | # the 'range', i.e. how many of the previous and next peers in the ring to |
| 117 | # connect to |
| 118 | PEERRANGE=$(( $PEERNUM/2 )) |
| 119 | [ "$PEERPROP" -gt 0 -a "$NUM" -ge 3 -a "$PEERRANGE" -le 0 ] && PEERRANGE=1 |
| 120 | |
| 121 | # and a convenience expansion |
| 122 | PEEREXP="" |
| 123 | if [ "$PEERRANGE" -gt 0 ]; then |
| 124 | PEEREXP=($(seq -${PEERRANGE} ${PEERRANGE})) |
| 125 | # dont need 0 |
| 126 | unset PEEREXP[PEERRANGE] |
| 127 | fi |
| 128 | |
| 129 | #echo ${PEEREXP[@]} |
| 130 | |
| 131 | ############################################################################ |
| 132 | ## helpers |
| 133 | |
| 134 | # translate instance ID to its address. |
| 135 | id2addr () { |
| 136 | local ID=$1 |
| 137 | echo ${PREFIX}${ID} |
| 138 | } |
| 139 | |
| 140 | # return the ID of a peer, in terms of an offset on the given instance's ID. |
| 141 | # |
| 142 | # E.g., given an ID of 1 and an offset of -1, if there are 10 instances overall, |
| 143 | # this will return 10. |
| 144 | peeridoff () { |
| 145 | local ID=$1 |
| 146 | local OFF=$2 |
| 147 | echo $(( (($ID + $OFF - 1 + $NUM) % $NUM) + 1 )) |
| 148 | } |
| 149 | |
| 150 | # return IPv4 address to advertise, for given instance ID and number. |
| 151 | advipaddr () { |
| 152 | local ID=$1 |
| 153 | local N=$2 |
| 154 | echo "$ADVPREF.$(( ($N >> 16) %256 )).$(( ($N >> 8) % 256 )).$(( $N % 256 ))" |
| 155 | } |
| 156 | |
| 157 | ############################################################################ |
| 158 | # launch functions |
| 159 | # |
| 160 | # do not daemonise, so that all launched instances can be killed by killing |
| 161 | # the script. |
| 162 | # |
| 163 | |
| 164 | quagga_launch () { |
| 165 | local ID=$1 |
| 166 | local ASN=$2 |
| 167 | local ADDR=$3 |
| 168 | local BIN=$4 |
| 169 | local CONF=$5 |
| 170 | ${BIN} -i "${PIDBASE}"/bgpd${ID}.pid \ |
| 171 | -l ${ADDR} \ |
| 172 | -f "${CONF}" \ |
| 173 | -u $USER -g $GROUP \ |
| 174 | -P $((${VTYBASE}+${ID})) |
| 175 | } |
| 176 | |
| 177 | exabgp_launch () { |
| 178 | local ID=$1 |
| 179 | local ASN=$2 |
| 180 | local ADDR=$3 |
| 181 | local BIN=$4 |
| 182 | local CONF=$5 |
| 183 | |
| 184 | env exabgp.api.file="${PIDBASE}"/exabgp${ID}.ctl \ |
| 185 | exabgp.daemon.pid="${PIDBASE}"/bgpd${ID}.pid \ |
| 186 | exabgp.daemon.daemonize=false \ |
| 187 | exabgp.tcp.bind=${ADDR} \ |
| 188 | exabgp.log.enable=false \ |
| 189 | exabgp.daemon.user=quagga \ |
| 190 | ${BIN} ${CONF} |
| 191 | } |
| 192 | |
| 193 | bird_launch () { |
| 194 | local ID=$1 |
| 195 | local ASN=$2 |
| 196 | local ADDR=$3 |
| 197 | local BIN=$4 |
| 198 | local CONF=$5 |
| 199 | ${BIN} -P "${PIDBASE}"/bird${ID}.pid \ |
| 200 | -c "${CONF}" \ |
| 201 | -s "${PIDBASE}"/bird${ID}.ctl \ |
| 202 | -f |
| 203 | } |
| 204 | |
| 205 | ####################################################################### |
| 206 | # |
| 207 | # functions to write the configuration for instances |
| 208 | # |
| 209 | |
| 210 | exabgp_config () { |
| 211 | local ID=$1 |
| 212 | local ASN=$2 |
| 213 | local ADDR=$3 |
| 214 | |
| 215 | local N |
| 216 | local P |
| 217 | |
| 218 | cat <<- EOF |
| 219 | group default { |
| 220 | local-address $ADDR; |
| 221 | local-as $ASN; |
| 222 | router-id $ADDR; |
| 223 | |
| 224 | capability { |
| 225 | asn4 enable; |
| 226 | } |
| 227 | EOF |
| 228 | |
| 229 | for N in $(seq 1 $ADV) ; do |
| 230 | echo " static {" |
| 231 | echo " route `advipaddr $ID $N`/32 {" |
| 232 | echo " next-hop $ADDR;" |
| 233 | echo " }" |
| 234 | echo " }" |
| 235 | done |
| 236 | |
| 237 | for P in ${PEEREXP[@]}; do |
| 238 | [ "$P" -eq 0 ] && continue; |
Paul Jakma | 3a02d1f | 2007-11-01 14:29:11 +0000 | [diff] [blame] | 239 | |
Paul Jakma | 366bb4a | 2016-05-17 13:59:55 +0100 | [diff] [blame^] | 240 | #local PID=$(( (($ID + $P - 1 + $NUM) % $NUM) + 1 )) |
| 241 | local PID=`peeridoff $ID $P` |
| 242 | #local PADDR="${PREFIX}${PID}" |
| 243 | local PADDR=`id2addr $PID` |
| 244 | local PAS=$((${ASBASE} + $PID)) |
| 245 | |
| 246 | echo " neighbor $PADDR {" |
| 247 | #echo " local-address $ADDR;" |
| 248 | #echo " local-as $ASN;" |
| 249 | #echo " graceful-restart;" |
| 250 | #echo " router-id $ADDR;" |
| 251 | echo " peer-as $PAS;" |
| 252 | echo " }" |
| 253 | done |
| 254 | |
| 255 | for P in ${!EXPEERS[@]}; do |
| 256 | echo " neighbor ${EXPEERS[$P]} {" |
| 257 | echo " peer-as ${EXPEERASES[$P]};" |
| 258 | echo " }" |
| 259 | done |
| 260 | |
| 261 | cat <<- EOF |
| 262 | } |
| 263 | EOF |
| 264 | } |
| 265 | |
| 266 | quagga_config () { |
| 267 | local ID=$1 |
| 268 | local ASN=$2 |
| 269 | local ADDR=$3 |
| 270 | |
| 271 | local N |
| 272 | local P |
| 273 | |
| 274 | # Edit config to suit. |
| 275 | cat <<- EOF |
| 276 | password foo |
| 277 | service advanced-vty |
| 278 | ! |
| 279 | router bgp ${ASN} |
| 280 | bgp router-id ${ADDR} |
| 281 | !maximum-paths 32 |
| 282 | !bgp bestpath as-path multipath-relax |
| 283 | EOF |
| 284 | |
| 285 | for N in $(seq 1 $ADV) ; do |
| 286 | echo " network `advipaddr $ID $N`/32" |
| 287 | done |
| 288 | |
| 289 | cat <<- EOF |
| 290 | neighbor default peer-group |
| 291 | neighbor default update-source ${ADDR} |
| 292 | neighbor default capability orf prefix-list both |
| 293 | !neighbor default soft-reconfiguration inbound |
| 294 | neighbor default advertisement-interval $MRAI |
| 295 | neighbor default timers connect $CONNECTRETRY |
| 296 | neighbor default route-map test out |
| 297 | EOF |
| 298 | |
| 299 | for P in ${PEEREXP[@]}; do |
| 300 | [ "$P" -eq 0 ] && continue; |
| 301 | |
| 302 | local PID=`peeridoff $ID $P` |
| 303 | local PADDR=`id2addr $PID` |
| 304 | local PAS=$((${ASBASE} + $PID)) |
| 305 | echo " neighbor ${PADDR} remote-as ${PAS}" |
| 306 | echo " neighbor ${PADDR} peer-group default" |
| 307 | done |
| 308 | |
| 309 | for P in ${!EXPEERS[@]}; do |
| 310 | echo " neighbor ${EXPEERS[$P]} remote-as ${EXPEERASES[$P]}" |
| 311 | echo " neighbor ${EXPEERS[$P]} peer-group default" |
| 312 | done |
| 313 | |
| 314 | cat <<- EOF |
| 315 | ! |
| 316 | address-family ipv6 |
| 317 | network 3ffe:${ID}::/48 |
| 318 | network 3ffe:${ID}:1::/48 pathlimit 1 |
| 319 | network 3ffe:${ID}:2::/48 pathlimit 3 |
| 320 | network 3ffe:${ID}:3::/48 pathlimit 3 |
| 321 | neighbor default activate |
| 322 | neighbor default capability orf prefix-list both |
| 323 | neighbor default default-originate |
| 324 | neighbor default route-map test out |
| 325 | EOF |
| 326 | |
| 327 | for P in ${PEEREXP[@]}; do |
| 328 | [ "$P" -eq 0 ] && continue; |
| 329 | |
| 330 | local PID=`peeridoff $ID $P` |
| 331 | local PADDR=`id2addr $PID` |
| 332 | local PAS=$((${ASBASE} + $PID)) |
| 333 | echo " neighbor ${PADDR} peer-group default" |
| 334 | done |
| 335 | |
| 336 | cat <<- EOF |
| 337 | exit-address-family |
| 338 | ! |
| 339 | ! bgpd still has problems with extcommunity rt/soo |
| 340 | route-map test permit 10 |
| 341 | set extcommunity rt ${ASN}:1 |
| 342 | set extcommunity soo ${ASN}:2 |
| 343 | set community ${ASN}:1 |
| 344 | ! |
| 345 | line vty |
| 346 | exec-timeout 0 0 |
| 347 | ! |
| 348 | end |
| 349 | EOF |
| 350 | } |
| 351 | |
| 352 | bird_config () { |
| 353 | local ID=$1 |
| 354 | local ASN=$2 |
| 355 | local ADDR=$3 |
| 356 | |
| 357 | cat <<- EOF |
| 358 | #log "/var/log/bird.log" all; |
| 359 | #debug protocols all; |
| 360 | |
| 361 | # Override router ID |
| 362 | router id ${ADDR}; |
| 363 | listen bgp address ${ADDR}; |
| 364 | |
| 365 | protocol kernel { device routes; import all; } |
| 366 | protocol device { import all; } |
| 367 | |
| 368 | function avoid_martians() |
| 369 | prefix set martians; |
| 370 | { |
| 371 | martians = [ |
| 372 | 224.0.0.0/4+, 240.0.0.0/4+ |
| 373 | ]; |
| 374 | |
| 375 | # Avoid RFC1918 and similar networks |
| 376 | if net ~ martians then return false; |
| 377 | return true; |
| 378 | } |
| 379 | |
| 380 | filter import_filter |
| 381 | { |
| 382 | if ! (avoid_martians()) then reject; |
| 383 | accept; |
| 384 | } |
| 385 | |
| 386 | filter set_comm |
| 387 | { |
| 388 | bgp_community.add ((${ASN}, 1)); |
| 389 | accept; |
| 390 | } |
| 391 | |
| 392 | template bgp peer_conf { |
| 393 | local as ${ASN}; |
| 394 | source address ${ADDR}; |
| 395 | import filter import_filter; |
| 396 | export filter set_comm; |
| 397 | multihop; |
| 398 | } |
| 399 | EOF |
| 400 | |
| 401 | local P; |
| 402 | |
| 403 | for P in ${PEEREXP[@]}; do |
| 404 | [ "$P" -eq 0 ] && continue; |
| 405 | |
| 406 | local PID=`peeridoff $ID $P` |
| 407 | local PADDR=`id2addr $PID` |
| 408 | local PAS=$((${ASBASE} + $PID)) |
| 409 | echo "protocol bgp from peer_conf {" |
| 410 | echo " neighbor ${PADDR} as ${PAS};" |
| 411 | echo "}" |
| 412 | done |
| 413 | |
| 414 | for P in ${!EXPEERS[@]}; do |
| 415 | echo "protocol bgp from peer_conf {" |
| 416 | echo " neighbor ${EXPEERS[$P]} as ${EXPEERASES[$P]};" |
| 417 | echo "}" |
| 418 | done |
| 419 | |
| 420 | |
| 421 | for N in $(seq 1 $ADV) ; do |
| 422 | echo " network `advipaddr $ID $N`/32" |
| 423 | done |
| 424 | } |
| 425 | |
| 426 | ####################################################################### |
| 427 | |
| 428 | for ID in $(seq 1 $NUM); do |
| 429 | BGP_INST=${BGP_INSTANCES[${ID} % ${#BGP_INSTANCES[@]}]} |
| 430 | BGPBIN=${BGP_BINS[$BGP_INST]} |
| 431 | CONF="${CONFBASE}"/${BGP_INST}_bgpd${ID}.conf |
| 432 | ASN=$(($ASBASE + ${ID})) |
| 433 | ADDR=`id2addr $ID` |
| 434 | |
| 435 | #if [ ! -e "$CONF" ] ; then |
| 436 | if [ ! -e "$CONF" -o "$CONFWRITE" = "Y" ] ; then |
| 437 | ${BGP_CONFIGGEN[$BGP_INST]} $ID $ASN $ADDR > "$CONF" |
| 438 | chown $USER:$GROUP "$CONF" |
Paul Jakma | 3a02d1f | 2007-11-01 14:29:11 +0000 | [diff] [blame] | 439 | fi |
| 440 | # You may want to automatically add configure a local address |
| 441 | # on a loop interface. |
| 442 | # |
Paul Jakma | f04b0e6 | 2008-07-21 18:43:04 +0000 | [diff] [blame] | 443 | # Solaris: ifconfig vni${H} plumb ${ADDR}/${ADDRPLEN} up |
Paul Jakma | 366bb4a | 2016-05-17 13:59:55 +0100 | [diff] [blame^] | 444 | # Linux: |
| 445 | #ip address add ${ADDR}/${ADDRPLEN} dev lo 2> /dev/null |
| 446 | |
| 447 | ip link add dummy${ID} type dummy 2> /dev/null |
| 448 | ip link set dev dummy${ID} up |
| 449 | ip address add ${ADDR}/${ADDRPLEN} dev dummy${ID} 2> /dev/null |
| 450 | |
| 451 | ${BGP_LAUNCH[$BGP_INST]} $ID $ASN $ADDR $BGPBIN $CONF & |
| 452 | |
| 453 | sleep 0.1 |
Paul Jakma | 3a02d1f | 2007-11-01 14:29:11 +0000 | [diff] [blame] | 454 | done |
Paul Jakma | 366bb4a | 2016-05-17 13:59:55 +0100 | [diff] [blame^] | 455 | |
| 456 | echo "multiple-bgpd.sh: waiting..." |
| 457 | |
| 458 | wait |