Adventures in IPv6 land

Updated:

Note : This page may contain outdated information and/or broken links; some of the formatting may be mangled due to the many different code-bases this site has been through in over 20 years; my opinions may have changed etc. etc.

I’ve spent the last week experimenting with IPv6; it now means that my whole home network and this website run over IPv6 as well as IPv4. As I’ve spent a while playing with this technology, I thought I’d write my notes up here in the hopes that it will help someone else.

I found that the hardest part of getting my head round IPv6 was forgetting what I previously knew about IPv4 networking. The concepts of NAT, private address space, CIDR subnet masks and so on was getting in the way of me understanding what is ultimately a much simpler system. Let’s face it, the current IPv4 status quo is pretty broken, and we’ve got the Internet this far based on a series of hacks built upon hacks. Sure, it sort of works but it’s pretty ugly - and I think it’s only because we’re so used to IPv4 concepts that I never took a step back and thought about how broken it truly is. 

Needless to say, although the theory should be equally applicable to Windows systems, all this is all written with a heavy Unix-bias as that’s what I use most of them time.

Address format

Here’s the first hurdle : IPv6 addresses look strange at first glance. But they’re really pretty simple: The basic structure of an IPv6 address is a 128-bit hexadecimal string, made up of groups of 16-bit characters, separated by colons. For the sake of brevity, you can omit leading zeros from a field, and when a field is all zeros it can be replaced by two colons - but you can only do this once in an address spec. So, an address of fdce:3916:08df:0000:0000:0000:0000:0001 can be represented by fdce:3916:08df::1. An extreme example of this is the loopback address (the equivalent of 127.0.0.1) - this is shortened to ::1.

IPv6 address subnet masks are also much simpler to understand - part of the address is simply designated as the network prefix. To see how this works, take the example of a unicast address identifying one single host on the internet. These usually consist of a 48-bit network prefix for routing information, a 16-bit subnet identifier and a 64-bit interface identifier to identify the system. So, the address is neatly split into three parts :

rrrr:rrrr:rrrr:ssss:iiii:iiii:iiii:iiii (r = routing identifier, s = subnet identifier, i = interface identifier). 

The number of bits used in a netmask is shown by standard slash notation, e.g.  fe80::211:32ff:fe0f:4a6a/64 shows a 64-bit prefix (fe80:0000:0000:0000 - note the double colon in the original collapsing the repeating zeros) offering a single subnet range (interface identifiers from 0:0:0:0 to ffff:ffff:ffff:ffff). In this case, the interface identifier was 211:32ff:fe0f:4a6a.

The interface identifier is usually always 64 bits long (well, technically you can use some of these fields for subnetting but various things like auto-configuration will break), and will usually stay the same for a host, so networks can easily be reconfigured by simply changing the subnet information. As a further example to see where this may be used: If you are an ISP and have been allocated several /48 ranges, you can then utilise the SLA ID field to carve them up into subnets and give each customer their own /64 range (which they can’t usually split into further subnets). Of course, ISPs will usually be provided with a /32 network, so they could split that up into 65,536 /48 networks and split those into /64s and so on. Read on to see just how massive that address space actually is…

There are also a number of prefixes that have special meanings in an IPv6 network, some of these are :

  • FC00 is the prefix of a site local address. Site local addresses are the equivalent of a private IPv4 address, but as I’ll explain later you probably don’t need to worry about these.
  • FE80 are link-local addresses. More about that later!
  • FF02::1 is an address that multicasts to all nodes on the LAN.
  • FF02::2 is an address that multicasts to all routers on the LAN.

One of the really nice things about IPv6 is that it "just works", usually right out of the box. If you have an IPv6 enabled system, you’ll probably have a link-local address automatically assigned to your network interface. On a Unix system, just look at a network card with "ifconfig", and you should see an inet6 address assigned alongside your usual IPv4 address, e.g.

eth0      Link encap:Ethernet  HWaddr 00:1c:14:01:3b:fc  
          inet addr:192.168.0.121  Bcast:192.168.0.255  Mask:255.255.255.0
          inet6 addr: fe80::21c:14ff:fe01:3bfc/64 Scope:Link

You can identify this address because it starts with "fe80" (mentioned above). These addresses are needed by some internal IPv6 functions (such as neighbour discovery and so on) and are automatically assigned to any IPv6 interface, using an algorithm based on the card’s MAC address. They are not routable and only work within a network segment, but it does mean that you can in theory plug a bunch of systems into a switch, turn them on, and start transferring data over IPv6 without any configuration. If you have a couple of systems you can experiment with, try pinging each of them using "ping6" and using their link-local address. Note: I had to specify the interface (e.g. ping6 -I eth0 fe80::21c:14ff:fe01:3bfc) on some systems, as I got a "connect: invalid argument" error when the kernel didn’t know which interface to use. 

Private networks

Now, this had me stumped for a while as I was approaching this with my IPv4 head on. I started off looking for the equivalent of the 192.168/172.16/10 ranges in IPv4 as I assumed I would be using NAT on my router as usual. I discovered that there is a rough equivalent of these ranges, called Unique Local Addresses (ULA).  These all start with FD as the first digits and avoid the problem of namespace clashes by creating a pseudo-random range. This means no two sites should be using the same range, which happens all the time with IPv4; if your home network and work VPN both use 192.168.1.0/24, one of you will have to change! 

There’s a tool at http://www.sixxs.net/tools/grh/ula/ which lets you generate these ranges using a MAC address as a "seed" and optionally register them in a database. However, I discovered you probably don’t need to worry about these addresses. The reason is simple - the IPv6 address space is huge. To paraphrase Douglas Adams: "You just won’t believe how vastly hugely mindbogglingly big it is" ! If you are assigned a single /64 network, that gives you 18,446,744,073,709,551,616 addresses to play with. To give you an idea of just how much space there is in the IPv6 internet, check out this reference : http://www.potato-people.com/blog/2009/02/ipv6-subnet-size-reference-table/.

This means that workarounds such as NAT or private address spaces just simply don’t need to exist for the most part. You can easily allocate every one of your systems with a publicly reachable address; you just have to set up appropriate firewall/ACL rules at the router to stop unwanted traffic to/from them. Of course, for truly private networks or topologies which may require a private component (database tier of a web stack for instance) you can go ahead and use a ULA range. They may also come in handy if for some reason you can’t use the link-local addresses, e.g. you only have a public /64 range and want to use subnets, but for a home network you don’t need to worry about it. 

Address autoconfiguration

OK, so you have a network range allocated to you. How do you configure the rest of the hosts on your network ? You have two options : RADVD and possibly also DHCPv6. RADVD is a "router advertisment" daemon and lets the rest of the systems on your network know what routes they should use, and can also allocate addresses. It’s very simple to configure; for example on a Linux system running as an IPv6 gateway your radvd.conf would look something like :

interface eth0
{
    AdvSendAdvert on;
    AdvLinkMTU 1280;
    MaxRtrAdvInterval 300;
    prefix 1234:5678:9abc:de::/64
    {
            AdvOnLink on;
            AdvAutonomous on;
    };
};

Your systems will then generate an IPv6 address based on this prefix and a unique interface identifier (again based on MAC address) - so, something like 1234:5678:9abc:de:211:32ff:f20f:4d6a. In a small home network this should suffice quite nicely: All your systems will obtain an IPv6 address from your range (along with their link local address) and know how to route out of your network. Assuming you have a functioning IPv4 stack and DNS etc. configured already (which will probably be the case for the foreseeable future), this could be all you need as long as your nameservers can return IPv6 AAAA records.

For any other kind of IPv6 configuration, you will need to look into DHCPv6 which is a totally different protocol and software stack to IPv4 DHCP, but performs the same function: handing out addresses, configuring name servers, domain name, time servers and so on. 

Command line tools

As IPv6 and supporting protocols such as ICMPv6 are so different from IPv4, a whole bunch of new tools are needed to manage it. In many cases, these tools function more or less the same as their IPv4 counterparts, and are identifiable with the "6" in their name. For example, ping6, traceroute6, ip6tables and so on. In addition, tools such as tcpdump can show IPv6 traffic simply by using the "ip6" filter. These should present few surprises, but where it gets interesting is when it comes to configuring addresses and routes. 

It turns out that the "ifconfig" tool is now deprecated, so although you can view interfaces and IPv6 addresses with it, you should use the "ip" tool under Linux for anything else. I’ll cover other Unix like systems (Solaris, BSD and so on) in another post. Here’s a quick cheatsheet with some examples :

  • Add an address to the eth0 interface : ip -6 addr add <address>/<prefix length> dev eth0
  • Remove an address from the eth1 interface : ip -6 addr del <address>/<prefix length> dev eth1
  • Show all IPv6 addresses : ip -6 addr
  • Show IPv6 neighbours (equivalent to the ARP table in IPv4) : ip -6 neigh
  • Define a default route for eth0 : ip -6 route add default via <gateway address> dev eth0
  • Look up the IPv6 DNS record for a website : dig -t aaaa www.markround.com

Note that the object "ip" operates on (addr,neigh,route etc.) can also be abbreviated. So, "ip -6 -a" will show all IPv6 addresses. 

Anyway, that’s the end of this quick crash course. I know I’ve barely scratched the surface, but sometime later I’ll give some practical examples and detail how I configured an IPv6 tunnel for my home broadband connection, configured my systems (Mac, Windows, Linux, BSD, Solaris, other Unixes and smartphones) and also what I had to do to get this website running over IPv6. As I mentioned before, if you have any comments, feedback or corrections please feel free to either reply in the comments section below or email me at ipv6 [at] mark round [dot] com.

Comments