RIPng the forgotten routing protocol

Traffic

In a small network of more than one router, one will quickly figure out that you can't get there from here, even with DHCPv6-PD (Prefix Delegate) providing IPv6 addressing,. Sure you can get to the internet, but you can't get to other parts of the network which are behind the other routers. You need a routing protocol to share information between the routers on your small network.

Back in the 90s there was RIPv2

Back in 1996 when I as attending an Advanced IP Routing class at Bay Networks, I learned that RIPv2 RFC 2453 solved most of the problems of the venerable RIP (Routing Information Protocol, RFC 1058). Specifically it improved:

We gave the instructor a hard time, because every network problem he presented on why we should use OSPF RFC 2328, could have been solved more simply with RIPv2. Perhaps the examples were too simple to really show how OSPF can get you out of trouble, but the take-away for me was that RIPv2 can work in most situations.

Fast Forward 20 Years

Fast forward 20 years, we have run out of IPv4 addresses, and although dual stack deployment is moving along well (except in the Enterprise), it is only halfway there. It is going to be easier to manage an IPv6-only network, going forward.

So when we all have /56s or larger delegated to our house/SOHO, and we put in multiple IPv6 capable routers, how will the routers convey connectivity information? Well we could use HomeNet RFC 7788, but currently home routers don't come out of the box configured for HomeNet (for example the Wifi is on the same subnet as the LAN). Not to diminish HomeNet's work, but there is a forgotten protocol that can solve the problem.

The Forgotten Protocol: RIPng

RIPng (RFC 2080), the IPv6 cousin of RIPv2, has all the advantages of RIPv2, except it conveys IPv6 route information (rather than RIPv2's IPv4 only info). But the same principles apply, easy to deploy, works pretty well, solves the problem (of distributing routes to multiple routers).

But finding support, and documentation on RIPng on the internet is threadbare. Even the venerable BIRD (BIRD Internet Routing Daemon) has a non-functional example configuration, not to mention they just call it RIP confusing whether it is for IPv4 or IPv6.

But why has it been forgotten? Because real men use OSPF (or IS-IS). And you can too, if you want to spend a week (or more) learning the complexities of the protocol. But what if you just wanted it to work, and move onto the next problem.

Getting from here to there

Although some ISPs will give a fairly stable prefix (via DHCPv6-PD), others do not, leading to a very dynamic prefix environment at home or the SOHO (see Request: Less Dynamic Prefix, Mr./Ms. ISP. Cascaded routers can further divide the /56 via DHCPv6-PD allocating smaller chunks downstream. If one only needs to get to the internet (everything is in the cloud, right?), this works quite well. After all each cascaded router has a default route pointing upstream (towards the ISP).

But what if you have your NAS on Network B, and you client on Network C, it isn't going to work.

Network

Router 3 doesn't know about Router 2, or even the subnet B behind Router 2

Of course, you could put in static route in each of Routers 2 & 3. But not only does that take a bit more knowledge of routing (hint: the next hop is a link-local address), but it doesn't work in a dynamic prefix (where the ISP is changing the prefix) environment.

RIPng to the rescue

So, enter an easy to configure, easy to use routing protocol, RIPng. Running RIPng, Routers 2 & 3 can share information about themselves, and more importantly, the subnets they are serving. If the ISP changes the prefix, DHCPv6-PD will propagate the new prefix down to the cascaded routers, and RIPng will refresh the routing information between the routers, all automagically.

Making RIPng work

First, use router software that supports RIPng. Of course Cisco supports RIPng, but not everyone has multiples of these at home. OpenWrt/LEDE is a good option, since if your router is not over 5 years old, it is probably supported. And it has BIRD as an easy to install package.

First, install bird6 & birdc6 (the CLI for bird6). The bird6 package is a direct port from the bird maintainers, and therefore uses its own configuration file (rather than UCI). But RIPng is easy, so onto the next step.

Uncomment the following lines in the package-provided configuration file /etc/bird6.conf


router id 192.168.0.1;

protocol kernel {
    learn;          # Learn all alien routes from the kernel
    scan time 20;       # Scan kernel routing table every 20 seconds
    export all;     # Default is export none
}

protocol rip {
    import all;
    export all;
    infinity 16;
    interface "*" { mode multicast; };
}

Even though RIPng does not require a "router id" bird6 won't run without it. Restart the bird6 daemon with /etc/init.d/bird6 restart, and you are done*

Of course you could optimize it a bit by telling the config which interfaces to use, such as interface "eth1" {mode multicast }; but this article is about making it easy, and it doesn't break anything sending route updates out the other interfaces.

How to tell RIPng is working

ssh to the router. The birdc6 CLI is the easiest in showing the routing table, which includes sources of the routes, like this:

# bindc6
bird> show route
fdcd:5cb4:d0e1::/64 dev br-lan [kernel1 2017-11-30] * (10)
fdcd:5cb4:d0e1::/48 unreachable [kernel1 2017-11-30] * (10)
2001:db8:ebbd:8::/64 dev br-lan [kernel1 2017-11-30] * (10)
2001:db8:ebbd:8::/62 unreachable [kernel1 2017-11-30] * (10)
2001:db8:ebbd:c::/64 via fe80::224:a5ff:fed7:3089 on eth1 [rip1 23:30:58] * (120/2)
2001:db8:ebbd:c::/62 via fe80::224:a5ff:fed7:3089 on eth1 [rip1 23:30:58] * (120/2)
2001:db8:ebbd::/60 via fe80::221:29ff:fec3:6cb0 on eth1 [rip1 00:00:44] * (120/2)
2001:db8:ebbd:4::/64 via fe80::221:29ff:fec3:6cb0 on eth1 [rip1 00:00:44] * (120/2)
2001:db8:ebbd:4::/62 via fe80::221:29ff:fec3:6cb0 on eth1 [rip1 00:00:44] * (120/2)
2001:270:ebbd::/60 via fe80::221:29ff:fec3:6cb0 on eth1 [kernel1 2017-11-30] * (10)
fd37:5218::/64     via fe80::221:29ff:fec3:6cb0 on eth1 [rip1 00:00:44] * (120/2)
fd37:5218::/48     via fe80::221:29ff:fec3:6cb0 on eth1 [rip1 00:00:44] * (120/2)
64:ff9b::/96       via fe80::221:29ff:fec3:6cb0 on eth1 [rip1 00:00:44] * (120/2)
fd11::/64          via fe80::224:a5ff:fed7:3089 on eth1 [rip1 23:30:58] * (120/2)
fd11::/48          via fe80::224:a5ff:fed7:3089 on eth1 [rip1 23:30:58] * (120/2)
bird> 

All those "rip1" lines are routes learned from RIPng. Since no filters were used, not only are the globally unique addresses shared, but so are the Unique Local addresses (ULA) configured in each router. Here's the fun part, even though there is not ULA on subnet A, RIPng provides connectivity between the ULA islands. Of course, it would be better to have a single ULA /48 for the entire network, but this is a test network, and OpenWrt/LEDE creates a ULA by default for each router. I wanted to see how RIPng would handle this non-optimal situation. And, it handles is pretty well.

While it is possible to figure out who the other RIPng routers are, it is easier to use the bindc6 command:

bird> show rip neigh
rip1:
IP address                Interface  Metric Routes   Seen
fe80::224:a5ff:fed7:3089  eth1            1      4     27
fe80::221:29ff:fec3:6cb0  eth1            1      9      6
bird> 

What's missing in this simple configuration: authentication

RIPv2 included authentication, to ensure that the routing updates that are being received, are from the correct sources.

RIPng RFC 2080 does not use authentication natively, but rather relies on IPv6's Authentication Header (AH), one of the extension header types, for authentication (see Stretching IPv6 with extension headers). The configuration above, does not utilize the AH header, however, the risk should be low in a small network. More to come later on RIPng with AH.

Don't forget DNS

Of course, if you creating a network with IPv6 and more than one subnet, don't forget the DNS. Have each router forward DNS requests to your local DNS server (you do have a local DNS server, right?).

Add your local DNS server address the following to dnsmasq section of /etc/config/dhcp

config dnsmasq
    list server '2001:db8:ebbd:0::dbc8'

Now all your IPv6 addresses will have names, and it will be a breeze getting around your network.

More Details: sniffing the routing updates

RIPng sends updates to the IPV6 multicast address FF02::9 (following the lead of RIPv2 destination address of 224.0.0.9). In IPv6 FF02 scope is limited to the link, and routers should not forward the packet to other subnets. Using tcpdump (or wireshark) it is easy to see the routes being advertised by each router. RIPng uses UDP port 521 to transmit route updates.

$ sudo tcpdump -i 1 -vv  -l -n port 521
tcpdump: listening on enp0s10, link-type EN10MB (Ethernet), capture size 262144 bytes
13:39:55.166377 IP6 (class 0xc0, flowlabel 0xf7a9f, hlim 255, next-header UDP (17) payload length: 412) fe80::86c9:b2ff:fe54:305b.521 > ff02::9.521: [udp sum ok]  ripng-resp 20:
    ::/0 (16)
    fdcd:5cb4:d0e1::/64 (1)
    fdcd:5cb4:d0e1::/48 (1)
    2001:db8:ebbd:8::/64 (1)
    2001:db8:ebbd:8::/62 (1)
    2001:db8:ebbd:c::/64 (16)
    2001:db8:ebbd:c::/62 (16)
    2001:db8:ebbd::/48 (16)
    2001:db8:ebbd::/64 (16)
    2001:db8:ebbd::/60 (16)
    2001:db8:ebbd:4::/64 (16)
    2001:db8:ebbd:4::/62 (16)
    fe80::221:29ff:fec3:6cb0/0 (255)
    2001:270:ebbd::/60 (1)
    ::/0 (255)
    fd37:5218::/64 (16)
    fd37:5218::/48 (16)
    64:ff9b::/96 (16)
    fd11::/64 (16)
    fd11::/48 (16)
13:40:09.190222 IP6 (class 0xc0, flowlabel 0x8aa19, hlim 255, next-header UDP (17) payload length: 492) fe80::221:29ff:fec3:6cb0.521 > ff02::9.521: [udp sum ok]  ripng-resp 24:
    fe80::224:a5ff:fef1:7ca/0 (255)
    ::/0 (1)
    ::/0 (255)
    fdcd:5cb4:d0e1::/64 (16)
    fdcd:5cb4:d0e1::/48 (16)
    2001:db8:ebbd:8::/64 (16)
    2001:db8:ebbd:8::/62 (16)
    2001:db8:ebbd:c::/64 (16)
    2001:db8:ebbd:c::/62 (16)
    fe80::224:a5ff:fef1:7ca/0 (255)
    2001:db8:ebbd::/48 (1)
    ::/0 (255)
    2001:db8:ebbd::/64 (1)
    fe80::86c9:b2ff:fe54:305b/0 (255)
    2001:db8:ebbd::/60 (1)
    ::/0 (255)
    2001:db8:ebbd:4::/64 (1)
    2001:db8:ebbd:4::/62 (1)
    2001:270:ebbd::/60 (16)
    fd37:5218::/64 (1)
    fd37:5218::/48 (1)
    64:ff9b::/96 (1)
    fd11::/64 (16)
    fd11::/48 (16)
13:40:18.200050 IP6 (class 0xc0, flowlabel 0x84a15, hlim 255, next-header UDP (17) payload length: 372) fe80::224:a5ff:fed7:3089.521 > ff02::9.521: [udp sum ok]  ripng-resp 18:
    ::/0 (16)
    fdcd:5cb4:d0e1::/64 (16)
    fdcd:5cb4:d0e1::/48 (16)
    2001:db8:ebbd:8::/64 (16)
    2001:db8:ebbd:8::/62 (16)
    2001:db8:ebbd:c::/62 (1)
    2001:db8:ebbd:c::/64 (1)
    2001:db8:ebbd::/48 (16)
    2001:db8:ebbd::/64 (16)
    2001:db8:ebbd::/60 (16)
    2001:db8:ebbd:4::/64 (16)
    2001:db8:ebbd:4::/62 (16)
    2001:270:ebbd::/60 (16)
    fd37:5218::/64 (16)
    fd37:5218::/48 (16)
    64:ff9b::/96 (16)
    fd11::/64 (1)
    fd11::/48 (1)
^C
3 packets captured
3 packets received by filter
0 packets dropped by kernel

RIPng is a Distance Vector routing protocol, which means that the cost of a particular route is increased by one as it crosses each router (the distance). Each router hears routes from other routers and increases the cost. As in the original RIP, a cost of 16 is considered unreachable (or infinity). tcpdump displays the cost in parens, after each route. The routes with (16) will not be placed into the routing table.

The route 64:ff9b::/96 (1) is my NAT64 router (read: my IPv6-only network).

Wrapping up RIPng

RIPng may have been forgotten, and overlooked for the past 20 years, but that doesn't mean it can't still solve problems for small networks (say less that 25 routers) quickly and easily. Sure, you can put in a more complex routing protocol like OSPFv3, but on the other hand, you might want to spend your time just going to the NAS, fire up a good movie, and enjoy the popcorn.

*traffic photo - Creative Commons

**if you are running a firewall, the default on OpenWrt/LEDE, you will need to put in a rule to accept IPv6 UDP port 521


Craig Miller -- 2 December 2017