New upstream release and new maintainer
[quagga-debian.git] / tools / multiple-bgpd.sh
1 #!/bin/bash
2
3 # Public domain, not copyrighted..
4
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.
34 VTYBASE=2610
35 # Base ASN to allocate ASNs to instances.
36 ASBASE=64500
37 PREFIX=192.168.145.
38 #PREFIX=3ffe:123:456::
39 ADDRPLEN=32
40 CONFBASE=/tmp
41 PIDBASE=/var/run/quagga
42 USER=quagga
43 GROUP=quagga
44
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"
104         
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;
239                 
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"
439         fi
440         # You may want to automatically add configure a local address
441         # on a loop interface.
442         #
443         # Solaris: ifconfig vni${H} plumb ${ADDR}/${ADDRPLEN} up
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
454 done
455
456 echo "multiple-bgpd.sh: waiting..."
457
458 wait