IPv6’ing my flat

For a long time now, I’ve been interested in IPv6 – the next generation protocol for the internet. All modern operating systems now support it, as do a large proportion of mainstream services and programs, but no-one actually uses it. During my recent network refurb, I decided to make my entire house IPv6-capable. Because I’m sad like that.

The first step was to look at how I could talk IPv6 to the world at large. Only after that was determined could I architect my internal network so it would a) work, and b) be secure.

Unfortunately very few ISPs in the UK support IPv6 natively. Be Broadband, my ISP, are vague about timescales and scope. So, the only option was to tunnel the IPv6 over IPv4 to an endpoint on the internet, at which point it would break out and be all good.

There are several companies that offer this service, “tunnel brokering” for free – I have had positive experiences with both Hurricane Electric (http://tunnelbroker.net/) and sixxs (http://www.sixxs.net/). This time, I decided to go for HE.

Step 1, register with HE and get a tunnel created

As I have a segmented network internally, I opted for a /48 network allocation. What this means is that I can create 65536 internal networks, each with 2^64 (a very big number) hosts. That should be enough :)

Step 2, create a tunnel endpoint

What we’re going to be doing is routing all IPv6 data on my network through this one host, which will wrap it with IPv4 and send it off to the HE tunnel endpoint. At this point the IPv4 wrapper will be removed, and IPv6 will pop out. Note that this tunnel will completely circumvent any NAT or IPv4 Firewalls! Which we’ll come back to.

So, to create the tunnel endpoint, I created a new VM on my server, and bound that VM to my spare ‘external’ NIC. This then plugs into a PIX, which plugs into the ASA on my network boundary, and then out. Linux was the obvious choice, and more accurately I’m using a Gentoo install with a whole 128MB of RAM and 1GB HDD assigned to it.

When registering for a tunnel with HE, it gives you sample configurations for all sorts of OSs. For me, it was the below (addresses changed to protect the innocent):

modprobe ipv6
ip tunnel add he-ipv6 mode sit remote local 188.220.xx.xx ttl 255
ip link set he-ipv6 up
ip addr add 2001:470:yyyy:yyyy::2/64 dev he-ipv6
ip route add ::/0 dev he-ipv6
ip -f inet6 addr

Then (lets ignore the firewalls for a minute) it was just a case of pinging some IP addresses and I was sorted. Or I should have been. After about 30 mins of troubleshooting I found I could ping me, my tunnel endpoint, the remote tunnel endpoint, but not any farther. I fired off an email to HE asking for support, and within 5 minutes they had got back to me. Apparently there had been an error in a script and it was all fixed. This is customer service I can massively respect, and big kudos to HE for the timely response!

Okay, I now had an IPv6 connection, from my tunnel endpoint.

Step 3, the internet-facing firewalls

There are a PIX and an ASA between my endpoint and the internet, and each of these needed some tweaks to allow the traffic through. Specifically, I wanted only in/outbound IP protocol 41 (i.e. IPv6 over IPv4), and only between my external IP (I actually used a second static IP) and the remote tunnel endpoint.

These looked like:

access-list external_access_out extended permit ip any any
access-list outside_access_in extended permit 41 host host outside_ip3
static (external,outside) outside_ip3 netmask
access-group outside_access_in in interface outside
access-group outside_access_out out interface outside
route external 1

IPv6 is fully blocked from entering/exitting the network other than via this one tunnel.

Step 4, using the endpoint as a router

Firstly, we want to tell the router to forward IPv6 data between interfaces, but not IPv4. On my linux box, this was the following (note these settings aren’t persistent, you’ll need to edit the correct config files):

sysctl -w net.ipv6.conf.all.forwarding=1
sysctl -w net.ipv4.ip_forward=0

Step 5, some additional security

As the tunnel endpoint is essentially sitting unprotected on the internet, it must be deemed at risk. I have done the standard stuff – minimal services, strong passwords, patching etc, but what about the rest of the network. Ideally I could route the IPv6 data through my PIX, onto the network, and use the PIX for firewalling. In theory. Unfortunately Cisco suck, and the PIX cannot support IPv6. So, back to my trusty VMWare server. I create a new virtual network on the server, add an extra interface to the endpoint box, bind it to the new virtual network,  spin up another VM with two nics, and connect one of these to the virtual network and the other bridged onto the internal network via the server’s primary NIC. After enabling IPv6 only routing, installing iptables/ip6tables, I now have a linux-based firewall between the tunnel endpoint and my internal network.

So, how to configure it. Well, see below…

#NOTE: eth4=external (i.e. towards v6 endpoint), eth5=internal
# allow v4 access from the internal net, but not the external
#used for ssh, syslog, etc
iptables -P INPUT ACCEPT
iptables -A INPUT -i eth4 -j DROP

#allow anything from the inside out, but only established/related back
#also need to allow neighbour solicitation/advertisment, and I allow echo request/reply and destination unreachable
ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 136 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 135 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 1 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 128 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 129 -j ACCEPT
ip6tables -A INPUT -i eth4 -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -i eth5 -j ACCEPT
ip6tables -A INPUT -j LOG --log-prefix "FW IN "
ip6tables -A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 1 -j ACCEPT
ip6tables -A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 128 -j ACCEPT
ip6tables -A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 129 -j ACCEPT
ip6tables -A FORWARD  -i eth4 -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -A FORWARD -i lo -j ACCEPT
ip6tables -A FORWARD -i eth5 -j ACCEPT
ip6tables -A FORWARD -j LOG --log-prefix "FW FWD "

You may be wondering what all the ICMPv6 is about. Unlike IPv4, IPv6 doesn’t use ARP for neighbour discovery. Instead, ICMPv6 is used. Without allowing these packets through, address resolution doesn’t work, and hence your neither does your network. So that’s 135/136. 128/129 are for pinging, and 1 is destination unreachable.

Step 6, internal addressing

Okay, we now have a tunnel endpoint on the network, which routes data happily and securely, and I can route data to it from my internal network. Just one problem, in order to do so, I have to statically configure the IPv6 address. Which sucks. Sooooo, what about autoconfiguration?

In IPv6-world, there are two ways to do autoconfiguration: stateful and stateless. Stateful is basically DHCP. Stateless is something different though, and is much easier to implement and use. Whilst I do use DHCP internally for some hosts, I may want to autoconfigure a different subset of hosts for IPv6. So, stateless it is…

On linux, IPv6 autoconfiguration is done through the radvd program. It’s really easy to set up and use – see the config below… Basically the radvd (router advertisement daemon) will periodically send out “Router Advertisements” (another ICMPv6 type) saying “this is what this network is called”. Hosts then take the top 64 bits, concatenate their MAC address (or a random 64 bits if privacy extensions are in use), and there you have a valid IPv6 address.

interface eth0
    AdvSendAdvert on;
    AdvLinkMTU      1350;
    MaxRtrAdvInterval  300;

    prefix 2001:470:yyyy:xxxx::/64
        AdvOnLink on;
        AdvAutonomous on;
    route 2001:470:yyyy:yyyy::/64 {};
    route ::/0 {};

Step 7, services

The network is now almost there. And in fact, it completely was there I just didn’t realise it. Any host in the network now picks up an IPv6 address, routes it through the linux firewall and the tunnel endpoint, and thence through the magic of IPv6 over IPv4 tunnels onto the internet. Cool eh. Unfortunately we don’t all talk in numbers, and certainly not 128-bit numbers, and so we use DNS.

Which just worked. Seriously, I was pretty impressed. I had bind 9.4.3 running as a caching resolving DNS server for my internal network, and it automagically just worked for IPv6. For those who care, it was installed on Gentoo with the following use flags: “berkdb ipv6 ldap mysql ssl threads xml -dlz -doc -geoip -gssapi -idn -odbc -postgres -resolvconf (-selinux) -urandom”

And there we are – I now have native IPv6 in a dual-stack in my entire network, and it works. In fact, sometimes when I browse to websites (using Chrome) I apparently use IPv6 without my even knowing. And how cool is that?!!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s