blob: c5668e19b820e0b9d7b279291d4fd605f9c3ec57 [file] [log] [blame]
Paul Jakma3a02d1f2007-11-01 14:29:11 +00001#!/bin/bash
2
3# Public domain, not copyrighted..
4
Paul Jakma366bb4a2016-05-17 13:59:55 +01005set -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.
9NUM=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.
23PEERPROP=100
24
25# number of routes each BGP instance should advertise
26ADV=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.
30ADVPREF=10
31
32# Base VTY port to allocate Quagga telnet vtys from. VTYBASE+ID will be
33# the port.
Paul Jakma3a02d1f2007-11-01 14:29:11 +000034VTYBASE=2610
Paul Jakma366bb4a2016-05-17 13:59:55 +010035# Base ASN to allocate ASNs to instances.
36ASBASE=64500
Paul Jakmaf04b0e62008-07-21 18:43:04 +000037PREFIX=192.168.145.
38#PREFIX=3ffe:123:456::
39ADDRPLEN=32
Paul Jakma0e7c1242008-06-01 14:26:48 +000040CONFBASE=/tmp
41PIDBASE=/var/run/quagga
Paul Jakma366bb4a2016-05-17 13:59:55 +010042USER=quagga
43GROUP=quagga
Paul Jakma3a02d1f2007-11-01 14:29:11 +000044
Paul Jakma366bb4a2016-05-17 13:59:55 +010045# MRAI to specify, where an implementation supports it.
46MRAI=1
47# Connect retry timer
48CONNECTRETRY=1
49
50# The binary locations for BGP instances.
51declare -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.
60declare -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.
69declare -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.
81declare -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
95EXPEERS=()
96EXPEERASES=()
97
98############################################################################
99# Can override any of the above from a supplied file with declarations
100CONFWRITE=Y
101if [ $# -gt 0 ] ; then
102 echo "multiple-bgpd.sh: sourcing config from $1"
103 [ -f "$1" ] && . "$1"
Paul Jakma3a02d1f2007-11-01 14:29:11 +0000104
Paul Jakma366bb4a2016-05-17 13:59:55 +0100105 # keep config, if exists
106 [ $# -gt 1 ] && [ "$2" = "k" ] && CONFWRITE=N
107fi
108
109############################################################################
110# Internal variables.
111
112# Number of peers for each instance to peer with
113PEERNUM=$(( ($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
118PEERRANGE=$(( $PEERNUM/2 ))
119[ "$PEERPROP" -gt 0 -a "$NUM" -ge 3 -a "$PEERRANGE" -le 0 ] && PEERRANGE=1
120
121# and a convenience expansion
122PEEREXP=""
123if [ "$PEERRANGE" -gt 0 ]; then
124 PEEREXP=($(seq -${PEERRANGE} ${PEERRANGE}))
125 # dont need 0
126 unset PEEREXP[PEERRANGE]
127fi
128
129#echo ${PEEREXP[@]}
130
131############################################################################
132## helpers
133
134# translate instance ID to its address.
135id2addr () {
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.
144peeridoff () {
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.
151advipaddr () {
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
164quagga_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
177exabgp_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
193bird_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
210exabgp_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 Jakma3a02d1f2007-11-01 14:29:11 +0000239
Paul Jakma366bb4a2016-05-17 13:59:55 +0100240 #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
266quagga_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
352bird_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
428for 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 Jakma3a02d1f2007-11-01 14:29:11 +0000439 fi
440 # You may want to automatically add configure a local address
441 # on a loop interface.
442 #
Paul Jakmaf04b0e62008-07-21 18:43:04 +0000443 # Solaris: ifconfig vni${H} plumb ${ADDR}/${ADDRPLEN} up
Paul Jakma366bb4a2016-05-17 13:59:55 +0100444 # 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 Jakma3a02d1f2007-11-01 14:29:11 +0000454done
Paul Jakma366bb4a2016-05-17 13:59:55 +0100455
456echo "multiple-bgpd.sh: waiting..."
457
458wait