Skip to content
 

Quick and dirty IPv6 with 6to4

The first thing to say here is: it's about time that everybody (including you reader) gets IPv6 connectivity. There are no excuses (assuming there were any in the past). IPv6 has been around for more than 14 years now, not two months. It's not really "cutting edge" anymore.

The second thing to say is: the preferred way to get IPv6 is to obtain native connectivity from your service provider. If they are not offering IPv6 yet, start bugging them and asking when they are going to do so.

All that said, here is a quick and dirty way to connect to the the IPv6 Internet that does not need any support from your ISP. You can set it up on your computer, probably even if your router/switch/ISP/whatever know nothing about IPv6. It's quick, and free. Even if you're not interested, it costs nothing, so why not try it? Ok, there are some gotchas (covered in a separate section later), but almost any modern computer or device should be able to deal with them. Specifically, the instructions provided will be for Linux; however, once the idea is understood, it's easy to find instructions for other operating systems.

6to4 tunneling

Ok, 6to4 is nothing new (it's actually almost ten years old, see RFC 3056, 3068 and 3964), and was one of the first transition methods that came out when people started to need IPv6 connectivity in an IPv4-only world. But still, maybe many people are not aware of it, so here it is.

To do 6to4, you need a public IPv4 address. Just one. The idea is that this IPv4 address can be used to generate an IPv6 /48 prefix which is unique. The special IPv6 prefix 2002::/16 would be prepended to the 32-bit IPv4 address. For example, the IPv4 address 192.0.32.10 would become the 6to4 prefix 2002:c000:200a::/48. The c000:200a part is nothing else than our IPv4 address in hexadecimal.

With a /48, one can connect not only his computer but obviously whole networks. In fact, a single IPv6 /48 provides more addresses than the full IPv4 address space.
The original idea of 6to4 was to use this prefix to create an IPv6 "island", ie an entire IPv6 network or set of networks, which would then communicate across the IPv4 Internet with other 6to4 "islands" through a 6to4 gateway.
The communication between islands would happen by tunneling IPv6 packets in IPv4 packets, and this is the job of the 6to4 gateway. Tunneling is automatic: since packets are sent between islands, each packet received by the 6to4 gateway from its local IPv6 island contains, encoded in the destination IPv6 address, the IPv4 address of another 6to4 gateway. So the router has enough information to establish an IPv4 tunnel on the fly and encapsulate IPv6 packets into it. At the other end, the remote 6to4 gateway would then decapsulate the IPv6 packet and send it to its local island. The same process would also allow communication in the opposite direction. In effect, the result is a mesh of dynamic point-to-point tunnels between 6to4 islands.

Internet eventually evolved, and so the need arose for 6to4 islands to connect not only to each other, but to the native IPv6 Internet as well. For this, 6to4 gateways need a way to connect to native IPv6 Internet, either directly or via other 6to4 routers (tunneling IPv6-in-IPv4 to them). A 6to4 router directly connected to the native IPv6 Internet is called a 6to4 relay, because it can, well, relay between the 6to4 and the native IPv6.

The final piece of the puzzle is that there are many 6to4 relays in the world, geographically dispersed. The good news is that if you run a 6to4 gateway, you don't have to know the IPv4 address of your nearest 6to4 relay; a "magic" IPv4 address, 192.88.99.1 has been reserved that corresponds to the "nearest" (in terms of routing) 6to4 relay. This magic (defined in RFC 3068) is nothing else than anycast, whereby the same IP address (actually the whole 192.88.99.0/24 prefix) is originated and advertised from many places; at the same time, 2002::/16 is advertised into the IPv6 routing from those same locations, so traffic from the native IPv6 Internet destined to a 6to4 island is sent to the nearest relay.

So, to sum it up, what we're doing here is to create a 6to4 gateway, and configure it to send all native (ie, non-6to4) traffic to the nearest 6to4 relay, effectively getting access to the whole IPv6 Internet.

Let's go

The following instructions assume Linux, a working installation of iproute2, and that the machine has a public IPv4 address of 192.0.32.10. If a public IPv4 address is not available, or the instructions don't work, see the following section "Gotchas".

So here's the recipe:

 1 # ip tunnel add 6to4 mode sit remote any local 192.0.32.10
 2 # ip link set 6to4 up
 3 # ip addr add 2002:c000:200a::1/16 dev 6to4
 4 # ip -6 route add default via ::192.88.99.1 dev 6to4

(Obviously, replace 192.0.32.10 and the c000:200a part in the IPv6 address with your corresponding values).

That's it. At this point, pinging an IPv6 address, whether native or 6to4, should work:

# ping6 -n -c 2 ipv6.google.com
PING ipv6.google.com(2a00:1450:8006::93) 56 data bytes
64 bytes from 2a00:1450:8006::93: icmp_seq=1 ttl=57 time=7.76 ms
64 bytes from 2a00:1450:8006::93: icmp_seq=2 ttl=57 time=7.68 ms

--- ipv6.google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 7.684/7.726/7.769/0.097 ms

# ping6 -n -c 2 2002:3cea:4272::1
PING 2002:3cea:4272::1(2002:3cea:4272::1) 56 data bytes
64 bytes from 2002:3cea:4272::1: icmp_seq=1 ttl=52 time=307 ms
64 bytes from 2002:3cea:4272::1: icmp_seq=2 ttl=52 time=307 ms

--- 2002:3cea:4272::1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 307.164/307.553/307.942/0.389 ms

The 6to4 address used in the above test is courtesy of this Waikato LUG page.

This SixXS page is a good starting point to find IPv6 sites and services.

Most if not all modern applications and libraries are designed to prefer IPv6 over IPv4 when it is available, so everything should still work as before, except IPv6 will be used when possible. If you want to change your configuration to use a DNS server reachable over IPv6 (in /etc/resolv.conf, for example), this page has a huge list.

Here's what the commands do: step 1 creates the tunnel interface, imaginatively named "6to4", and says that the remote endpoint is "any", since 6to4 tunneling is dynamic, so the remote endpoint will change depending on the connection. The tunnel interface is brought up in step 2, and in step 3 the 6to4 IP address we built using the IPv4 is assigned to it.
At this stage, the host is a fully functional 6to4 gateway, and can already connect to any other 6to4 address in the world (and is reachable from them as well). The 6to4 address assigned to the tunnel interface is a /16, which means that a route to 2002::/16 is created in the routing table, pointing to the 6to4 interface. This is how any other 6to4 destination can be reached: every other 6to4 address is seen as being "directly connected", and the "link layer" is really the IPv4 Internet: when you send a packet to another 6to4 address (ie 2002:anything/16), the kernel will look at the embedded IPv4 address and use it to establish a tunnel into which the IPv6 packet is encapsulated. Tunneling is totally stateless: each single packet is inspected and encapsulated according to the IPv4 addresses embedded in its source and destination 6to4 addresses.

However, this still doesn't provide connectivity to the native IPv6 Internet, and that's what step 4 above does. It establishes a default IPv6 route pointing to ::192.88.99.1, which is how the nearest 6to4 relay is identified. When the host needs to send a non-6to4 IPv6 packet, it uses the default route to encapsulate it into an IPv4 packet to 192.88.99.1 (the nearest 6to4 relay), where it will be decapsulated and sent into the native IPv6 Internet.
As a side note, IPv4-compatible addresses like ::192.88.99.1 have been deprecated for a long time (RFC 4291, year 2006). One would expect, as it happens in other operating systems, to be able to use this instead:

# ip -6 route add default via 2002:c058:6301:: dev 6to4     # does not work under Linux

(or any other address starting with 2002:c058:6301::, for that matter), where 2002:c058:6301:: is the 6to4 encoding of 192.88.99.1. On Linux, that fails because of the way the IPv4 address used for encapsulation is chosen. The kernel explicitly checks that it is an IPv4-compatible address, otherwise it fails. See sit.c, ipip6_tunnel_xmit(). And no, it's not going to change; a patch to change that and allow using the 2002:: format was refused.

Connecting a network

With little effort the above setup can be extended to provide IPv6 connectivity to one or more networks connected to the 6to4 gateway. Since the single public IPv4 address gives us a full IPv6 /48, we can effectively treat that /48 as if it had been assigned to us by a registry, and thus subnet it into a number of (usually) /64 prefixes. For this example, we will use 2002:c000:200a:f00d::/64 as the prefix to advertise to the to-be-connected network, which is assumed to be connected to the gateway's eth1 interface. Install radvd and create a basic configuration file /etc/radvd.conf as follows:

interface eth1 {
    AdvSendAdvert on;
    # set this to advertise a reduced MTU
    # AdvLinkMTU 1480;
    
    prefix 2002:c000:200a:f00d::/64 {
        AdvOnLink on;
        AdvAutonomous on;
    };
};

Assign an address in that range to the gateway interface facing the network, enable forwarding and start the daemon:

gateway # ip -6 addr add 2002:c000:200a:f00d::1/64 dev eth1
gateway # echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
gateway # /etc/init.d/radvd start

Now choose a client and check that it picked up the configuration automatically:

client $ ip -6 addr show dev eth0
3: eth0:  mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:1e:4f:f4:59:3e brd ff:ff:ff:ff:ff:ff
    inet6 2002:c000:200a:f00d:21e:4fff:fef4:593e/64 scope global dynamic 
       valid_lft 2591826sec preferred_lft 604626sec
    inet6 fe80::21e:4fff:fef4:593e/64 scope link 
       valid_lft forever preferred_lft forever
client $ ip -6 route show default
default via fe80::250:56ff:fea7:318 dev eth0  proto kernel  metric 1024  expires 1618sec mtu 1500 advmss 1440 hoplimit 64
client $ ping6 -n -c 1 ipv6.google.com
PING ipv6.google.com(2a00:1450:8006::68) 56 data bytes
64 bytes from 2a00:1450:8006::68: icmp_seq=1 ttl=57 time=24.8 ms

--- ipv6.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 24.894/24.894/24.894/0.000 ms

Gotchas/NAT/etc.

Hopefully the above commands "just worked" for you. But, there are some factors that may negatively affect the outcome, and result in 6to4 not working, or only partially working. Most of these are especially relevant if you're behind a NAT router. Here are the major points to be aware of when using 6to4.

Private IPv4 address

If you don't have a public IPv4 address, then it means you are behind a NAT router; however, 6to4 may still work. 6to4 strictly requires that the embedded IPv4 addresses be public addresses (ie, non-RFC 1918). The reason for this is obvious: if RFC 1918 addresses could be used, routing ambiguity would ensue because the same 6to4 prefixes could be used by different host at different locations (and, probably even more important, most if not all routers on the Internet won't have routes to RFC 1918 addresses anyway, since those routes are forbidden from leaking into the public network). So, IPv4 addresses embedded in 6to4 addresses MUST NOT be private addresses. If you are in this situation, use the NAT router's public IPv4 address to build your 6to4 prefix.

Encapsulation

6to4 encapsulates IPv6 in IPv4; in practice, this means that what hits the 6to4 gateway's upstream router is a packet in which the IPv4 header is followed by an IPv6 header, instead of a more common TCP, UDP or ICMP header. Some devices are confused by this, because they cannot find, in what follows the IPv4 header, port numbers and other information they need to build their stateful connection table. Hopefully, non-historic devices should be able to manage the situation just fine.

IP protocol 41

Due to the encapsulation described above, the "protocol" field in the IPv4 header is not 6 (TCP), 17 (UDP) or 1 (ICMP); it's 41 (IPv6). You may be behind some device that discards such packets (either for firewalling purposes, or because it doesn't recognize them). But again, any modern device should be able to cope, so ensure that IPv4 packets with protocol 41 are not filtered anywhere.

MTU issues

This is not 6to4-specific as it is true of all tunnels, but since encapsulation is involved, the 6to4 tunnel interface has an MTU of 1480. If, rather than just connecting the 6to4 host, you're using it as a 6to4 gateway to connect other computers, this introduces an MTU bottleneck in their path, since the local IPv6 hosts will by default have an MTU of 1500 instead. As such, make sure you're either correctly sending back the appropriate ICMPv6 messages to signal when a packet is too big, and that those hosts correctly receive them, or if the router advertising daemon you're using allows it, advertise the lower MTU to the hosts (with radvd, it's the AdvLinkMTU directive, commented in the sample configuration presented earlier).

Asymmetric routing

This is something that may not cause problems at first, but definitely needs to be understood. Routing of packets between 6to4 addresses and native IPv6 addresses is by its very nature highly asymmetrical.
Consider 6to4 host A talking to native IPv6 host B. Packets from A to B will go from A to the nearest 6to4 relay (or 6to4 gateway and then relay if A is not a gateway), and from there through the native IPv6 network until they reach B. Thus, looking at the lowest layer 3 header, few IPv4 hops (1 or 2) and many IPv6 hops.
Packets from B to A will be sent to B's nearest 6to4 relay, and from there, in a single IPv6 hop (comprised of multiple IPv4 hops), sent directly to A (or to A's 6to4 gateway, just one hop before A, in case A is not a 6to4 gateway itself). Again looking at the lowest layer 3 header, now we have few IPv6 hops, and many IPv4 hops.
This asymmetry is very apparent in traceroutes, so don't be surprised to come across it. However, this can negatively affect stateful IPv4 devices in the path that assume symmetric routing, for two reasons: the first, as said, is that they may only see one half of the traffic; the second is that the IPv4 tunnel addresses can be different for packets travelling in different directions. Simply put, if when A sends to B the encapsulating IPv4 packet has source 192.0.32.10 and destination 192.88.99.1, the return packet from B to A may have source, say, 192.88.99.2 and destination 192.0.32.10. This obviously confuses stateful devices in the path (or even the 6to4 endpoint itself) that see both halves of the traffic but cannot correlate them. Note that this is not just a theoretical risk; it does really happen in the real network. For example, at the time of this writing, communication between 6to4 and the native IPv6 addresses of sites like www.kame.net or www.deepspace6.net exhibits that very behavior at least from some locations.

So, be aware of all these issues when debugging. If you think 6to4 has too many issues....you already know the answer: get native IPv6.

6rd (rapid deployment)

For an ISP wanting to run a relay for its customers, 6to4 has some shortcomings. In 6to4, the advertised IPv6 prefix (2002::/16) and the advertised IPv4 prefix (192.88.99.0/24) are the same everywhere across the world; this means that every 6to4 relay in the world can receive or send any kind of traffic, so in the ISP case, even that of extraneous hosts (ie, non-customers). For the same reason, traffic patterns are unpredictable, and performance, especially for traffic going through busy relays, is sometimes suboptimal. Another problem with 6to4 is that the ISP's customers may not always be able to reach a 6to4 relay, because there may be no route to it from that part of the network. In short, traditional 6to4 makes it difficult for the ISP to organize and control service access and usage.

The most recent development of the 6to4 idea is called 6rd (rapid deployment), and is described in RFC 5569. The ideas behind 6rd are very simple, but have profound implications.

Basically, 6rd is just like 6to4, but with two crucial differences: the IPv6 prefix used is not 2002::/16, but a prefix chosen by the ISP from its own IPv6 address space; and the IPv4 anycast address of the nearest relay is not 192.88.99.1, but an address chosen by the ISP among its own IPv4 addresses (anycast of that address will still be used in case the ISP wants to deploy many geographically distributed 6rd relays).
The IPv6 prefix obviously will be longer than a /16 (no ISP has /16s), but the concept is the same: the 32 bits following that prefix, wherever they are, are the embedded IPv4 address.

These two seemingly innocuous changes are instead very important. They allow an ISP to effectively control their own IPv6 traffic. With a unique prefix to advertise to the IPv6 Internet for tunneled traffic, and a unique IPv4 address for the relay, the shortcomings of 6to4 can be tackled in an effective way. With 6rd, tunneled traffic from/to the ISP's customers "naturally" flows through the ISP's 6rd relays due to the uniqueness of the advertised prefixes/addresses, and this in turn allows the ISP to do accurate management, monitoring, accounting and planning of the IPv6 traffic and infrastructure. All of this, at just the price of deploying a bunch of what are essentially modified 6to4 relays (rather than a more complete full IPv6 infrastructure). Also, from the point of view of the larger Internet, 6rd traffic is not special; it just looks like normal IPv6 traffic from/to the ISP. Overall, 6rd should solve some or many of the asymmetric routing issues described previously.

Linux supports 6rd since kernel 2.6.33.