Another IPv6 Crash Course For Linux: Real IPv6 Addresses, Routing, Name Services



In the first IPv6 for Linux crash course, we covered some of the bare basics of IPv6 on Linux. Today we’re going to learn how to use routable IPv6 addresses, some iptables rules to keep our experimentation from leaking out into the world, and about implementing DNS in IPv6.

You can perform all these tests on any two Linux PCs on your LAN without getting in the way of your normal IPv4 activities. Just remember to undo everything that you don’t want to be permanent when you’re finished. Save time and hassle by having SSH set up on all your PCs; then you can park yourself comfortably in one place and run most of these tests over SSH sessions and never get up, except at healthy intervals to prevent embolisms and joint seizures.

Preventing IPv6 Leaks

We’ll be practicing with real global unicast IPv6 addresses, since most of us don’t have any real ones assigned to us by a friendly IPv6-supporting ISP. It’s unlikely this will cause problems, but prevention is cheaper than cleanup, so these iptables rule block all IPv6 traffic from entering or leaving your LAN:

ip6tables -P output drop
ip6tables -p input drop
ip6tables -p forward drop

If you have a different firewall then of course you’ll have to do whatever works for it.

Assigning Global IPv6 Addresses

The link-local addresses we used in part 1 are very limited. To do real work we need to use global unicast addresses. These are the squillions of addresses in the 2000::/3 range. As an excellent reader reminded me, the 2001:0DB8::/32 block is reserved for documentation and examples, so that is what we are going to use. So we can assign a new global unicast address to an interface like this:

# ip -6 addr add 2001:0db8::1/64 dev eth0

/64 is not CIDR (classless inter-domain routing) notation, but the size in bits of the prefix. This artistic ASCII diagram explains it:


network ID   subnet  interface ID


The network ID is determined by your ISP when you receive a block of real IPv6 addresses. You control the subnet and interface. So there are 64 bits for the network ID + subnet, and 64 bits just for the interface ID. The entire IPv4 address space is 32 bits, so your personal block of IP addresses is going to last you a long, long time.

Now you can ping this address locally. I’ve expanded it just as a reminder of how long IPv6 addresses really are. The ping6 command helpfully shortens it:

$ ping6 2001:db8:0:0:0:0:0:1
PING 2001:db8:0:0:0:0:0:1(2001:db8::1) 56 data bytes
64 bytes from 2001:db8::1: icmp_seq=1 ttl=64 time=0.043 ms

But you can’t ping from another PC, because just like IPv4 we need a router to do this.

Adding More Addresses

You can add more addresses to a single network interface, or to more of your PCs. Just count up sequentially in hexadecimal, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, 10, 11, and so on, like this:




Your router must support IPv6, of course. If you’re using a Linux PC as your test machine you’re in business. First start IPv6 forwarding:

# sysctl -w net.ipv6.conf.all.forwarding=1

Verify forwarding is enabled:

# cat /proc/sys/net/ipv6/conf/eth0/forwarding

A return value of 1 means it is enabled, 0 means it is not. Now install radvd, the router advertiser daemon. Then create /etc/radvd.conf with this simple configuration:


interface eth0
   AdvSendAdvert on;
   prefix 2001:db8::/64


Copy it exactly, changing only the interface name if necessary. Now create an IPv6 address on your router, or PC acting as your router, and set an IPv6 route:

# ip address add 2001:db8::1a/64 dev eth0
# ip route add 2001:db8::/64 dev eth0

Check your work by running ifconfig and ip -6 route show. Your ip output should look similar to this:

2001:db8::/64 dev eth0 proto kernel metric 256 mtu 1500 advmss 1440 hoplimit 4294967295
2001:db8::/64 dev eth0 metric 1024 mtu 1500 advmss 1440 hoplimit 4294967295
fe80::/64 dev eth0 proto kernel metric 256 mtu 1500 advmss 1440 hoplimit 4294967295

Finally, start up the radvd daemon:

# etc/init.d/radvd start

You should now be able to ping the router from another PC. Note that unlike our link-local addresses, we don’t need to specify the network interface:

$ ping6 2001:0db8::1a

All of the computers on the same switch as your IPv6 router should have new addresses in the 2001:0db8::/64 range. You can look these up and go on a ping6 frenzy. A default gateway (for connecting to other networks) is the IPv6 address of your router:

# ip -6 route add default via 2001:db8::1a


Of course it’s less work to configure it in radvd.conf and have it advertised automatically by adding these lines:

route ::/0

Then when you run ip -6 route show you’ll see this:

default via 2001:db8::1a dev eth0 metric 1024 mtu 1500 advmss 1440 hoplimit 0

What if you don’t want radvd blasting IPv6 addresses all over your network? No worries, for you can limit its clients by listing their IPv6 link-local addresses like this:


       interface eth0
               AdvSendAdvert on;
               prefix 2001:db8::/64
                       AdvOnLink on;
                       AdvAutonomous on;
               route ::/0


Mind your braces and semi-colons!

IPv6 Address Calculator

Understanding the binary math behind IPv6 addressing is crucial to understanding IPv6 addresses. Working out any conversions manually is fun and instructive a couple of times, and then it becomes an error-prone exercise in tedium, so don’t be shy about using a calculator. ip6calc is available on most Linux distributions, and there are many online Web-based IPv6 calculators.

Name Services

Setting up DNS for IPv6 is a whole article by itself, so hopefully these hints will help you.

The idea behind IPv6 auto-configuration (via link-local addressing and router advertisements) is to make it easy for hosts to join and leave the network without needing a DHCP server. You still need a DNS server to assign hostnames. BIND9 supports IPv6, and in classic BIND fashion requires too much work and too many separate configuration files. (DNS is matching numbers to names, so it seems it shouldn’t require such heavy lifting. And it doesn’t in other DNS servers, but none of them have complete IPv6 support yet.) Please refer to the BIND9 manual because it’s too long to include here. The Linux IPv6 howto has some hints on configuring BIND that breaks it down into digestible chunks.

In part 1 we did a quick review of using plain old hosts files. Another option is using Dnsmasq, which is my favorite for LAN name services. It supports IPv6 for DNS and TFTP, but not DHCP, so it’s not yet a complete solution. Dnsmasq makes the contents of /etc/hosts available in DNS. First set up static IPv6 addresses on the clients you want to access by hostname, such as servers and any machines you administer remotely. Then write an /etc/hosts file on your Dnsmasq server and restart Dnsmasq. Next, install rdnssd, the IPv6 recursive DNS server discovery daemon, on your radvd server. Then add this line to /etc/radvd.conf, using the IPv6 address of your Dnsmasq server:

RDNSS 2001:0db8::1b


Restart radvd and it should all work. rdnssd and radvd are works in progress, so it may or may not be glitchy. On my Debian Sid systems everything works nicely. So far.