After the University Of Hawaii began getting Google Over IPv6 in March of 2010, we began noticing problems with user devices on our wireless sending router advertisements and “black-holing” traffic. This problem is, of course made more apparent by initiating Google Over IPv6, which causes significantly more content to be requested by clients over IPv6. Despite first appearances, this is a good thing, since it is a problem that must be faced and dealt with in order to operate a IPv6 network for the near term.
In a nutshell, a “rogue RA” scenario occurs when some device besides an “official” router identifies itself as a router using “router advertisement” ICMP6 messages. Once client hosts see the “rogue” as a router, they may prefer it as their next hop to send traffic out to the Internet.
This can result in one of two problems:
These issues are not IPv6 specific problems. There are numerous similar problems that occur in IPv4 networks, on 802.11 “WiFi” networks, and on Layer 2 switched wired networks.
The best-known cause of rogue RAs on an IPv6 network comes from Windows Vista hosts with Internet Connection Sharing (ICS) enabled. Other causes are probably common, since the “personalities” of rogue RAs seem to differ widely.
Several drafts have been published in the IETF addressing the problem, and there is work currently proceeding in the IETF to address better RA controls, but as with most things, production implementations of standards currently in the IETF will not appear for some time.
Other solutions include layer2 filtering for the “FF02::1″ multicast group, in order that RAs coming from segments where RAs should not be are excluded. This can help isolate the problem, but it does not protect other hosts in the rogue’s “filtered region” from the rogue, and it makes rogue detection more complex.
On the U.H. wireless network, it is simple to listen to the appropriate multicast group, and see router advertisements:
tcpdump -ln -i wlan0 dst host ff02::1
which will show only messages to the all-nodes multicast group, no other traffic.
A notable attribute of all of the rogue RA messages that I have looked at is that they all have “router lifetime” of more than 9000 seconds (2.5 hours), which is higher than the max of 9000 seconds specified by RFC 4861. Apparently this includes mis-configured Microsoft ICS hosts, which advertise router lifetime of 65,536 seconds, and many rogues advertise even higher router lifetime.
Of course, the high router lifetime “feature” has two sides (or perhaps three). It is discouraging that Microsoft released code that “violates” the RFC-specified behavior, and it is further discouraging that clients (perhaps especially Mac OS X) believe these illicit announcements. Of course, it also provides a way to distinguish between “legal” and illegal messages.
Since the clients are so gullible, the rafixd approach is to follow each illegal with a spoofed message (ostensibly) from the rogue which resets the router lifetime to zero, supposedly negating the rogue’s effect.
In the course of seeking a way to mitigate rogue RA problems on our wireless network, and having encountered resistance on the part of rafixd to compile, I lit upon a solution using Python module/tool scapy. My very simple script, shown below, is built upon the scapy docs “simple ARP monitor”, but I have since seen similar RA monitors elsewhere. This script has been working on the UH wireless for about a month, and seems to have mitigate issues that we were having with Apple clients unable to reach Google (and other) IPv6 resources. It differentiates between “bad” and “good” RAs by assuming that good ones will advertise router lifetime <= 9000.
This requires root permissions (to set the listening interface to promiscuous mode and capture packets), and is indicative of the power and utility of Scapy. If you are a network admin/engineer/monitor and you are unfamiliar with Scapy, you really need to look into it. It can change your life…
from scapy.all import *
if ICMPv6ND_RA in pkt and pkt[ICMPv6ND_RA].routerlifetime > 9000:
u = pkt.sprintf("rogue %Ether.src% %IPv6.src% > %IPv6.dst% %ICMPv6ND_RA.routerlifetime%")
s = time.asctime()
t = "\t"
return s + t + u sniff(prn=ra_monitor_callback, filter="dst host ff02::1", store=0, iface="wlan0")