For those who have been following my articles, they will know I am a fan of RIPng routing protocol for IPv6 in small networks. It requires little to no configuration, turn it on, and it just works.
Recently, I was exchanging emails with the bird
package maintainer for Openwrt, and he suggested I give Babel another look. I looked at the bird
implementation of Babel about five years ago, and found the convergence time to be slow. That was back when bird
was at version 1.6.3. bird
has improved since then and is now at version 3.01.
I set up a small IPv6 test lab to see how the new bird (with Babel support) would perform. In this article I will be making comparisons to RIPng, as to features, and convergence performance.
Babel is a loop-avoiding distance-vector routing protocol that is robust and efficient both in ordinary wired networks and in wireless mesh networks. Based on the loss of hellos the cost of wireless links can be increased, making sketchy wireless links less preferred.
RFC 8966 standardizes the routing protocol. There are two implementations which are supported on OpenWrt routers, babeld
and bird
Although I did try babeld on one of my routers as a test, this lab is using the bird
implementation of the babel protocol.
Like anything in networking, it starts with the physical layer (wireless is a form of physical layer). I setup a lab with 4 nodes, AA, BB, CC,and DD, with a redundant path from AA to CC via BB.
In this lab, I used different versions of OpenWrt and bird
. I wanted to ensure that the different versions would work together using the Babel Protocol.
Node | Name | Device | OpenWrt | Bird |
---|---|---|---|---|
AA | Waihee | TP-Link AX23 | 24.10.0 | v3.01 |
BB | Waimanu | MX6 | 24.10.0-rc7 | v2.15 |
CC | Malmo | BT Home Hub 51 | 23.05.5 | v2.15 |
DD | Makiki | GL-iNET AR750 | 18.06.2 | v1.6.8 |
Babel lab on the dining room table
As of version 2, bird
supports both IPv4 and IPv6 in the same configuration file: /etc/bird.conf
. I used the IPv6-only config in node DD with bird 1.6.8
The Bird Documentation provides an example. The following is a basic Babel IPv6 configuration (/etc/bird.conf
) to just plug and play, without any wireless link additions, which will run similar to RIPng (but converge faster).
# Basic bird.conf for Babel
# Use Source Address Dependent Routing Routing (SADR)
ipv6 sadr table sadr6;
protocol kernel {
ipv6 sadr {
export all; # Default is export none
table sadr6;
};
}
# Required to get info about Net Interfaces from Kernel
protocol device {
}
#advertises directly connected interfaces
protocol direct {
ipv6 sadr {
table sadr6;
};
interface "*";
}
protocol babel {
# duplicate interface line to add additional interfaces
interface "*";
ipv6 sadr {
import all;
export all;
table sadr6;
};
# prevent stale routes staying in the network on restart
randomize router id yes;
}
In the example (above), the interface "*";
could be more specific and list several interfaces, where eth0
is the WAN interface, such as:
interface "br-lan";
interface "eth0";
However, your router may have a different interface than eth0
for the WAN, and the example config is more universal.
Similar to RIPng, a firewall rule is required to allow Babel routes in on the WAN interface. Not surprisingly, Babel uses a different port from RIPng, 6696.
Place the following rule in /etc/config/firewall
for Babel, and reload the firewall: fw4 reload
config rule
option name 'Babel'
option family 'ipv6'
list proto 'udp'
option src 'wan'
option dest_port '6696'
option target 'ACCEPT'
When determining the connectivity path, traceroute6
(the IPv6 version) is your friend. Checking between Node DD and Node AA, the path is (showing the wireless link):
root@makiki:~# traceroute6 aa
traceroute to aa (fdaa::1) from fdcc::e695:6eff:fe01:aa01, 30 hops max, 16 byte packets
1 cc (fdcc::1) 0.849 ms 0.78 ms 0.657 ms
2 fdaa::e255:3dff:fe75:70af (fdaa::e255:3dff:fe75:70af) 1.861 ms 1.525 ms 1.437 ms
3 fdaa::e255:3dff:fe75:70af (fdaa::e255:3dff:fe75:70af) 1.592 ms 1.598 ms 1.437 ms
And below traceroute
is showing the the path via the wired link (between Node DD & AA):
root@makiki:~# traceroute6 aa
traceroute to aa (fdaa::1) from fdaa:0:0:6::d7b, 30 hops max, 16 byte packets
1 fdaa:0:0:6::1 (fdaa:0:0:6::1) 0.74 ms 0.726 ms 0.679 ms
2 fdaa:0:0:4::1 (fdaa:0:0:4::1) 0.917 ms 0.764 ms 0.747 ms
3 aa (fdaa::1) 1.437 ms 1.099 ms 1.02 ms
To test how well Babel can automatically route around failed links, I started a ping from Node DD to Node AA and unplugged the disabled the wifi on Node CC, thus blocking the link the pings were using, and waited...
...
64 bytes from fdaa::1: seq=15 ttl=63 time=2.263 ms
64 bytes from fdaa::1: seq=16 ttl=63 time=2.168 ms
64 bytes from fdaa::1: seq=17 ttl=63 time=2.078 ms
64 bytes from fdaa::1: seq=92 ttl=62 time=1.461 ms
64 bytes from fdaa::1: seq=93 ttl=62 time=1.756 ms
64 bytes from fdaa::1: seq=94 ttl=62 time=1.268 ms
64 bytes from fdaa::1: seq=95 ttl=62 time=1.337 ms
64 bytes from fdaa::1: seq=96 ttl=62 time=1.265 ms
64 bytes from fdaa::1: seq=97 ttl=62 time=1.269 ms
64 bytes from fdaa::1: seq=98 ttl=62 time=1.302 ms
64 bytes from fdaa::1: seq=99 ttl=62 time=1.293 ms
64 bytes from fdaa::1: seq=100 ttl=62 time=1.256 ms
64 bytes from fdaa::1: seq=101 ttl=62 time=1.363 ms
^C
--- aa ping statistics ---
103 packets transmitted, 28 packets received, 72% packet loss
round-trip min/avg/max = 1.256/1.837/2.966 ms
As you can see the outage was 75 seconds (103-287). Not particularly faster than RIPng, and it did fix itself (called convergence) without human intervention.
Starting a ping6
again from node DD to AA, and enabling the 5 Ghz radio, one can measure the time of the outage while Babel recalculates the shortest path. And there was no outage.
Restoring the Node CC to AA wireless link, I noticed that the network would continue to use the path DD->CC->BB->AA, and would not flip to the DD->CC->AA path. This is because the example bird.conf
above has no specific wireless link configuration.
So I tried again, to see how fast the wireless link would take up the traffic. Starting the ping again from Node DD to AA, I pulled the ethernet cable between Node CC & BB.
...
64 bytes from fdaa::1: seq=9 ttl=63 time=2.144 ms
64 bytes from fdaa::1: seq=10 ttl=63 time=2.390 ms
64 bytes from fdaa::1: seq=11 ttl=63 time=2.287 ms
64 bytes from fdaa::1: seq=12 ttl=63 time=2.150 ms
64 bytes from fdaa::1: seq=13 ttl=63 time=2.271 ms
64 bytes from fdaa::1: seq=14 ttl=63 time=2.011 ms
64 bytes from fdaa::1: seq=15 ttl=63 time=1.970 ms
64 bytes from fdaa::1: seq=16 ttl=63 time=2.075 ms
64 bytes from fdaa::1: seq=17 ttl=63 time=1.978 ms
64 bytes from fdaa::1: seq=18 ttl=63 time=2.137 ms
64 bytes from fdaa::1: seq=19 ttl=63 time=3.590 ms
^C
--- aa ping statistics ---
20 packets transmitted, 19 packets received, 5% packet loss
round-trip min/avg/max = 1.619/2.221/4.438 ms
Convergence was 1 second, much faster than RIPng!
bird.conf
for Wireless LinksIn order to tell Babel that there is a wireless link (between Node AA & CC) one must change the Babel configuration in /etc/bird.conf
a bit:
...
protocol babel {
# duplicate interface line to add additional interfaces
interface `"phy0-sta0" {
type wireless;
};
interface "*";
ipv6 sadr {
import all;
export all;
table sadr6;
};
# prevent stale routes staying in the network on restart
randomize router id yes;
}
Because I wanted to change the config as little as possible, I called out the one interface (phy0-sta0) as wireless first, then all other interfaces (indicated as *) would be treated as wired. If you reverse the interface lines, phy0-sta0
will be included in the "*"
and won't be treated as wireless. Order is important.
By defining the phy0-sta0
interface as wireless
, the default cost will be increased to 256
, and therefore the wireless link will be less preferred than wired links (default cost is 96
).
On OpenWrt there is also a Bird CLI package called birdc
. It offers a nice view of what Bird is doing under the covers. Since I was keeping things simple, only Babel was enabled, but bird can certainly redistribute routes between differing routing protocols such as RIPng, OSPF and Babel.
bird
has a nice CLI, which can be invoked with the birdc
command, and get a bird>
prompt. That said, it can also be run on the shell command line, and output can be piped to grep
or even a file. I tend to use the shell
method.
Looking at Babel running in bird, one can see the interfaces, neighbours, and route table entries. Looking at node CC.
root@Malmo:/etc# birdc show babel interface
BIRD 2.15.1 ready.
babel1:
Interface State Auth RX cost Nbrs Timer Next hop (v4) Next hop (v6)
eth0 Up No 96 0 1.910 :: fe80::3897:38ff:fef5:4167
wan Up No 96 1 3.367 192.168.174.214 fe80::3897:38ff:fef5:4167
br-lan Up No 96 1 0.409 192.168.116.1 fe80::1a62:2cff:fe07:a474
phy0-sta0 Up No 256 2 1.999 192.168.117.206 fe80::1a62:2cff:fe07:a477
Since this is IPv6, not surprisingly, the neighbours command displays the link-local addresses of the Babel peers. And this is where vanity link-local addressing really comes in handy. Sadly, I didn't use it in this small lab.
root@Malmo:~# birdc show babel neighbor
BIRD 2.15.1 ready.
babel1:
IP address Interface Metric Routes Hellos Expires Auth RTT (ms)
fe80::e255:3dff:fe75:70ae wan 96 9 16 3.866 No 0.674
fe80::e695:6eff:fe01:aa01 br-lan 96 9 16 4.557 No 0.000
fe80::7ef1:7eff:fe11:bc75 phy0-sta0 96 9 16 2.671 No 2.026
fe80::e255:3dff:fe75:70af phy0-sta0 96 9 16 4.990 No 1.938
The routes show the routing entries which it will use to calculate paths:
root@Waimanu:~# birdc show babel route
BIRD 2.16.1 ready.
babel1:
Prefix Nexthop Interface Metric F Seqno Expires
fdaa:0:0:6::d7b/128 from ::/0 fe80::3897:38ff:fef5:4167 br-lan 192 * 68 51.657
fdaa:0:0:6::d7b/128 from ::/0 fe80::7ef1:7eff:fe11:bc75 br-wan 384 68 53.645
fdaa:0:0:6::d7b/128 from ::/0 fe80::1a62:2cff:fe07:a477 br-wan 388 + 68 49.096
fdaa:0:0:4::/64 from ::/0 fe80::3897:38ff:fef5:4167 br-lan 96 * 9 51.657
fdaa:0:0:4::/64 from ::/0 fe80::7ef1:7eff:fe11:bc75 br-wan 288 9 53.645
fdaa:0:0:4::/64 from ::/0 fe80::1a62:2cff:fe07:a477 br-wan 292 + 9 49.096
fdbb::/64 from ::/0 fe80::3897:38ff:fef5:4167 br-lan 96 * 9 51.657
fdbb::/64 from ::/0 fe80::7ef1:7eff:fe11:bc75 br-wan 288 9 53.645
fdbb::/64 from ::/0 fe80::1a62:2cff:fe07:a477 br-wan 292 + 9 49.096
fdaa:0:0:6::/63 from ::/0 fe80::7ef1:7eff:fe11:bc75 br-wan 288 9 53.645
fdaa:0:0:6::/63 from ::/0 fe80::3897:38ff:fef5:4167 br-lan 96 * 9 51.657
fdaa:0:0:6::/63 from ::/0 fe80::1a62:2cff:fe07:a477 br-wan 292 + 9 49.096
fd8d:54d4:73fa::/60 from ::/0 fe80::7ef1:7eff:fe11:bc75 br-wan 288 9 53.645
fd8d:54d4:73fa::/60 from ::/0 fe80::3897:38ff:fef5:4167 br-lan 96 * 9 51.657
fd8d:54d4:73fa::/60 from ::/0 fe80::1a62:2cff:fe07:a477 br-wan 292 + 9 49.096
fd93:7f97:8ab8:6::/63 from ::/0 fe80::7ef1:7eff:fe11:bc75 br-wan 288 9 53.645
fd93:7f97:8ab8:6::/63 from ::/0 fe80::3897:38ff:fef5:4167 br-lan 96 * 9 51.657
fd93:7f97:8ab8:6::/63 from ::/0 fe80::1a62:2cff:fe07:a477 br-wan 292 + 9 49.096
fdbb:0:0:2::/63 from ::/0 fe80::7ef1:7eff:fe11:bc75 br-wan 288 9 53.645
fdbb:0:0:2::/63 from ::/0 fe80::3897:38ff:fef5:4167 br-lan 96 * 9 51.657
fdbb:0:0:2::/63 from ::/0 fe80::1a62:2cff:fe07:a477 br-wan 292 + 9 49.096
fd8d:54d4:73fa::/64 from ::/0 fe80::7ef1:7eff:fe11:bc75 br-wan 384 68 53.645
fd8d:54d4:73fa::/64 from ::/0 fe80::3897:38ff:fef5:4167 br-lan 192 * 68 51.657
fd8d:54d4:73fa::/64 from ::/0 fe80::1a62:2cff:fe07:a477 br-wan 388 + 68 49.096
fd93:7f97:8ab8::/60 from ::/0 fe80::7ef1:7eff:fe11:bc75 br-wan 96 * 9 53.645
fd93:7f97:8ab8::/60 from ::/0 fe80::3897:38ff:fef5:4167 br-lan 192 9 51.657
fd93:7f97:8ab8::/60 from ::/0 fe80::1a62:2cff:fe07:a477 br-wan 388 9 49.096
fdbb::c9c/128 from ::/0 fe80::3897:38ff:fef5:4167 br-lan 96 * 9 51.657
fdbb::c9c/128 from ::/0 fe80::7ef1:7eff:fe11:bc75 br-wan 288 9 53.645
fdbb::c9c/128 from ::/0 fe80::1a62:2cff:fe07:a477 br-wan 292 + 9 49.096
fdcc::/60 from ::/0 fe80::7ef1:7eff:fe11:bc75 br-wan 288 9 53.645
fdcc::/60 from ::/0 fe80::3897:38ff:fef5:4167 br-lan 96 * 9 51.657
fdcc::/60 from ::/0 fe80::1a62:2cff:fe07:a477 br-wan 292 + 9 49.096
fd93:7f97:8ab8:4::/64 from ::/0 fe80::3897:38ff:fef5:4167 br-lan 96 * 9 51.657
fd93:7f97:8ab8:4::/64 from ::/0 fe80::7ef1:7eff:fe11:bc75 br-wan 288 9 53.645
fd93:7f97:8ab8:4::/64 from ::/0 fe80::1a62:2cff:fe07:a477 br-wan 292 + 9 49.096
fdaa::/60 from ::/0 fe80::7ef1:7eff:fe11:bc75 br-wan 96 * 9 53.645
fdaa::/60 from ::/0 fe80::3897:38ff:fef5:4167 br-lan 192 9 51.657
fdaa::/60 from ::/0 fe80::1a62:2cff:fe07:a477 br-wan 388 9 49.096
fd68:7aa9:9d9a::/64 from ::/0 fe80::3897:38ff:fef5:4167 br-lan 96 * 9 51.657
fd68:7aa9:9d9a::/64 from ::/0 fe80::7ef1:7eff:fe11:bc75 br-wan 288 9 53.645
fd68:7aa9:9d9a::/64 from ::/0 fe80::1a62:2cff:fe07:a477 br-wan 292 + 9 49.096
fdaa:0:0:4::c9c/128 from ::/0 fe80::3897:38ff:fef5:4167 br-lan 96 * 9 51.657
fdaa:0:0:4::c9c/128 from ::/0 fe80::7ef1:7eff:fe11:bc75 br-wan 288 9 53.645
fdaa:0:0:4::c9c/128 from ::/0 fe80::1a62:2cff:fe07:a477 br-wan 292 + 9 49.096
As you can see four (4) nodes with only four links (one is wireless) can create quite the routing table. That said, 18 of the routes are due to ULAs being set on each router (a default in OpenWrt). RIPng also creates a lot of routes as well.
Unlike RIPng which has no concept of RouterID, Babel uses RouterID to identify the source of routes and avoid loops. Using wireshark
to sniff the Babel packets (UDP port 6696), it can be seen that the RouterIDs are being transmitted. The line in at the bottom of the config file helps flush old routes from the other routers when restarting bird (or reloading a config)
# prevent stale routes staying in the network on restart
randomize router id yes;
Babel is still being actively developed, and has a more modern approach to wireless links (something that was near non-existent when RIPng was being standardized back in 1997). There are more controls that can be applied to Babel for wireless links. Refer to the Babel Doc for more info.
Additionally, Babel has support for authentication between neighbours (something OSPF has had since the 1990s). Sadly, RIPng does not have this feature, as the IPv6 team was enamored with IPSec over IPv6 at the time. Authentication can be passwords, or even encrypted hashes (hmac sha1 | hmac sha256 | hmac sha384 | hmac sha512| blake2s128 | blake2s256 | blake2b256 | blake2b512), so that even your router jocks won't know the passwords by looking at the bird.conf
file.
Babel also has the concept of Round Trip Time (RTT), as you will note in the show babel neighbors
command. The config file can be further adjusted to handle variable (read: wireless links) RTT time, and take action if the RTT exceeds a configured value.
I did configure the phy0-sta0
(on node CC) as wireless. This caused the network to prefer the ethernet-only path (DD->CC->BB->AA). By removing the ethernet cable between nodes CC & BB, the reconvegence to the wireless link was quite fast (about 5 seconds). However, when restoring the ethernet cable, I noted that the network would reconverge again in about 5 seconds, but then after another 30 seconds or so there would be nearly a full minute of outage.
This second outage may be a result of my lab setup. Even with the second minute long outage, the total convergence time is much less than RIPng. Looking back at my old RIPng notes, where the outages were closer to three (3) minutes.
Of course some of that delay is that RIPng only sends updates every 30 seconds. Babel on the other hand send hello messages out every two (2) seconds.
Like RIPng, Babel is easy to set up without having to understand the complexities of more enterprise-level routing protocols such as OSPF or IS-IS. It has been the choice of mesh-networks where radio links are variable. It is easy to setup on OpenWrt routers and provides redundancy in your SOHO network with wired and wireless links. I think I'll be converting my SOHO network from RIPng to Babel soon.
Notes:
br-lan
. Therefore both the wired connection to node BB and wireless to node CC were in the same "broadcast domain". I suspect this caused the longer convergence times for Babel.Craig Miller -- 11 Februay 2025