Home > Articles > Operating Systems, Server

  • Print
  • + Share This
This chapter is from the book

6.5 A Brief Introduction to IPv6 on Linux

IPv6 has been around for several years now. The original proposal was made in 1994 and was approved that same year by the Internet Engineering Steering Group (IESG). In 1998 the basic protocols were approved as drafts by the IETF (Internet Engineering Task Force), which means that the protocols were officially RFCs. The protocol seeks to address some of the shortcomings with IPv4 while retaining features that allow the migration from IPv4 to occur piecewise (meaning that you can run both protocols simultaneously on the same interface and there is a proposed mapping of IPv4 address space to IPv6). IPv6 has the following features:

  • The new addressing scheme has 128 bits, as opposed to IPv4's measly 32 bits. Instead of a horrendous dotted-sixteen, in accordance with IPv4's dotted quad, addresses are represented in hexadecimal using a colon to separate each 16 bits. This still results in 8 fields of 4 hex digits—for example:


    However, you're allowed to drop leading zeroes and to coalesce as many adjacent fields of all zeroes into a single :: as you can. So the address above becomes:


    That might not make you feel a lot better, but then again, users typically won't be typing them out (leave that to the poor lummox running the routers, eh?)

  • IPv6's header structure is extensible, unlike IPv4's fixed-length (and for many people's tastes, somewhat cramped) header. The minimum header length in IPv6 is 40 bytes, but 32 of those are used for the source and destination addresses alone. The remaining 8 bytes contain the version, priority, flow label, payload length, next header, and hop limit fields. The priority and flow label fields are worth mentioning. The former is a 4-bit field meant to be populated by the sender to categorize the traJc for use by queuing and congestion management facilities within the network. The latter is 3 bytes long and is meant to identify a specific traJc stream. This is intended to work hand-in-hand with QoS mechanisms, where a level of service is negotiated for that particular stream when the connection is initiated. The next header field is used to indicate whether or not the data payload begins a TCP or UDP packet, or perhaps more headers.

  • There is a concept of an anycast address, which is a way of the sender's saying please deliver this packet to any of the machines configured with this anycast address. When this packet is routed, it's delivered to the closest routable interface configured with that anycast address. Providing redundancy and ease of configuration has just become much easier. You may want to provide three or four proxy servers on diNerent segments in your network and configure them with the same anycast address. Users will always get the nearest one, even if that means that 3 of the 4 are down for maintenance. And there is no more haggling with what's the right name for multihomed boxes. Assign the same anycast address to all of the interfaces and advertise the anycast address via DNS.

  • The semantics of the multicast address have been refined to include a scope and to do away with broadcast addresses. This makes sense, since a broadcast is just a special case of a multicast anyway. BTW, a regular unique address assigned to a single interface is called a unicast address in IPv6.

  • IPv6 oNers better support for security and authentication (IPsec) and takes advantage of that extensible header format in that only routers that care to look at the extended headers in a stream must do so. Other routers along the packet's path can process the 40-byte header and ignore the rest.

  • The ICMP portion of the protocol, ICMPv6, has options for automatic assignment of unique link-local, site-local, and global addresses. As its name implies, the first of these is valid only on the physical link to which that interface is attached, implying uniqueness only within the realm of that specific link-layer space. Such addresses cannot be routed across subnet boundaries. The site-local address is routable only within a site. And a global address is just that—a unique routable address, anywhere IPv6 is spoken. Obviously the assignment of the global address requires some information from routers at your site with your routable IPv6 address allocation, which is done via an ICMPv6 Router Solicitation. See Table 6.1 for some special IPv6 addresses/networks, or prefixes as they are commonly called.

Table 6.1 Common IPv6 Addresses and Prefixes prefix/address significance


provider based prefix


part of the Test pTLA


link-local prefix


site-local prefix


multicast prefix


Ipv6 mapping of Ipv4 address




default mode

Note that you may also see fe80::/10 expressed as fe80::/64, as the convention is to fill the intervening 54 bits with zeroes and use the last 64 bits for the unique link-local address.3 fec0::/10 similarly is often seen as fec0::xxxx/64—the convention being to fill the address with zeroes until you get to the 16-bit subnet field, which you'll populate for this local address space. The next 64 bits are for a unique host id within one of those 65 thousand subnets.

6.5.1 IPv6 Test Drive

If you aren't already experimenting with IPv6, stop what you're doing (er, finish reading this section first) and set up an IPv6 test lab so that you can start building skills with IPv6. Linux provides a quite capable testbed, and by the time you're reading this, the Linux implementation is far from bleeding edge. We'll work through a couple of examples in this chapter to whet your appetite for further exploration.

You'll need to compile IPv6 support into your kernel (if it's not already there). While it's not an absolute necessity, you'll probably want to have the iproute2 toolset loaded on your box. Its syntax is easier to follow, and more and more of the examples and HOWTOs you find will be using it. Finally, you'll need some applications that support IPv6. This last bit might prove the most diJcult, but applications with IPv6 support compiled into them are becoming more prevalent for Linux. Although you might not have all of your favorite tools, there should be enough on a modern Linux system to at least test things. At the bare minimum, you'll want to have ping6 and tracepath6 (or traceroute6). The first two are part of the iputils package by Alexey Kuznetsov, the networking genius of iproute2 fame, and both can be found at ftp://ftp.inr.ac.ru/ip-routing/. (Most distributions have an iputils package; Debian has iputils-ping and iputils-tracepath.) To configure your kernel for IPv6 support:

Code maturity level options --->
      [*] Prompt for development and/or incomplete code/drivers
Networking options --->
    <M>    The IPv6 protocol (EXPERIMENTAL) 

Then rebuild and boot from the new kernel. To check to see if your kernel already has IPv6 support, look for the existence of the /proc/sys/net/ipv6/ directory in the /proc filesystem, or try modprobe ipv6 (and then check in /proc). You should also be able to ping your loopback address with ping6 ::1 (and notice how, on reasonably fast machines, ping6 returns the time in µsec for submillisecond ping responses).

Configuring Interfaces and Routes for IPv6

In order to become familiar with the syntax and accustomed to working with the new addresses, let's configure something basic and very much like the router silicon in Chapter 3. First let's get two systems on the same subnet speaking to each other by assigning them IPv6 unicast addresses. The systems are drone3, which is our router box with two interfaces, and vivaldi, which is a Linux workstation. We'll use address space from the site-local prefix and invent a subnet following the fec0::xxxx/64 convention mentioned previously. If we assign our network address to be fec0:0:0:1/64, we can take the first two IPs from this, which are fec0:0:0:1:0:0:0:1 and fec0:0:0:1:0:0:0:2. To illustrate how to use both ip and ifconfig, drone3 will be configured with the former and vivaldi with the latter. (Refer to Section 6.3 for more details on ip.)

root@drone3:~# ip -6 addr show
1: lo: <LOOPBACK,UP> mtu 16436 qdisc noqueue
      inet6 ::1/128 scope host
2: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 100
      inet6 fe80::250:56ff:fee2:69/10 scope link
3: eth1: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 100
      inet6 fe80::250:56ff:fead:102/10 scope link

root@drone3:~# ip addr add fec0::1:0:0:0:1/64 dev eth0

root@drone3:~# ip addr list eth0
2: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 100
      link/ether 00:50:56:e2:00:69 brd ff:ff:ff:ff:ff:ff
      inet brd scope global eth0
      inet6 fec0:0:0:1::1/64 scope site
      inet6 fe80::250:56ff:fee2:69/10 scope link
root@drone3:~# ip -6 route show
fe80::/10 dev eth0 proto kernel metric 256 mtu 1500
fe80::/10 dev eth1 proto kernel metric 256 mtu 1500
fec0:0:0:1::/64 dev eth0 proto kernel metric 256 mtu 1500
ff00::/8 dev eth0 proto kernel metric 256 mtu 1500
ff00::/8 dev eth1 proto kernel metric 256 mtu 1500
default dev eth1 proto kernel metric 256 mtu 1500
default dev eth0 proto kernel metric 256 mtu 1500
unreachable default dev lo metric -1 error -101 

There are a few details to notice in the four prior commands. First, we display only the IPv6 addresses for the interfaces on our system using the -6 option for ip. Note that when the ipv6 module is loaded an IPv6 address is automatically added to all the interfaces, the ::1 by convention to the loopback, and automatically generated local-link addresses for the Ethernet adapters. The second command performs the address assignment. Note that I used :: to save typing a few zeroes. The next command is just another form of ip addr list that shows all of the interfaces on eth0 that I'm using to check my work. The fourth command displays the IPv6 routing table on drone3. It looks like a mess, with duplicate entries for fe80::/10, N00::/8, and the default route. I include it to make a point about Linux IPv6 (at least at the time of writing). Although the functionality is there, some of the polish and fine-tuning is yet to be done. In this case, it seems that the interface initialization code assigns these routes to every interface it finds. (Besides the default route, there aren't any really negative side-effects, and you can use ip -6 route flush root prefix to clean these up.) The route that we did want, the one for fec0:0:0:1/64, was added correctly when the IPv6 address was added to eth0. Now let's configure vivaldi with another address on this subnet using ifconfig and route. root@vivaldi:~# ifconfig eth0 add fec0:0:0:1::2/64

root@vivaldi:~# ifconfig eth0
eth0           Link encap:Ethernet HWaddr 00:50:56:40:40:86
                  inet addr: Bcast: Mask:
                  inet6 addr: fec0:0:0:1::2/64 Scope:Site
                  inet6 addr: fe80::250:56ff:fe40:4086/10 Scope:Link
                  UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
                  RX packets:1447 errors:0 dropped:0 overruns:0 frame:0
                  TX packets:1103 errors:0 dropped:0 overruns:0 carrier:0
                  collisions:0 txqueuelen:100
                  RX bytes:178627 (174.4 Kb) TX bytes:200231 (195.5 Kb)
                  Interrupt:9 Base address:0x1080

root@vivaldi:~# route -A inet6
Kernel IPv6 routing table
Destination                              Next   Hop   Flags    Metric   Ref   Use   Iface
::1/128                                     ::                  U           0           0       0 lo
fe80::250:56ff:fe40:4086/128 ::                  U           0           0       0 lo
fe80::/10                                  ::                  UA        256       0       0 eth0
fec0:0:0:1::2/128                     ::                  U           0           0       0 lo
fec0:0:0:1::/64                         ::                  UA        256       0       0 eth0
ff00::/8                                     ::                 UA        256        0       0 eth0
::/0                                            ::                 UDA     256        0       0 eth0

Using ifconfig isn't terribly diNerent from using ip—I tend to believe that the version of ifconfig in the more recent netkits was influenced by iproute2. To view the IPv6 routing table, you have to tell route to use address family inet6. Try displaying the routing table using both route4 and ip and you'll notice some subtle diNerences. For example, route displays host routes for the loopback, link-local, and our newly added unicast address, and indicates that all of these are tied to the loopback device, while ip shows none of these. The last entry in the table is the prefix that designates the default route, ::/0 in IPv6 notation. At this point, we should be able to test communications over our link.

root@drone3:~# ping6 fec0:0:0:1::2
PING fec0:0:0:1::2(fec0:0:0:1::2) from fec0:0:0:1::1 : 56 data bytes
64 bytes from fec0:0:0:1::2: icmp_seq=0 hops=64 time=837 usec
64 bytes from fec0:0:0:1::2: icmp_seq=1 hops=64 time=492 usec
64 bytes from fec0:0:0:1::2: icmp_seq=2 hops=64 time=521 usec

--- fec0:0:0:1::2 ping statistics ---

3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/mdev = 0.492/0.616/0.837/0.158 ms

Now we need to configure the system on the other side of our router. I chose fec0:0:0:8/64 for that network, just so the 1::2 and 2::1s wouldn't be confusing. Of course, anything between 0–NN would be valid in the 16-bit subnet field. We'll assign fec0:0:0:8::1 to eth1 on drone3, and fec0:0:0:8::2 to our interface on the workstation bach. After testing to make sure that bach can ping the address on drone3 and vice versa, we need to enable forwarding for IPv6 packets on drone3. This is done with the command echo "1" >/proc/sys/net/ipv6/conf/all/ forwarding (because we're lazy and don't want to have to echo the value more than once into the /proc/sys/net/ipv6/conf/ethX/forwarding files). So now vivaldi should be able to communicate with bach, right?

root@vivaldi:~# ping6 fec0:0:0:8::2
ping: bind icmp socket: Invalid argument 

Chances are that you caught my mistake right away. We need network routes. But the error message you receive if you don't have an appropriate route is worth noting, as I don't find it particularly intuitive. If we had tried a telnet, we would have seen a more helpful No route to host. The next two statements illustrate how to add a network route using a gateway.

root@vivaldi:~# ip route add fec0:0:0:8::/64 via fec0:0:0:1::1
root@bach:~# route -A inet6 add fec0:0:0:1::/64 gw fec0:0:0:8::1 

6.5.2 radvd—IPv6 Autoconfiguration

If you found typing out those long IPv6 IP addresses and routes in the previous section tedious, good for you; good computer administrators have to possess a certain type of laziness in order to achieve greatness.5 Plus, IPv6 hasn't yet begun to live up to its promise of ease of administration with autoconfiguration. The Router ADVertisement Daemon, or radvd, is alleged to deliver on that promise. Freely available from ftp://ftp.cityline.net/pub/systems/linux/network/ ipv6/radvd/, I recommend that you obtain the patched version from http://v6web.litech.org/radvd/. I loaded the Debian package radvd.

After an initial glance at the configuration file for radvd, /etc/radvd.conf, it reminds one of DHCP. On drone3 I set up the basic default configuration to see if I could autoassign an address to vivaldi. Start radvd with -d3, place it into the background, and then tail -f /var/log/syslog so that you can watch the action.

drone3:~# cat /etc/radvd.conf
interface eth0
    AdvSendAdvert on;
    prefix fec0:0:0:1::/64
         AdvOnLink on;
         AdvAutonomous on;

Then, I rebooted vivaldi to make sure I was starting with a clean configuration, since once the IPv6 module is loaded, it cannot be unloaded, and it's more work to configure all of the interfaces down and back up than it is to reboot. Upon loading the IPv6 module on the new system, I noticed some radvd activity on drone3. Checking on vivaldi, a pseudorandom IPv6 address from the correct prefix was applied, and a default route was configured. (I say pseu-dorandom because you can tell that it's a mangled version of the MAC address of the card configured.) I was able to ping drone3's address on the same link, so that was good. But the default route was not via the site-local gateway address I had configured for drone3, fec0:0:0:1::1, but instead over drone3's link-local address. Although unexpected, it works, making another point for starting to play with IPv6 now, before you actually have to start deploying it, so that you're familiar with how it really works.

If you recall from the configuration of drone3, there were all kinds of unexpected IPv6 routes automatically configured for each interface. Now it was time to see if those collided with each other when I configured radvd to advertise one of the router interfaces. I was afraid that this

drone3:~# ip -6 route list
fe80::/10 dev eth0 proto kernel metric 256 mtu 1500
fe80::/10 dev eth1 proto kernel metric 256 mtu 1500 

was going to spell trouble for drone3 when it tried to respond to clients for replies forwarded back over the gateway. My concern was completely unfounded. Although the gateway address autoassigned via the IPv6 router advertisements contains the local-link address of the router on a given interface, it turns out that clients sending packets always use their local-site unicast address. In fact, you cannot even ping a local-link address (not even your own), most likely because you cannot assign your local-link address as the packet source address.

At this point, you should have a basic IPv6 routing configuration up and running along with some minimal autoconfiguration capabilities. I highly encourage you to load a copy of ethereal onto your equivalent of drone3 and watch some IPv6 traJc traverse your router. Also, drill down on some of the Router solicitation and Router advertisement packets (preferably with copies of the IPv6 RFCs available) to get a feeling for what's happening.

6.5.3 IPv6 Application Support

So far we've gone to a lot of trouble to set up IPv6, and all we can do with it is transmit pings back and forth. What about actually accomplishing some work with IPv6? For that, you'll need applications compiled with IPv6 support as well as some sort of DNS support. In both cases, I believe that including any significant amount of HOWTO detail here in this book is pointless, as the state of the applications will have changed drastically by the time you are reading this. I can, though, at least give you some pointers on where to start.

  • Recent versions of the Bind DNS server software support forward IPv6 address resolution via its AAAA record format (as compared to the A records used for IPv4 addresses). Reverse mappings are still via PTR records but use the designation .ip6.int. instead of .in-addr.arpa when specifying the origin. For more detailed instructions, refer to http://www.isi.edu/simbmanning/v6DNS.html.

  • You should refer to Peter Bieringer's site (http://www.bieringer.de/linux/IPv6/) for a fairly up-to-date matrix of application support available for Linux, including which distributions include IPv6-enabled versions of given applications. Much of the application support is available via patches to apply against the source. One of the primary repositories for these patches is ftp://ftp.kame.net/pub/kame/misc/. (Although the site focuses on BSD support, many of the applications use the same code base.)

  • Keep in mind that network services typically bind to all available network interfaces on startup by listening to (This is the convention for binding to all interfaces using the AF INET address family, which is IPv4.) However, some daemons may be explicitly configured to bind only to a certain interface/address, and these you will need to configure for your IPv6 interfaces. (This isn't specific to IPv6, so don't let this bite you with regular IPv4 either.) The best way to tell if your application is listening for an IPv6 connection is to use either netstat -pln or lsof -Pni to list all of the ports open for listening on your system.

Some applications currently support either IPv6 or IPv4, but not both simultaneously. At the time of writing, the openssh daemon is like that, so I have a separate instance for IPv6 running on port 44. Notice how netstat also detects radvd listening on an IPv6 socket for raw IPv6 protocol 7.

drone3:~# lsof -i | grep IPv6
sshd           391 root  3u  IPv6     6657     TCP *:44 (LISTEN)

drone3:~# netstat -pnl | grep ::
tcp      0         0  :::44 :::* LISTEN        391/sshd
raw     0         0  :::58 :::* 7         175/radvd 

6.5.4 IPv6 over IPv4 Tunnel Broker Setup

You may be thinking to yourself, This is a lot of work for nothing. My boxes could communicate just fine over IPv4. In this example, we're going set up a system to communicate with the rest of the IPv6 world using a tunnel broker to ferry our IPv6 traJc over our existing IPv4 ISP connection. While this won't open up an entire new world of Internet sites, several sites have been kind enough to set up a few Internet servers to be accessible only via IPv6, which helps us to validate our setup. It's also a taste of how things will be in the (perhaps distant) future. A good number of IPv6 tunnel brokers out there oNer free tunnelling accounts to allow you to get your feet wet with IPv6 without having to purchase anything. Most of the tunnel providers will even provide a ::/64 or ::/48 prefix delegation. The only requirements are that you have a valid Internet IPv4 address (a masqueraded private address won't do), that you are not behind a firewall that filters IP protocol 41 traJc (the protocol used by the tunnel), and that you don't mind registering with the tunnel broker provider. (If you're very concerned about privacy, try http://www.freenet6.net/, where you can obtain an anonymous tunnel for a single address.) You'll also need IPv6 support and will want the iproute2 toolset.

Of the tunnel broker setups I have tried, I have to recommend Hurricane Electric's as being by far the simplest to get running quickly. After you register with them, they give you explicit instructions for several diNerent operating systems. The commands are surprisingly simple.

  1. modprobe ipv6

  2. ip tunnel add sb0 mode sit remote tbaddr local laddr ttl 255 Here we use ip to create a tunneling interface named sb0 (for sixbone0) using the mode sit (which is specifically for IPv6 over IPv4) between the tunnel broker's IPv4 address and our local IPv4 address. We also specify that the TTL field for packets traversing this link should be set to 255.

  3. ip link set sb0 up Administratively mark the tunnel as up.

  4. ip addr add ipv6-addr /127 dev sb0 Add the IPv6 address assigned to you by the tunnel broker to the sb0 interface. The /127 subnet mask should make sense, as there are only two addresses on the tunnel, yours and the tunnel broker's on the other end.

  5. ip route add ::/0 dev sb0 Add a default route via the tunneling interface. Note that at the time of writing, Linux doesn't seem to like the default route over the tunnel interface—it works fine using IPv6 over nontunnelled devices. So you may have to replace this with ip route add 2000::0/3 dev sb0, which, if you work out the math, is all of the provider-based IPv6 address space.

  6. ip -f inet6 addr Display all of the IPv6 addresses on your system; just a sanity check. The -f inet6 is equivalent to using -A inet6 with route and can be abbreviated as simply -6.

Once you've configured the tunnel, make sure that you can ping6 the tunnel broker's end of the tunnel, and then start accessing IPv6-only sites. Perhaps try lynx ipv6.research. microsoft.com for your first visit. Then http://www.normos.org/, which has quite a number of IETF standards online.

And that concludes our introduction to IPv6 on Linux. If you think the addresses are a pain to type, let's step back and try to get some perspective on the size of the address space now, as compared to before. It wasn't that long ago that I had to write up a formal justification to my ISP to request a block of eight class C addresses. After all, they couldn't be handing out address space willy-nilly. Fortunately for everyone involved, it was a lot easier to dole out private address space for most user needs and reserve my precious routable IPs for machines that actually needed server interaction with the Internet. Now via an IPv6 broker I can quite easily obtain a /48 prefix from the RFC 2471 IPv6 Testing Address Allocation. This means that the broker allocates a unique address out of their portion of the first 48 bits of the address,6 leaving me with 16 bits for subnets and another 64 bits for hosts. Let's see, with 280 bits of address space, I could host the entire IPv4 Internet address space 281 trillion times. And that's just my /48 prefix Seriously, it's a lot of addresses. I've heard several folks make comments about IPv6 being ready for nanotechnology. With the NLA ID (Next Level Aggregate Identifier) field of 32 bits, we could steal a couple of bits from the TLA ID field and allocate every person on the planet their own /48 prefix. If you read RFC 2471 carefully, you note that the TLA ID may change in the future and that users may have to renumber. (Sounds ominous.) Realize that readdressing in the IPv6 world means changing a couple of values on your router, not having to touch every client in your network (unless you've done something very strange indeed internal to your network with your allocation). Here are a few links for further research on IPv6:

  • + Share This
  • 🔖 Save To Your Account