Vincent Bernat: High availability with ExaBGP
When it comes to provide redundant services, several options are available:
- The service can be hosted behind a set of load-balancers. They will detect any faulty node. However, you need to ensure that this new layer is also fault-tolerant.
- The nodes providing the service can rely on IP failover to share a set of IP using protocols like VRRP1 or CARP. The IP address of a faulty node will be assigned to another node. This requires all nodes to be part of the same IP subnet.
- The clients of the service can ask a third-party for available nodes. Usually, this is achieved through round-robin DNS where only working nodes are advertised in a DNS record. The failover can be quite long because of caches.
- The nodes advertise their availability with BGP to announce the set of service IP addresses they are able to serve. Each address is weighted such that IP addresses are balanced among the available nodes.
Environment
We will be working in the following (IPv6-only) environment:
BGP configuration
BGP is enabled on ER2 and ER3 to exchange routes with peers and
transits (only R1 for this lab). BIRD is used as a BGP
daemon. Its configuration is pretty basic. Here is a fragment:
router id 1.1.1.2;
protocol static NETS #
import all;
export none;
route 2001:db8::/40 reject;
protocol bgp R1 #
import all;
export where proto = "NETS";
local as 64496;
neighbor 2001:db8:1000::1 as 64511;
protocol bgp ER3 #
import all;
export all;
next hop self;
local as 64496;
neighbor 2001:db8:1::3 as 64496;
First, in , we declare the routes that we want to export to our
peers. We don t try to summarize them from the IGP. We just
unconditonnaly export the networks that we own. Then, in , R1 is
defined as a neighbor and we export the static route we declared
previously. We import any route that R1 is willing to send us. In ,
we share everything we know with our pal, ER3, using internal
BGP.
OSPF configuration
OSPF will distribute routes inside the AS. It is enabled on ER2,
ER3, DR6, DR7 and DR8. For example, here is the relevant part
of the configuration of DR6:
router id 1.1.1.6;
protocol kernel
persist;
import none;
export all;
protocol ospf INTERNAL
import all;
export none;
area 0.0.0.0
networks
2001:db8:1::/64;
2001:db8:6::/64;
;
interface "eth0";
interface "eth1" stub yes; ;
;
ER2 and ER3 inject a default route into OSPF:
protocol static DEFAULT
import all;
export none;
route ::/0 via 2001:db8:1000::1;
filter default_route
if proto = "DEFAULT" then accept;
reject;
protocol ospf INTERNAL
import all;
export filter default_route;
area 0.0.0.0
networks
2001:db8:1::/64;
;
interface "eth1";
;
Web nodes
The web nodes are pretty basic. They have a default static route to
the nearest router and that s all. The interesting thing here is that
they are each on a separate IP subnet: we cannot share an IP using
VRRP2.
Why are those web servers on different subnets? Maybe they are not in
the same datacenter or maybe your network architecture is using a
routed access layer.
Let s see how to use BGP to enable redundancy of those web nodes.
Redundancy with ExaBGP
ExaBGP is a convenient tool to plug scripts into BGP. They can
then receive and advertise routes. ExaBGP does the hard work of
speaking BGP with your routers. The scripts just have to read routes
from standard input or advertise them on standard output.
The big picture
Here is the big picture:
Let s explain it step by step:
-
Three IP addresses will be allocated for our web service:
2001:db8:30::1, 2001:db8:30::2 and 2001:db8:30::3. Those are
distinct from the real IP addresses of W1, W2 and W3.
-
Each web node will advertise all of them to the route servers
we added in the network. I will talk more about those route
servers later.
-
Each route comes with a metric to help the route server
choose where it should be routed. We choose the metrics such that
each IP address will be routed to a distinct web node (unless
there is a problem).
-
The route servers (which are not routers) will then advertise the
best routes they learned to all the routers in our network. This
is still done using BGP.
-
Now, for a given IP address, each router knows to which web node
the traffic should be directed.
Here are the respective metrics announced routes for W1, W2 and
W3 when everything works as expected:
router id 1.1.1.2; protocol static NETS # import all; export none; route 2001:db8::/40 reject; protocol bgp R1 # import all; export where proto = "NETS"; local as 64496; neighbor 2001:db8:1000::1 as 64511; protocol bgp ER3 # import all; export all; next hop self; local as 64496; neighbor 2001:db8:1::3 as 64496;
OSPF configuration
OSPF will distribute routes inside the AS. It is enabled on ER2,
ER3, DR6, DR7 and DR8. For example, here is the relevant part
of the configuration of DR6:
router id 1.1.1.6;
protocol kernel
persist;
import none;
export all;
protocol ospf INTERNAL
import all;
export none;
area 0.0.0.0
networks
2001:db8:1::/64;
2001:db8:6::/64;
;
interface "eth0";
interface "eth1" stub yes; ;
;
ER2 and ER3 inject a default route into OSPF:
protocol static DEFAULT
import all;
export none;
route ::/0 via 2001:db8:1000::1;
filter default_route
if proto = "DEFAULT" then accept;
reject;
protocol ospf INTERNAL
import all;
export filter default_route;
area 0.0.0.0
networks
2001:db8:1::/64;
;
interface "eth1";
;
Web nodes
The web nodes are pretty basic. They have a default static route to
the nearest router and that s all. The interesting thing here is that
they are each on a separate IP subnet: we cannot share an IP using
VRRP2.
Why are those web servers on different subnets? Maybe they are not in
the same datacenter or maybe your network architecture is using a
routed access layer.
Let s see how to use BGP to enable redundancy of those web nodes.
Redundancy with ExaBGP
ExaBGP is a convenient tool to plug scripts into BGP. They can
then receive and advertise routes. ExaBGP does the hard work of
speaking BGP with your routers. The scripts just have to read routes
from standard input or advertise them on standard output.
The big picture
Here is the big picture:
Let s explain it step by step:
-
Three IP addresses will be allocated for our web service:
2001:db8:30::1, 2001:db8:30::2 and 2001:db8:30::3. Those are
distinct from the real IP addresses of W1, W2 and W3.
-
Each web node will advertise all of them to the route servers
we added in the network. I will talk more about those route
servers later.
-
Each route comes with a metric to help the route server
choose where it should be routed. We choose the metrics such that
each IP address will be routed to a distinct web node (unless
there is a problem).
-
The route servers (which are not routers) will then advertise the
best routes they learned to all the routers in our network. This
is still done using BGP.
-
Now, for a given IP address, each router knows to which web node
the traffic should be directed.
Here are the respective metrics announced routes for W1, W2 and
W3 when everything works as expected:
router id 1.1.1.6; protocol kernel persist; import none; export all; protocol ospf INTERNAL import all; export none; area 0.0.0.0 networks 2001:db8:1::/64; 2001:db8:6::/64; ; interface "eth0"; interface "eth1" stub yes; ; ;
protocol static DEFAULT import all; export none; route ::/0 via 2001:db8:1000::1; filter default_route if proto = "DEFAULT" then accept; reject; protocol ospf INTERNAL import all; export filter default_route; area 0.0.0.0 networks 2001:db8:1::/64; ; interface "eth1"; ;
Redundancy with ExaBGP
ExaBGP is a convenient tool to plug scripts into BGP. They can
then receive and advertise routes. ExaBGP does the hard work of
speaking BGP with your routers. The scripts just have to read routes
from standard input or advertise them on standard output.
The big picture
Here is the big picture:
Let s explain it step by step:
-
Three IP addresses will be allocated for our web service:
2001:db8:30::1, 2001:db8:30::2 and 2001:db8:30::3. Those are
distinct from the real IP addresses of W1, W2 and W3.
-
Each web node will advertise all of them to the route servers
we added in the network. I will talk more about those route
servers later.
-
Each route comes with a metric to help the route server
choose where it should be routed. We choose the metrics such that
each IP address will be routed to a distinct web node (unless
there is a problem).
-
The route servers (which are not routers) will then advertise the
best routes they learned to all the routers in our network. This
is still done using BGP.
-
Now, for a given IP address, each router knows to which web node
the traffic should be directed.
Here are the respective metrics announced routes for W1, W2 and
W3 when everything works as expected:
Let s explain it step by step:
-
Three IP addresses will be allocated for our web service:
2001:db8:30::1,2001:db8:30::2and2001:db8:30::3. Those are distinct from the real IP addresses of W1, W2 and W3. - Each web node will advertise all of them to the route servers we added in the network. I will talk more about those route servers later.
- Each route comes with a metric to help the route server choose where it should be routed. We choose the metrics such that each IP address will be routed to a distinct web node (unless there is a problem).
- The route servers (which are not routers) will then advertise the best routes they learned to all the routers in our network. This is still done using BGP.
- Now, for a given IP address, each router knows to which web node the traffic should be directed.
| Route | W1 | W2 | W3 | Best | Backup |
|---|---|---|---|---|---|
| 2001:db8:30::1 | 102 | 101 | 100 | W3 | W2 |
| 2001:db8:30::2 | 101 | 100 | 102 | W2 | W1 |
| 2001:db8:30::3 | 100 | 102 | 101 | W1 | W3 |
ExaBGP configuration
The configuration of ExaBGP is quite simple:
group rs
neighbor 2001:db8:1::4
router-id 1.1.1.11;
local-address 2001:db8:6::11;
local-as 65001;
peer-as 65002;
neighbor 2001:db8:8::5
router-id 1.1.1.11;
local-address 2001:db8:6::11;
local-as 65001;
peer-as 65002;
process watch-nginx
run /usr/bin/python /lab/healthcheck.py -s --config /lab/healthcheck-nginx.conf --start-ip 0;
A script is entailed to check if the service (an nginx web server)
is up and running and advertise the appropriate IP addresses to the
two declared route servers. If we run the script manually, we can see
the advertised routes:
$ python /lab/healthcheck.py --config /lab/healthcheck-nginx.conf --start-ip 0
INFO[healthcheck] send announces for UP state to ExaBGP
announce route 2001:db8:30::3/128 next-hop self med 100
announce route 2001:db8:30::2/128 next-hop self med 101
announce route 2001:db8:30::1/128 next-hop self med 102
[...]
WARNING[healthcheck] Check command was unsuccessful: 7
INFO[healthcheck] Output of check command: curl: (7) Failed connect to ip6-localhost:80; Connection refused
WARNING[healthcheck] Check command was unsuccessful: 7
INFO[healthcheck] Output of check command: curl: (7) Failed connect to ip6-localhost:80; Connection refused
WARNING[healthcheck] Check command was unsuccessful: 7
INFO[healthcheck] Output of check command: curl: (7) Failed connect to ip6-localhost:80; Connection refused
INFO[healthcheck] send announces for DOWN state to ExaBGP
announce route 2001:db8:30::3/128 next-hop self med 1000
announce route 2001:db8:30::2/128 next-hop self med 1001
announce route 2001:db8:30::1/128 next-hop self med 1002
When the service becomes unresponsive, the healthcheck script detect
the situation and retry several times before acknowledging that the
service is dead. Then, the IP addresses are advertised with higher
metrics and the service will be routed to another node (the one
advertising 2001:db8:30::3/128 with metric 101).
This healthcheck script is now part of ExaBGP.
Route servers
We could have connected our ExaBGP servers directly to each
router. However, if you have 20 routers and 10 web servers, you now
have to manage a mesh of 200 sessions. The route servers are here
for three purposes:
-
Reduce the number of BGP sessions (from 200 to 60) between
equipments (less configuration, less errors).
-
Avoid modifying the configuration on routers each time a new
service is added.
-
Separate the routing decision (the route servers) from the
routing process (the routers).
You may also ask yourself: why not use OSPF? . Good question!
-
OSPF could be enabled on each web node and the IP addresses
advertised using this protocol. However, OSPF has several
drawbacks: it does not scale, there are restrictions on the allowed
topologies, it is difficult to filter routes inside OSPF and a
misconfiguration will likely impact the whole network. Therefore,
it is considered a good practice to limit OSPF to network equipments.
-
The routes learned by the route servers could be injected into
OSPF. On paper, OSPF has a next-hop field to provide an explicit
next-hop. This would be handy as we wouldn t have to configure
adjacencies with each router. However, I have absolutely no idea
how to inject BGP next-hop into OSPF next-hop. What happens is that
BGP next-hop is resolved locally using OSPF routes. For example, if
we inject BGP routes into OSPF from RS4, RS4 will know the
appropriate next-hop but other routers will route the traffic to
RS4.
Let s look at how to configure our route servers. RS4 will use
BIRD while RS5 will use Quagga. Using two different
implementations will help with resiliency by avoiding a bug to hit the
two route servers at the same time.
Bird configuration
There are two sides for BGP configuration: the BGP sessions with the
ExaBGP nodes and the ones with the routers. Here is the
configuration for the later:
template bgp INFRABGP
export all;
import none;
local as 65002;
rs client;
protocol bgp ER2 from INFRABGP
neighbor 2001:db8:1::2 as 65003;
protocol bgp ER3 from INFRABGP
neighbor 2001:db8:1::3 as 65003;
protocol bgp DR6 from INFRABGP
neighbor 2001:db8:1::6 as 65003;
protocol bgp DR7 from INFRABGP
neighbor 2001:db8:1::7 as 65003;
protocol bgp DR8 from INFRABGP
neighbor 2001:db8:1::8 as 65003;
The AS number used by our route server is 65002 while the AS number
used for routers is 65003 (the AS numbers for web nodes will be
65001). They are reserved for private use by RFC 6996. All routes
known by the route server are exported to the routers but no routes
are accepted from them.
Let s have a look at the other side:
# Only import loopback IPs
filter only_loopbacks #
if net ~ [ 2001:db8:30::/64 128,128 ] then accept;
reject;
# General template for an EXABGP node
template bgp EXABGP
local as 65002;
import filter only_loopbacks; #
export none;
route limit 10; #
rs client;
hold time 6; #
multihop 10;
igp table internal;
protocol bgp W1 from EXABGP
neighbor 2001:db8:6::11 as 65001;
protocol bgp W2 from EXABGP
neighbor 2001:db8:7::12 as 65001;
protocol bgp W3 from EXABGP
neighbor 2001:db8:8::13 as 65001;
To ensure separation of concerns, we are being a bit more picky. With
and , we only accept loopback addresses and only if they are
contained in the subnet that we reserved for this use. No server
should be able to inject arbitrary addresses into our network. With ,
we also limit the number of routes that a server can advertise.
With , we reduce the hold time from 240 to 6. It means that after 6
seconds, the peer is considered dead. This is quite important to be
able to recover quickly from a dead machine. The minimal value
is 3. We could have use a similar setting with the session with the routers.
Quagga configuration
Quagga s configuration is a bit more verbose but should be strictly equivalent:
router bgp 65002 view EXABGP
bgp router-id 1.1.1.5
bgp log-neighbor-changes
no bgp default ipv4-unicast
neighbor R peer-group
neighbor R remote-as 65003
neighbor R ebgp-multihop 10
neighbor EXABGP peer-group
neighbor EXABGP remote-as 65001
neighbor EXABGP ebgp-multihop 10
neighbor EXABGP timers 2 6
!
address-family ipv6
neighbor R activate
neighbor R soft-reconfiguration inbound
neighbor R route-server-client
neighbor R route-map R-IMPORT import
neighbor R route-map R-EXPORT export
neighbor 2001:db8:1::2 peer-group R
neighbor 2001:db8:1::3 peer-group R
neighbor 2001:db8:1::6 peer-group R
neighbor 2001:db8:1::7 peer-group R
neighbor 2001:db8:1::8 peer-group R
neighbor EXABGP activate
neighbor EXABGP soft-reconfiguration inbound
neighbor EXABGP maximum-prefix 10
neighbor EXABGP route-server-client
neighbor EXABGP route-map RSCLIENT-IMPORT import
neighbor EXABGP route-map RSCLIENT-EXPORT export
neighbor 2001:db8:6::11 peer-group EXABGP
neighbor 2001:db8:7::12 peer-group EXABGP
neighbor 2001:db8:8::13 peer-group EXABGP
exit-address-family
!
ipv6 prefix-list LOOPBACKS seq 5 permit 2001:db8:30::/64 ge 128 le 128
ipv6 prefix-list LOOPBACKS seq 10 deny any
!
route-map RSCLIENT-IMPORT deny 10
!
route-map RSCLIENT-EXPORT permit 10
match ipv6 address prefix-list LOOPBACKS
!
route-map R-IMPORT permit 10
!
route-map R-EXPORT deny 10
!
The view is here to not install routes in the kernel3.
Routers
Configuring BIRD to receive routes from route servers is straightforward:
# BGP with route servers
protocol bgp RS4
import all;
export none;
local as 65003;
neighbor 2001:db8:1::4 as 65002;
gateway recursive;
protocol bgp RS5
import all;
export none;
local as 65003;
neighbor 2001:db8:8::5 as 65002;
multihop 4;
gateway recursive;
It is important to set gateway recursive because most of the time,
the next-hop is not reachable directly. In this case, by default,
BIRD will use the IP address of the advertising router (the route servers).
Testing
Let s check that everything works as expected. Here is the view from RS5:
# show ipv6 bgp
BGP table version is 0, local router ID is 1.1.1.5
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,
r RIB-failure, S Stale, R Removed
Origin codes: i - IGP, e - EGP, ? - incomplete
Network Next Hop Metric LocPrf Weight Path
* 2001:db8:30::1/128
2001:db8:6::11 102 0 65001 i
*> 2001:db8:8::13 100 0 65001 i
* 2001:db8:7::12 101 0 65001 i
* 2001:db8:30::2/128
2001:db8:6::11 101 0 65001 i
* 2001:db8:8::13 102 0 65001 i
*> 2001:db8:7::12 100 0 65001 i
*> 2001:db8:30::3/128
2001:db8:6::11 100 0 65001 i
* 2001:db8:8::13 101 0 65001 i
* 2001:db8:7::12 102 0 65001 i
Total number of prefixes 3
For example, traffic to 2001:db8:30::2 should be routed through
2001:db8:7::12 (which is W2). Other IP are affected to W1 and
W3.
RS4 should see the same thing4:
$ birdc6 show route
BIRD 1.3.11 ready.
2001:db8:30::1/128 [W3 22:07 from 2001:db8:8::13] * (100/20) [AS65001i]
[W1 23:34 from 2001:db8:6::11] (100/20) [AS65001i]
[W2 22:07 from 2001:db8:7::12] (100/20) [AS65001i]
2001:db8:30::2/128 [W2 22:07 from 2001:db8:7::12] * (100/20) [AS65001i]
[W1 23:34 from 2001:db8:6::11] (100/20) [AS65001i]
[W3 22:07 from 2001:db8:8::13] (100/20) [AS65001i]
2001:db8:30::3/128 [W1 23:34 from 2001:db8:6::11] * (100/20) [AS65001i]
[W3 22:07 from 2001:db8:8::13] (100/20) [AS65001i]
[W2 22:07 from 2001:db8:7::12] (100/20) [AS65001i]
Let s have a look at DR6:
$ birdc6 show route
2001:db8:30::1/128 via fe80::5054:56ff:fe6e:98a6 on eth0 * (100/20) [AS65001i]
via fe80::5054:56ff:fe6e:98a6 on eth0 (100/20) [AS65001i]
2001:db8:30::2/128 via fe80::5054:60ff:fe02:3681 on eth0 * (100/20) [AS65001i]
via fe80::5054:60ff:fe02:3681 on eth0 (100/20) [AS65001i]
2001:db8:30::3/128 via 2001:db8:6::11 on eth1 * (100/10) [AS65001i]
via 2001:db8:6::11 on eth1 (100/10) [AS65001i]
So, 2001:db8:30::3 is owned by W1 which is behind DR6 while the
two others are in another part of the network and will be reached
through the appropriate link-local addresses learned by OSPF.
Let s kill W1 by stopping nginx. A few seconds later, DR6 learns
the new routes:
$ birdc6 show route
2001:db8:30::1/128 via fe80::5054:56ff:fe6e:98a6 on eth0 * (100/20) [AS65001i]
via fe80::5054:56ff:fe6e:98a6 on eth0 (100/20) [AS65001i]
2001:db8:30::2/128 via fe80::5054:60ff:fe02:3681 on eth0 * (100/20) [AS65001i]
via fe80::5054:60ff:fe02:3681 on eth0 (100/20) [AS65001i]
2001:db8:30::3/128 via fe80::5054:56ff:fe6e:98a6 on eth0 * (100/20) [AS65001i]
via fe80::5054:56ff:fe6e:98a6 on eth0 (100/20) [AS65001i]
Demo
For a demo, have a look at the following video (it is also available
as an Ogg Theora video).
-
The primary target of VRRP is to make a default gateway for
an IP network highly available by exposing a virtual router
which is an abstract representation of multiple routers. The
virtual IP address is held by the master router and any
backup router can be promoted to master in case of a
failure. In practice, VRRP can be used to achieve high
availability of services too.
-
However, you could deploy an L2 overlay network using, for
example, VXLAN and use VRRP on it.
-
I have been told on the Quagga mailing-list that such a
setup is quite uncommon and that it would be better to not
use views but use
--no_kernel flag for bgpd instead. You
may want to look at the whole thread for more details.
-
The output of
birdc6 shows both the next-hop as
advertised in BGP and the resolved next-hop if the route
have to be exported into another protocol. I have
simplified the output to avoid confusion.
group rs neighbor 2001:db8:1::4 router-id 1.1.1.11; local-address 2001:db8:6::11; local-as 65001; peer-as 65002; neighbor 2001:db8:8::5 router-id 1.1.1.11; local-address 2001:db8:6::11; local-as 65001; peer-as 65002; process watch-nginx run /usr/bin/python /lab/healthcheck.py -s --config /lab/healthcheck-nginx.conf --start-ip 0;
$ python /lab/healthcheck.py --config /lab/healthcheck-nginx.conf --start-ip 0 INFO[healthcheck] send announces for UP state to ExaBGP announce route 2001:db8:30::3/128 next-hop self med 100 announce route 2001:db8:30::2/128 next-hop self med 101 announce route 2001:db8:30::1/128 next-hop self med 102 [...] WARNING[healthcheck] Check command was unsuccessful: 7 INFO[healthcheck] Output of check command: curl: (7) Failed connect to ip6-localhost:80; Connection refused WARNING[healthcheck] Check command was unsuccessful: 7 INFO[healthcheck] Output of check command: curl: (7) Failed connect to ip6-localhost:80; Connection refused WARNING[healthcheck] Check command was unsuccessful: 7 INFO[healthcheck] Output of check command: curl: (7) Failed connect to ip6-localhost:80; Connection refused INFO[healthcheck] send announces for DOWN state to ExaBGP announce route 2001:db8:30::3/128 next-hop self med 1000 announce route 2001:db8:30::2/128 next-hop self med 1001 announce route 2001:db8:30::1/128 next-hop self med 1002
- Reduce the number of BGP sessions (from 200 to 60) between equipments (less configuration, less errors).
- Avoid modifying the configuration on routers each time a new service is added.
- Separate the routing decision (the route servers) from the routing process (the routers).
- OSPF could be enabled on each web node and the IP addresses advertised using this protocol. However, OSPF has several drawbacks: it does not scale, there are restrictions on the allowed topologies, it is difficult to filter routes inside OSPF and a misconfiguration will likely impact the whole network. Therefore, it is considered a good practice to limit OSPF to network equipments.
- The routes learned by the route servers could be injected into OSPF. On paper, OSPF has a next-hop field to provide an explicit next-hop. This would be handy as we wouldn t have to configure adjacencies with each router. However, I have absolutely no idea how to inject BGP next-hop into OSPF next-hop. What happens is that BGP next-hop is resolved locally using OSPF routes. For example, if we inject BGP routes into OSPF from RS4, RS4 will know the appropriate next-hop but other routers will route the traffic to RS4.
Bird configuration
There are two sides for BGP configuration: the BGP sessions with the
ExaBGP nodes and the ones with the routers. Here is the
configuration for the later:
template bgp INFRABGP
export all;
import none;
local as 65002;
rs client;
protocol bgp ER2 from INFRABGP
neighbor 2001:db8:1::2 as 65003;
protocol bgp ER3 from INFRABGP
neighbor 2001:db8:1::3 as 65003;
protocol bgp DR6 from INFRABGP
neighbor 2001:db8:1::6 as 65003;
protocol bgp DR7 from INFRABGP
neighbor 2001:db8:1::7 as 65003;
protocol bgp DR8 from INFRABGP
neighbor 2001:db8:1::8 as 65003;
The AS number used by our route server is 65002 while the AS number
used for routers is 65003 (the AS numbers for web nodes will be
65001). They are reserved for private use by RFC 6996. All routes
known by the route server are exported to the routers but no routes
are accepted from them.
Let s have a look at the other side:
# Only import loopback IPs
filter only_loopbacks #
if net ~ [ 2001:db8:30::/64 128,128 ] then accept;
reject;
# General template for an EXABGP node
template bgp EXABGP
local as 65002;
import filter only_loopbacks; #
export none;
route limit 10; #
rs client;
hold time 6; #
multihop 10;
igp table internal;
protocol bgp W1 from EXABGP
neighbor 2001:db8:6::11 as 65001;
protocol bgp W2 from EXABGP
neighbor 2001:db8:7::12 as 65001;
protocol bgp W3 from EXABGP
neighbor 2001:db8:8::13 as 65001;
To ensure separation of concerns, we are being a bit more picky. With
and , we only accept loopback addresses and only if they are
contained in the subnet that we reserved for this use. No server
should be able to inject arbitrary addresses into our network. With ,
we also limit the number of routes that a server can advertise.
With , we reduce the hold time from 240 to 6. It means that after 6
seconds, the peer is considered dead. This is quite important to be
able to recover quickly from a dead machine. The minimal value
is 3. We could have use a similar setting with the session with the routers.
Quagga configuration
Quagga s configuration is a bit more verbose but should be strictly equivalent:
router bgp 65002 view EXABGP
bgp router-id 1.1.1.5
bgp log-neighbor-changes
no bgp default ipv4-unicast
neighbor R peer-group
neighbor R remote-as 65003
neighbor R ebgp-multihop 10
neighbor EXABGP peer-group
neighbor EXABGP remote-as 65001
neighbor EXABGP ebgp-multihop 10
neighbor EXABGP timers 2 6
!
address-family ipv6
neighbor R activate
neighbor R soft-reconfiguration inbound
neighbor R route-server-client
neighbor R route-map R-IMPORT import
neighbor R route-map R-EXPORT export
neighbor 2001:db8:1::2 peer-group R
neighbor 2001:db8:1::3 peer-group R
neighbor 2001:db8:1::6 peer-group R
neighbor 2001:db8:1::7 peer-group R
neighbor 2001:db8:1::8 peer-group R
neighbor EXABGP activate
neighbor EXABGP soft-reconfiguration inbound
neighbor EXABGP maximum-prefix 10
neighbor EXABGP route-server-client
neighbor EXABGP route-map RSCLIENT-IMPORT import
neighbor EXABGP route-map RSCLIENT-EXPORT export
neighbor 2001:db8:6::11 peer-group EXABGP
neighbor 2001:db8:7::12 peer-group EXABGP
neighbor 2001:db8:8::13 peer-group EXABGP
exit-address-family
!
ipv6 prefix-list LOOPBACKS seq 5 permit 2001:db8:30::/64 ge 128 le 128
ipv6 prefix-list LOOPBACKS seq 10 deny any
!
route-map RSCLIENT-IMPORT deny 10
!
route-map RSCLIENT-EXPORT permit 10
match ipv6 address prefix-list LOOPBACKS
!
route-map R-IMPORT permit 10
!
route-map R-EXPORT deny 10
!
The view is here to not install routes in the kernel3.
Routers
Configuring BIRD to receive routes from route servers is straightforward:
# BGP with route servers
protocol bgp RS4
import all;
export none;
local as 65003;
neighbor 2001:db8:1::4 as 65002;
gateway recursive;
protocol bgp RS5
import all;
export none;
local as 65003;
neighbor 2001:db8:8::5 as 65002;
multihop 4;
gateway recursive;
It is important to set gateway recursive because most of the time,
the next-hop is not reachable directly. In this case, by default,
BIRD will use the IP address of the advertising router (the route servers).
Testing
Let s check that everything works as expected. Here is the view from RS5:
# show ipv6 bgp
BGP table version is 0, local router ID is 1.1.1.5
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,
r RIB-failure, S Stale, R Removed
Origin codes: i - IGP, e - EGP, ? - incomplete
Network Next Hop Metric LocPrf Weight Path
* 2001:db8:30::1/128
2001:db8:6::11 102 0 65001 i
*> 2001:db8:8::13 100 0 65001 i
* 2001:db8:7::12 101 0 65001 i
* 2001:db8:30::2/128
2001:db8:6::11 101 0 65001 i
* 2001:db8:8::13 102 0 65001 i
*> 2001:db8:7::12 100 0 65001 i
*> 2001:db8:30::3/128
2001:db8:6::11 100 0 65001 i
* 2001:db8:8::13 101 0 65001 i
* 2001:db8:7::12 102 0 65001 i
Total number of prefixes 3
For example, traffic to 2001:db8:30::2 should be routed through
2001:db8:7::12 (which is W2). Other IP are affected to W1 and
W3.
RS4 should see the same thing4:
$ birdc6 show route
BIRD 1.3.11 ready.
2001:db8:30::1/128 [W3 22:07 from 2001:db8:8::13] * (100/20) [AS65001i]
[W1 23:34 from 2001:db8:6::11] (100/20) [AS65001i]
[W2 22:07 from 2001:db8:7::12] (100/20) [AS65001i]
2001:db8:30::2/128 [W2 22:07 from 2001:db8:7::12] * (100/20) [AS65001i]
[W1 23:34 from 2001:db8:6::11] (100/20) [AS65001i]
[W3 22:07 from 2001:db8:8::13] (100/20) [AS65001i]
2001:db8:30::3/128 [W1 23:34 from 2001:db8:6::11] * (100/20) [AS65001i]
[W3 22:07 from 2001:db8:8::13] (100/20) [AS65001i]
[W2 22:07 from 2001:db8:7::12] (100/20) [AS65001i]
Let s have a look at DR6:
$ birdc6 show route
2001:db8:30::1/128 via fe80::5054:56ff:fe6e:98a6 on eth0 * (100/20) [AS65001i]
via fe80::5054:56ff:fe6e:98a6 on eth0 (100/20) [AS65001i]
2001:db8:30::2/128 via fe80::5054:60ff:fe02:3681 on eth0 * (100/20) [AS65001i]
via fe80::5054:60ff:fe02:3681 on eth0 (100/20) [AS65001i]
2001:db8:30::3/128 via 2001:db8:6::11 on eth1 * (100/10) [AS65001i]
via 2001:db8:6::11 on eth1 (100/10) [AS65001i]
So, 2001:db8:30::3 is owned by W1 which is behind DR6 while the
two others are in another part of the network and will be reached
through the appropriate link-local addresses learned by OSPF.
Let s kill W1 by stopping nginx. A few seconds later, DR6 learns
the new routes:
$ birdc6 show route
2001:db8:30::1/128 via fe80::5054:56ff:fe6e:98a6 on eth0 * (100/20) [AS65001i]
via fe80::5054:56ff:fe6e:98a6 on eth0 (100/20) [AS65001i]
2001:db8:30::2/128 via fe80::5054:60ff:fe02:3681 on eth0 * (100/20) [AS65001i]
via fe80::5054:60ff:fe02:3681 on eth0 (100/20) [AS65001i]
2001:db8:30::3/128 via fe80::5054:56ff:fe6e:98a6 on eth0 * (100/20) [AS65001i]
via fe80::5054:56ff:fe6e:98a6 on eth0 (100/20) [AS65001i]
Demo
For a demo, have a look at the following video (it is also available
as an Ogg Theora video).
-
The primary target of VRRP is to make a default gateway for
an IP network highly available by exposing a virtual router
which is an abstract representation of multiple routers. The
virtual IP address is held by the master router and any
backup router can be promoted to master in case of a
failure. In practice, VRRP can be used to achieve high
availability of services too.
-
However, you could deploy an L2 overlay network using, for
example, VXLAN and use VRRP on it.
-
I have been told on the Quagga mailing-list that such a
setup is quite uncommon and that it would be better to not
use views but use
--no_kernel flag for bgpd instead. You
may want to look at the whole thread for more details.
-
The output of
birdc6 shows both the next-hop as
advertised in BGP and the resolved next-hop if the route
have to be exported into another protocol. I have
simplified the output to avoid confusion.
template bgp INFRABGP export all; import none; local as 65002; rs client; protocol bgp ER2 from INFRABGP neighbor 2001:db8:1::2 as 65003; protocol bgp ER3 from INFRABGP neighbor 2001:db8:1::3 as 65003; protocol bgp DR6 from INFRABGP neighbor 2001:db8:1::6 as 65003; protocol bgp DR7 from INFRABGP neighbor 2001:db8:1::7 as 65003; protocol bgp DR8 from INFRABGP neighbor 2001:db8:1::8 as 65003;
# Only import loopback IPs filter only_loopbacks # if net ~ [ 2001:db8:30::/64 128,128 ] then accept; reject; # General template for an EXABGP node template bgp EXABGP local as 65002; import filter only_loopbacks; # export none; route limit 10; # rs client; hold time 6; # multihop 10; igp table internal; protocol bgp W1 from EXABGP neighbor 2001:db8:6::11 as 65001; protocol bgp W2 from EXABGP neighbor 2001:db8:7::12 as 65001; protocol bgp W3 from EXABGP neighbor 2001:db8:8::13 as 65001;
router bgp 65002 view EXABGP bgp router-id 1.1.1.5 bgp log-neighbor-changes no bgp default ipv4-unicast neighbor R peer-group neighbor R remote-as 65003 neighbor R ebgp-multihop 10 neighbor EXABGP peer-group neighbor EXABGP remote-as 65001 neighbor EXABGP ebgp-multihop 10 neighbor EXABGP timers 2 6 ! address-family ipv6 neighbor R activate neighbor R soft-reconfiguration inbound neighbor R route-server-client neighbor R route-map R-IMPORT import neighbor R route-map R-EXPORT export neighbor 2001:db8:1::2 peer-group R neighbor 2001:db8:1::3 peer-group R neighbor 2001:db8:1::6 peer-group R neighbor 2001:db8:1::7 peer-group R neighbor 2001:db8:1::8 peer-group R neighbor EXABGP activate neighbor EXABGP soft-reconfiguration inbound neighbor EXABGP maximum-prefix 10 neighbor EXABGP route-server-client neighbor EXABGP route-map RSCLIENT-IMPORT import neighbor EXABGP route-map RSCLIENT-EXPORT export neighbor 2001:db8:6::11 peer-group EXABGP neighbor 2001:db8:7::12 peer-group EXABGP neighbor 2001:db8:8::13 peer-group EXABGP exit-address-family ! ipv6 prefix-list LOOPBACKS seq 5 permit 2001:db8:30::/64 ge 128 le 128 ipv6 prefix-list LOOPBACKS seq 10 deny any ! route-map RSCLIENT-IMPORT deny 10 ! route-map RSCLIENT-EXPORT permit 10 match ipv6 address prefix-list LOOPBACKS ! route-map R-IMPORT permit 10 ! route-map R-EXPORT deny 10 !
Routers
Configuring BIRD to receive routes from route servers is straightforward:
# BGP with route servers
protocol bgp RS4
import all;
export none;
local as 65003;
neighbor 2001:db8:1::4 as 65002;
gateway recursive;
protocol bgp RS5
import all;
export none;
local as 65003;
neighbor 2001:db8:8::5 as 65002;
multihop 4;
gateway recursive;
It is important to set gateway recursive because most of the time,
the next-hop is not reachable directly. In this case, by default,
BIRD will use the IP address of the advertising router (the route servers).
Testing
Let s check that everything works as expected. Here is the view from RS5:
# show ipv6 bgp
BGP table version is 0, local router ID is 1.1.1.5
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,
r RIB-failure, S Stale, R Removed
Origin codes: i - IGP, e - EGP, ? - incomplete
Network Next Hop Metric LocPrf Weight Path
* 2001:db8:30::1/128
2001:db8:6::11 102 0 65001 i
*> 2001:db8:8::13 100 0 65001 i
* 2001:db8:7::12 101 0 65001 i
* 2001:db8:30::2/128
2001:db8:6::11 101 0 65001 i
* 2001:db8:8::13 102 0 65001 i
*> 2001:db8:7::12 100 0 65001 i
*> 2001:db8:30::3/128
2001:db8:6::11 100 0 65001 i
* 2001:db8:8::13 101 0 65001 i
* 2001:db8:7::12 102 0 65001 i
Total number of prefixes 3
For example, traffic to 2001:db8:30::2 should be routed through
2001:db8:7::12 (which is W2). Other IP are affected to W1 and
W3.
RS4 should see the same thing4:
$ birdc6 show route
BIRD 1.3.11 ready.
2001:db8:30::1/128 [W3 22:07 from 2001:db8:8::13] * (100/20) [AS65001i]
[W1 23:34 from 2001:db8:6::11] (100/20) [AS65001i]
[W2 22:07 from 2001:db8:7::12] (100/20) [AS65001i]
2001:db8:30::2/128 [W2 22:07 from 2001:db8:7::12] * (100/20) [AS65001i]
[W1 23:34 from 2001:db8:6::11] (100/20) [AS65001i]
[W3 22:07 from 2001:db8:8::13] (100/20) [AS65001i]
2001:db8:30::3/128 [W1 23:34 from 2001:db8:6::11] * (100/20) [AS65001i]
[W3 22:07 from 2001:db8:8::13] (100/20) [AS65001i]
[W2 22:07 from 2001:db8:7::12] (100/20) [AS65001i]
Let s have a look at DR6:
$ birdc6 show route
2001:db8:30::1/128 via fe80::5054:56ff:fe6e:98a6 on eth0 * (100/20) [AS65001i]
via fe80::5054:56ff:fe6e:98a6 on eth0 (100/20) [AS65001i]
2001:db8:30::2/128 via fe80::5054:60ff:fe02:3681 on eth0 * (100/20) [AS65001i]
via fe80::5054:60ff:fe02:3681 on eth0 (100/20) [AS65001i]
2001:db8:30::3/128 via 2001:db8:6::11 on eth1 * (100/10) [AS65001i]
via 2001:db8:6::11 on eth1 (100/10) [AS65001i]
So, 2001:db8:30::3 is owned by W1 which is behind DR6 while the
two others are in another part of the network and will be reached
through the appropriate link-local addresses learned by OSPF.
Let s kill W1 by stopping nginx. A few seconds later, DR6 learns
the new routes:
$ birdc6 show route
2001:db8:30::1/128 via fe80::5054:56ff:fe6e:98a6 on eth0 * (100/20) [AS65001i]
via fe80::5054:56ff:fe6e:98a6 on eth0 (100/20) [AS65001i]
2001:db8:30::2/128 via fe80::5054:60ff:fe02:3681 on eth0 * (100/20) [AS65001i]
via fe80::5054:60ff:fe02:3681 on eth0 (100/20) [AS65001i]
2001:db8:30::3/128 via fe80::5054:56ff:fe6e:98a6 on eth0 * (100/20) [AS65001i]
via fe80::5054:56ff:fe6e:98a6 on eth0 (100/20) [AS65001i]
Demo
For a demo, have a look at the following video (it is also available
as an Ogg Theora video).
-
The primary target of VRRP is to make a default gateway for
an IP network highly available by exposing a virtual router
which is an abstract representation of multiple routers. The
virtual IP address is held by the master router and any
backup router can be promoted to master in case of a
failure. In practice, VRRP can be used to achieve high
availability of services too.
-
However, you could deploy an L2 overlay network using, for
example, VXLAN and use VRRP on it.
-
I have been told on the Quagga mailing-list that such a
setup is quite uncommon and that it would be better to not
use views but use
--no_kernel flag for bgpd instead. You
may want to look at the whole thread for more details.
-
The output of
birdc6 shows both the next-hop as
advertised in BGP and the resolved next-hop if the route
have to be exported into another protocol. I have
simplified the output to avoid confusion.
# BGP with route servers protocol bgp RS4 import all; export none; local as 65003; neighbor 2001:db8:1::4 as 65002; gateway recursive; protocol bgp RS5 import all; export none; local as 65003; neighbor 2001:db8:8::5 as 65002; multihop 4; gateway recursive;
# show ipv6 bgp BGP table version is 0, local router ID is 1.1.1.5 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, R Removed Origin codes: i - IGP, e - EGP, ? - incomplete Network Next Hop Metric LocPrf Weight Path * 2001:db8:30::1/128 2001:db8:6::11 102 0 65001 i *> 2001:db8:8::13 100 0 65001 i * 2001:db8:7::12 101 0 65001 i * 2001:db8:30::2/128 2001:db8:6::11 101 0 65001 i * 2001:db8:8::13 102 0 65001 i *> 2001:db8:7::12 100 0 65001 i *> 2001:db8:30::3/128 2001:db8:6::11 100 0 65001 i * 2001:db8:8::13 101 0 65001 i * 2001:db8:7::12 102 0 65001 i Total number of prefixes 3
2001:db8:30::2 should be routed through
2001:db8:7::12 (which is W2). Other IP are affected to W1 and
W3.
RS4 should see the same thing4:
$ birdc6 show route BIRD 1.3.11 ready. 2001:db8:30::1/128 [W3 22:07 from 2001:db8:8::13] * (100/20) [AS65001i] [W1 23:34 from 2001:db8:6::11] (100/20) [AS65001i] [W2 22:07 from 2001:db8:7::12] (100/20) [AS65001i] 2001:db8:30::2/128 [W2 22:07 from 2001:db8:7::12] * (100/20) [AS65001i] [W1 23:34 from 2001:db8:6::11] (100/20) [AS65001i] [W3 22:07 from 2001:db8:8::13] (100/20) [AS65001i] 2001:db8:30::3/128 [W1 23:34 from 2001:db8:6::11] * (100/20) [AS65001i] [W3 22:07 from 2001:db8:8::13] (100/20) [AS65001i] [W2 22:07 from 2001:db8:7::12] (100/20) [AS65001i]
$ birdc6 show route 2001:db8:30::1/128 via fe80::5054:56ff:fe6e:98a6 on eth0 * (100/20) [AS65001i] via fe80::5054:56ff:fe6e:98a6 on eth0 (100/20) [AS65001i] 2001:db8:30::2/128 via fe80::5054:60ff:fe02:3681 on eth0 * (100/20) [AS65001i] via fe80::5054:60ff:fe02:3681 on eth0 (100/20) [AS65001i] 2001:db8:30::3/128 via 2001:db8:6::11 on eth1 * (100/10) [AS65001i] via 2001:db8:6::11 on eth1 (100/10) [AS65001i]
2001:db8:30::3 is owned by W1 which is behind DR6 while the
two others are in another part of the network and will be reached
through the appropriate link-local addresses learned by OSPF.
Let s kill W1 by stopping nginx. A few seconds later, DR6 learns
the new routes:
$ birdc6 show route 2001:db8:30::1/128 via fe80::5054:56ff:fe6e:98a6 on eth0 * (100/20) [AS65001i] via fe80::5054:56ff:fe6e:98a6 on eth0 (100/20) [AS65001i] 2001:db8:30::2/128 via fe80::5054:60ff:fe02:3681 on eth0 * (100/20) [AS65001i] via fe80::5054:60ff:fe02:3681 on eth0 (100/20) [AS65001i] 2001:db8:30::3/128 via fe80::5054:56ff:fe6e:98a6 on eth0 * (100/20) [AS65001i] via fe80::5054:56ff:fe6e:98a6 on eth0 (100/20) [AS65001i]
Demo
For a demo, have a look at the following video (it is also available
as an Ogg Theora video).
-
The primary target of VRRP is to make a default gateway for
an IP network highly available by exposing a virtual router
which is an abstract representation of multiple routers. The
virtual IP address is held by the master router and any
backup router can be promoted to master in case of a
failure. In practice, VRRP can be used to achieve high
availability of services too.
-
However, you could deploy an L2 overlay network using, for
example, VXLAN and use VRRP on it.
-
I have been told on the Quagga mailing-list that such a
setup is quite uncommon and that it would be better to not
use views but use
--no_kernel flag for bgpd instead. You
may want to look at the whole thread for more details.
-
The output of
birdc6 shows both the next-hop as
advertised in BGP and the resolved next-hop if the route
have to be exported into another protocol. I have
simplified the output to avoid confusion.
- The primary target of VRRP is to make a default gateway for an IP network highly available by exposing a virtual router which is an abstract representation of multiple routers. The virtual IP address is held by the master router and any backup router can be promoted to master in case of a failure. In practice, VRRP can be used to achieve high availability of services too.
- However, you could deploy an L2 overlay network using, for example, VXLAN and use VRRP on it.
-
I have been told on the Quagga mailing-list that such a
setup is quite uncommon and that it would be better to not
use views but use
--no_kernelflag forbgpdinstead. You may want to look at the whole thread for more details. -
The output of
birdc6shows both the next-hop as advertised in BGP and the resolved next-hop if the route have to be exported into another protocol. I have simplified the output to avoid confusion.
Pretend I run a cluster, for hosting a site. Pretend that I have three-six web-nodes, and each one needs to know which database host to contact.
How do I control that?
Right now I have a /etc/settings.conf file, more or less, deployed by
The first alpha of
The blog engine 
Big bad baby boomers
It is obvious that older voters prefer the conservative "coalition" parties, 57% of over-55s in particular. It is a huge jump from the 44% of voters in the age group below them.
What this table doesn't tell us is that Australia's population is top-heavy with
Altimeter Testing at Airfest
Bdale and I, along with AJ Towns and Mike Beattie, spent last weekend
in Argonia, Kansas, flying rockets with our Kloudbusters friends at





I think of Joshua often, and wonder how life might be different were he with us
today. The long day of his delivery is one of the most poignant memories that I
will carry with me forever.
This year is the first year I've been back in Australia for Father's Day.
Father's Day in the US is in June, so it's never really been the constant
reminder that it will be back here (although I guess it'll vary year to year).
The US Labor Day long weekend tended to be the trigger holiday for me.
It's also the first year I've been separated, so this Father's Day was
particularly difficult for me, but I found I enjoyed the day with Zoe all the
more for it.
Now that I'm no longer in the US, I miss seeing Eric and Katie's daughter, as
she was my physical yardstick of how big Joshua would be today.
The Garmin 910XT is the premier GPS watch for triathlon training and racing.
Some of you might have noticed there is a new
A work-related blog post for a change, but still about Haskell. My student Alexander Kuhnle has submitted his bachelor thesis
The Google Summer of Code rewriting the Package Tracking System is approaching its end and I m starting to think about deploying it on debian.org. Its scope has expanded over the years and the rewritten PTS will continue this trend by bringing some new features for teams (like the possibility to subscribe to all packages of a team).
I believe that its current hostname (and name) doesn t reflect properly the role of the PTS. Add to this the fact that there s still some work left to be done to reach feature-parity with the current PTS, I m considering deploying it in parallel to the current PTS under a new name.
Package Tracking System is also a bit too long for a name, and sounds more like a description than a name
But if I get rid of packages.qa.debian.org and Package Tracking System , how should we call the new PTS?
I've been hacking on Python for a lot of years now, on various project. For
the last two years, I've beenheavily involved in