IPv6 setup

:: ipv6, computers, technical

Some time ago I was finally in a position where my ISP had IPv6 capabilities and I was in full control of my internet setup (IE it wasn’t controlled by landlords or shared with roommates who had stronger opinions than mine). So naturally I decided to join the internet of the future (that should have been the internet of the 90’s) and get IPv6 set up.

Instead of plugging my modem directly into my wireless router1, I plug my modem into a Debian box which I use as a router and home server. Aside from the server components (such as file storage, access, syncing with various protocols…), this is nice because a Linux box makes a great router. You can set up port forwarding, dhcp, dns (for the local network and for caching), and any static portions of the network using text files that you can keep under version control, and you can access it easily with ssh. Crappy router firmware configuration interfaces avoided (mostly)! But Debian doesn’t come out of the box configured to be an IPv6 router, and finding out how to set that up was a huge pain! Hopefully this helps out others trying to do the same.

To start off, let’s start in /etc/network/interfaces. You likely already have ipv4 set up correctly there for each interface on your machine. It turns out this part is easier for IPv6 — you just have to add iface <interface-name> inet6 auto for each interface. Here is a completed version:

# /etc/network/interfaces

# The loopback network interface
auto lo
iface lo inet loopback

# External-facing interface
allow-hotplug eth1
iface eth1 inet dhcp
iface eth1 inet6 auto

# Internal-facing interface
allow-hotplug eth0
iface eth0 inet static
    address 192.168.1.1
    netmask 255.255.255.0
iface eth0 inet6 auto

Next let’s talk about forwarding. Sysctl needs to tell your kernel that it should forward packets rather than just dropping them when it sees that they are not destined for the machine’s ip address. We also want to allow it to accept router advertisements, which we’ll talk about more later. You can do this in /etc/sysctl.conf, but preferably in a file in /etc/sysctl.d/. Here is my configuration:

# forwarding for ipv4 is this simple:
net.ipv4.ip_forward=1

# This allows you to forward, but DISABLES router advertisement acceptance
# unless you set it up properly as well.
# One of these lines may be unnecessary, but I'm not fussing with it now
# that it's finally working.
net.ipv6.conf.all.forwarding=1
net.ipv6.conf.default.forwarding=1

# accept_ra is a very special "boolean" value which defaults to 1: accept
# router advertisements!  However, with forwarding enabled a value of 1 reverts
# to acting like a value of 0 (don't accept advertisements), so you need to set
# this boolean's magical *third* value of 2, which accepts advertisements
# even with forwarding on!
net.ipv6.conf.all.accept_ra=2
net.ipv6.conf.default.accept_ra=2

Now let’s talk about getting an IPv6 address, and even an IPv6 subnet. There are two ways to dynamically get IPv6 addresses: dhcpv6 and stateless autoconfiguration. Stateless autoconfiguration is awesome, and it’s what clients of our router will use. Basically, with stateless autoconfiguration a router advertises a /64 IPv6 subnet, and clients use some heuristic (based on their mac address or something) to choose their own address in that range. It’s not what we will actually use on our server (you can get an IPv6 address just stateless autoconfiguration on the server, but not a subnet to let clients do the same) — so maybe we should not have bothered setting the “boolean” accept_ra to 2, but I think it’s good to have it as a fallback so that if for some reason your server can’t get a DHCP lease it can at least get an address via autoconf. And it took me forever to get that working before I realised I did in fact need to use dhcpv6 for the server.

So let’s move on to dhcpv6. I set this up with wide-dhcpv6-client. Here is my annotated config file /etc/wide-dhcpv6/dhcp6c.conf:

# Default dhpc6c configuration: it assumes the address is autoconfigured using
# router advertisements.
profile default
{
  information-only;

  request domain-name-servers;
  request domain-name;

  script "/etc/wide-dhcpv6/dhcp6c-script";
};

# external interface
interface eth1 {
    send rapid-commit;

    # send requests for a /128 address and a /60 prefix, linked to
    # their configurations below by their ID (0 for both)
    send ia-na 0;
    send ia-pd 0;
};

# ia-na gives you a /128 bit address for the external interface.
# No real configuration needed.
id-assoc na 0 {
};

# ia-pd is prefix delegation, so we can have a subnet
id-assoc pd 0 {

    # The prefix statement says what prefix we want and how big
    # our subnet should be.  The :: prefix says we'll just take
    # whatever it gives us, but we want a /60 subnet.
    # The infinity is for the pltime, and I don't remember what
    # that is.  But infinity works there.
    prefix ::/60 infinity;

    # Since we have a /60 subnet, we can set up several /64 subnets.
    # Subnets should have a prefix length of at least 64 bits for
    # stateless autoconfiguration to work.

    # Internal interface (LAN)
    prefix-interface eth0 {
        # sla-len should be the difference between the size of the
        # whole subnet we have available and what we want to assign
        # to this interface.  64 - 60 = 4.
        sla-len 4;
        # This ID determines the rest of the prefix bits to form a /64 prefix
        sla-id 0;
        # This ID determines the rest of this interface's address.
        # With this we will have an address of <64-bit-prefix>::1
        ifid 1;
    };
};

So now all we have left is to communicate to clients so they can have addresses. To do this we can either use a dhcpv6 server, or send router advertisements for autoconfiguration. I see autoconfiguration as part of the magic of IPv6 for some reason, and autoconfiguration seems to “just work” on all my machines, so that’s what I set up.

To set up router advertisements, use radvd. The final bit of (not really documented) magic to get radvd to work right was the last thing I needed, so here is my /etc/radvd.conf:

# eth0 is still my internal interface
interface eth0
{
        AdvSendAdvert on;

        # Advertise at least every 30 seconds
        MaxRtrAdvInterval 30;

        # Using the magic :: prefix tells it to autodetect it.
        # For some reason I didn't find this documented anywhere until
        # I stumbled across it on the internet somewhere.  I don't
        # know why -- I would never dream of hard coding an IPv6 address
        # in a configuration file.  Dynamic network setup is part of the
        # dream of IPv6 in my eyes.
        prefix ::/64
        {
        };
};

Boom. There you have it. With those configurations, I now have my Debian router giving IPv6 addresses to all of the computers in my network. That’s right. All of my computers now get a public IP address. It’s so beautiful. Now at this point if you’re saying “but I need a firewall!”, you’re full of crap2, but you can use ip6tables to make a firewall if you want. But if you want to let your computers talk directly with each other (which is the dream of IPv6), you may need to change some router settings. Wireless router manufacturers have decided to continue the erroneous war on on peer-to-peer computing on the IPv6 front, and by default they drop incoming IPv6 traffic. So you may need to tell your router to not drop incoming traffic. Stupid router.

Ok, so now that you’ve stopped your router from dropping traffic it’s beautiful. Just beautiful. Native dual-stack IPv4 and IPv6. A public IP address for every computer, no NATs3 or firewalls standing in the way. You still get an IPv4 address and can talk to the internet over IPv4 when the other end of a connection doesn’t have IPv6. Next you’re going to want dynamic DNS to be able to refer to your computers by name from anywhere in the world, and be able to get their dynamically configured public IPv6 address. Perhaps I’ll write about that some day. But since it’s been about a year4 since I set up IPv6 and I’ve been meaning to write this the whole time, it might be a while…

  1. Or even worse - putting the two into one box! 

  2. I hate firewalls. Everybody repeats the dogma that they are so important, but most people don’t even know what a firewall is or why it matters. Unless your computer has a severe bug in its network stack, the operating system itself is a firewall — it drops traffic that isn’t headed for a listening port. So unless your computer is running a vulnerable, buggy service you are perfectly safe. And unless your operating system is completely retarded, it is only running services that you told it to (IE if it’s listening on any port, it’s listening because you want to handle traffic coming in there, not have it dropped by a stupid firewall). So unless you know you need to run a service and want it to only be visible on your LAN, you don’t need a firewall. But if you want to run a publicly visible service (for instance, to run peer-to-peer protocols, which our increasingly centralized internet desperately needs more of, or to remotely access your files) you need to punch holes through any firewalls in your way. Any firewall you didn’t set up is more likely there for the purpose of controlling you and how you can use the internet than to protect you. The only reason I would set up a firewall is to run a insecure service in a trusted network and shield it from the public internet. But I would advise people who don’t know how to set up their own firewall to simply not run a service they aren’t willing to have visible to the public (and let’s be honest — useful services are usually the ones that let you connect directly with other people or allow you to access your stuff remotely). Please correct me if I’m wrong, dear internet — if someone knows something I don’t about firewalls and why they really are important I’d like to hear it. But currently I not only believe they are usually not important, but that they are almost always bad

  3. NATs suck precisely because they are super-firewalls. 

  4. I’ve been meaning to write this post for a long time — I hope I didn’t make mistakes or omissions because I waited so long.