Friday, February 22, 2013

Redhat Enterprise Linux, IPv6, and SLAAC

If you are just starting to get into IPv6, you might notice in your researching that there are a very large number of web sites out there that "solve" IPv6 issues by describing how to outright disable the IPv6 protocol on a host entirely.  With the pool of IPv4 addresses having been exhausted for quite some time now, I feel this is a very short-sighted approach.  In the end, it is better to understand the problem and solve it rather than simply sweeping it under the carpet.  What follows is a description of a problem I spent the better part of a day investigating, namely trying to get a RedHat Enterprise Linux (RHEL) server to properly stop auto-assigning its own IPv6 address.


I should mention that I am by no means an "expert" on the topic.  Most of what I have learned has been by piecing together bits of information from various sources that were either vague, incomplete, or outdated (as is much of the IPv6 information out there, sadly), and by brute-force experimentation on my own RHEL systems.  What I write here is completely of a "it worked for me, but YMMV" nature.

IPv6 Addressing Primer

IPv6 is still "new" enough to many people that I feel a quick primer is warranted.

If you have a RedHat Enterprise Linux host on a network and haven't taken any of these steps to disable IPv6, you will probably observe that out of the box, it will have a "link-local" address assigned to every network interface.  These addresses start with "fe80", are automatically generated based on the interface's MAC address, and provide connectivity only on the local network segment.

As soon as global IPv6 connectivity is added to a connected network, you will likely see another address pop up on the interface associated with it.  This will be a globally routeable address that is automatically determined via "Stateless Autoconfiguration" or "SLAAC".  Like the link-local address, it is "self-assigned" by the client system, and is typically based on the interface's MAC address (unless privacy extensions are enabled).  However, the client needs to know information, such as the IPv6 subnet and router information.  These are provided via periodic "router advertisements" or RAs that are ICMPv6 broadcast packets containing these data.  These RAs are periodically broadcast by a router (typically about every 10 minutes), but can also be explicitly requested by the client via a "Router Solicitation" ICMPv6 request.

There are other ways of accomplishing this, such as via DHCPv6, but for the purposes of this article, SLAAC is what we care about.  Plus, my impression is that DHCPv6 is still rather new and isn't used as much.

The Problem

This stateless auto-configuration works great for client systems -- systems where a user is typically sitting at a keyboard and interacting directly with the computer.  Much in the same way a client system frequently receives an IPv4 address automatically via a DHCP server, SLAAC provides a way to automatically assign IPv6 addresses:  turn on IPv6 on the network and things "just work."

The same can't necessarily be said for IPv6-connected servers however.  Just as it is fairly uncommon to let your servers auto-assign an IPv4 address via DHCP (yes, I know it can be done in a perfect world.  Perhaps I'm just old fashioned), you probably don't want an IPv6 address to auto-assign either.  You may not be ready to offer IPv6 services on the system, or you aren't prepared to support IPv6 because you don't have a firewall configuration ready to support it.  No matter what your reason, you probably don't want a server to obtain a globally-routable IPv6 address until you are ready to support it, so you don't want SLAAC.

One way to accomplish this is to simply turn off router advertisements for the entire network.  However, this leads to its own problems -- what if you have some systems that need IPv6 and others that don't?  Or, in some cases this may lead to reduced functionality of the network as a whole (for example, I'm told by the network engineers at work that disabling RAs on a subnet will break VRRP for the subnet, effectively eliminating router redundancy).

The better solution would be to simply configure the server to ignore router advertisements, effectively disabling SLAAC.  Then you can add an IPv6 configuration when you are ready to support it.

Googling will tell you that on a Linux system the way to do this is to set some kernel tunables in /etc/sysctl.conf and restart your networking stack:

net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0

However, on a Redhat Enterprise Linux (or any of the various clones), you might be in for a bit of a surprise.  Despite these settings being in sysctl.conf, upon a reboot, you may find a nasty surprise:

# sysctl -a | grep "accept_ra ="
net.ipv6.conf.default.accept_ra = 1
net.ipv6.conf.all.accept_ra = 1
net.ipv6.conf.eth0.accept_ra = 1
net.ipv6.conf.lo.accept_ra = 1

The active configuration still has the settings enabled!  And if you look at your network interfaces, you'll note that they do in fact still have globally routeable IPv6 addresses.   What goes on?

The Solution

The key lies in the /etc/sysconfig/network file.  The following lines need to be present:


After the lines have been added, you can make the configuration active via a standard network stack restart via "/sbin/service network restart".  If all goes well, you should see the SLAAC-assigned v6 address disappear, and the kernel tunables are changed to more desirable values.

Without these lines, RHEL seems to actively revert to the default case of enabling SLAAC, as seen above.  Also, both lines are required.  Unless the first is present and enabled, any other IPv6 directives appear to be ignored.

Once you are ready to actually support IPv6 on your server, you can then proceed to manually assign an IP address in the appropriate /etc/sysconfig/network-scripts/ifcfg-* file (using the IPV6ADDR and IPV6_DEFAULTGW variables).  Or, you could turn on SLAAC again by setting IPV6_AUTOCONF=yes I suppose, but I wouldn't recommend it.