ViewRevisions

Easy IPv6 with 6to4

Do you know? It seems to me that one of the obstacles which bar the development of the Internet as a real distributed network are the objections that access providers pose to offering public, static IP addresses to their home clients, who must deal with dynamic addresses, private networks and NAT. A pity, since making everyone's machines reachable via the Internet would allow anyone to offer their own services, empowering individuals and levelling their position against big content providers, as it happens right now in free (as in freedom), neutral networks like guifi.net. In contrast with mass media, the Internet doesn't tell emitters apart from receivers.

IPv4 address scarcity is a (very debatable) reason wielded by providers for keeping the current statu quo. IPv6, for a change, offers real address abundance for everyone and, though it may seem, it's already usable whatever our way of accessing the Internet is. Moreover, connecting to the Internet of the future is quite fun, so let's jump in!

To move one step at a time, this article describes how to provide a stable IPv6 connection to a Debian machine having a fixed IPv4 address, using 6to4, with no need for a tunnel broker subscription and performing a really minimal configuration.

6to4 configuration

In the first place, we need to make sure that we already have access to a 6to4 router through the anycast address 192.88.99.1:

$ ping -c 4 192.88.99.1
PING 192.88.99.1 (192.88.99.1) 56(84) bytes of data.
64 bytes from 192.88.99.1: icmp_seq=1 ttl=249 time=2.00 ms
64 bytes from 192.88.99.1: icmp_seq=2 ttl=249 time=2.09 ms
64 bytes from 192.88.99.1: icmp_seq=3 ttl=249 time=1.85 ms
64 bytes from 192.88.99.1: icmp_seq=4 ttl=249 time=2.04 ms

--- 192.88.99.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3000ms
rtt min/avg/max/mdev = 1.854/1.996/2.091/0.099 ms

We can also run mtr 192.88.99.1 to check how far away the router is. Of course, the nearer the better.

To create a 6to4 configuration for the machine's IPv4 public address, the simplest way is to use the IPv6 6to4 config generator for Debian. For instance, running the generator on the IPv4 address 1.2.3.4 we'd get the following fragment:

# IPv6 using a 6to4 tunnel to the internet (from 1.2.3.4)
# Generated at http://debian6to4.gielen.name/
auto tun6to4
iface tun6to4 inet6 v4tunnel
        pre-up modprobe ipv6
        address 2002:0102:0304::1
        netmask 16
        gateway ::192.88.99.1
        endpoint any
        local 1.2.3.4

# If you have an IPv6-capable firewall (and you really should have one),
# it can be enabled by using an "up" rule, such as the example below.
#       up /usr/local/sbin/ipv6firewall.sh tun6to4

We simply include the previous fragment in /etc/network/interfaces and bring the interface up with:

# ifup tun6to4

We can check the new IPv6 address by pinging some machine in the IPv6 network, for instance:

# ping6 -I tun6to4 -c 4 www.whatismyv6.com
PING www.whatismyv6.com(www.whatismyv6.com) from 2002:102:304::1 tun6to4: 56 data bytes
64 bytes from www.whatismyv6.com: icmp_seq=1 ttl=59 time=9.10 ms
64 bytes from www.whatismyv6.com: icmp_seq=2 ttl=59 time=10.3 ms
64 bytes from www.whatismyv6.com: icmp_seq=3 ttl=59 time=10.3 ms
64 bytes from www.whatismyv6.com: icmp_seq=4 ttl=59 time=10.1 ms

--- www.whatismyv6.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 9.107/9.980/10.333/0.518 ms

If this didn't work and the machine runs a firewall, we may need to tell it to let IPv4 packets with protocol IPv6 pass, with the following command or the equivalent one in our firewall configuration system:

# iptables -t filter -A INPUT -p ipv6 -j ACCEPT

Once pings work, we can check web access via IPv6. The simplest way to do it is visiting www.whatismyv6.com using w3m:

$ w3m -6 www.whatismyv6.com

If the page displays an IPv6 source address, we're done. Yes, really, that's it. Welcome to the wonderful world of IPv6!

Additional addresses

Well, we already have our IPv6 address, but we really have much more than that: 6to4 allocates 216 subnets of 264 IPv6 addresses (that's one quadrillion (1024) addresses, now that's total abundance!) for each IPv4 address to let us build our own IPv6 network. Moreover, assigning additional IPv6 addresses to our machine is as easy as adding one up and one down option per extra address to the previous tun6to4 configuration block. For instance:

up   ip -6 addr add 2002:0102:0304::2/16 dev $IFACE preferred_lft 0
down ip -6 addr del 2002:0102:0304::2/16 dev $IFACE

The option preferred_lft 0 avoids using the last added address as the default source address for outgoing connections. See this article for a more detailed description of this mechanism (thanks Axel!).

If our machine had several IPv4 public addresses, we could add a similar tun6to4 block for each one of them. However, a single one provides us with plenty of addresses, and we thus simplify network configuration while keeping things easier if we later want to set up reverse name resolution.

If you want to know how to set up an IPv6 network behind your machine, please have a look at this section of Madduck's guide to IPv6.

Firewall configuration

Now that the machine is publicly accessible via IPv6, it's recommended to set up a firewall with ip6tables (e.g. using local-iptables). Rules should be similar to IPv4 ones. For instance, to allow local traffic, incoming SSH and SMTP connections, and outgoing connections:

# ip6tables -t filter -N in-new
# ip6tables -t filter -A INPUT -i lo -j ACCEPT
# ip6tables -t filter -A INPUT -p ipv6-icmp -j ACCEPT
# ip6tables -t filter -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# ip6tables -t filter -A INPUT -m state --state INVALID -j DROP
# ip6tables -t filter -A INPUT -m state --state NEW -j in-new
# ip6tables -t filter -A in-new -p tcp -m tcp ! --syn -j RETURN
# ip6tables -t filter -A in-new -p tcp -m tcp --dport 22 -j ACCEPT
# ip6tables -t filter -A in-new -p tcp -m tcp --dport 25 -j ACCEPT
# ip6tables -t filter -A INPUT -m limit --limit 3/min --limit-burst 10 \
            -j LOG --log-prefix "[INPUT6]: "
# ip6tables -t filter -A INPUT -j REJECT

Versions prior to 2.6.20 of the Linux kernel fail to handle connection tracking properly. The following configuration is similar to the previous one, but doesn't take connection state into consideration (unfortunately, it neither allows UDP traffic over IPv6 without explicit rules):

# ip6tables -t filter -A INPUT -i lo -j ACCEPT
# ip6tables -t filter -A INPUT -p ipv6-icmp -j ACCEPT
# ip6tables -t filter -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
# ip6tables -t filter -A INPUT -p tcp -m tcp --dport 25 -j ACCEPT
# ip6tables -t filter -A INPUT -p tcp -m tcp --dport 1024:65535 \
            ! --syn -j ACCEPT
# ip6tables -t filter -A INPUT -m limit --limit 3/min --limit-burst 10 \
            -j LOG --log-prefix "[INPUT6]: "
# ip6tables -t filter -A INPUT -j REJECT

Name resolution

IPv6 addresses are hard-to-remember, lengthy numbers. If we want to associate names to addresses using DNS, we'll have to include new AAAA-type entries in the proper name servers. In the previous example, the following entry would assign the name test6to4 to the IPv6 address:

test6to4  IN  AAAA  2002:102:304::1

Once the changes to DNS are done, we can use dig (dnsutils package) to check that direct resolution works as expected in our DNS server. In our example:

$ dig -t AAAA test6to4.example.net @SERVIDOR_DE_DNS +short
2002:102:304::1

If we want reverse resolution to work, we'll have to suffer a little more, since we need a DNS server where we can create new domains, and we need to request that the resolution of IPv6 addresses starting with the prefix associated with our IPv4 address is delegated to us. Fortunately, this last step is automatic.

Here we're trying to make the IPv6 address in our example, which is fully written as:

2002:0102:0304:0000:0000:0000:0000:0001

or in DNS terms (take breath):

1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.0.3.0.2.0.1.0.2.0.0.2.ip6.arpa

(that is, the complete address reversed, with dot-separated digits) be resolved to a name like test6to4.example.net.

We'll start by visiting 6to4.nro.net using IPv6 from the very machine we're configuring:

$ w3m -6 http://6to4.nro.net/

The domain displayed as reverse will be the one that we'll create in our DNS server (its installation and configuration is left as an exercise to the reader, but this page can be useful). In our example, the reverse domain would be 4.0.3.0.2.0.1.0.2.0.0.2.ip6.arpa, and the full zone file would be similar to this one:

@  IN  SOA  4.0.3.0.2.0.1.0.2.0.0.2.ip6.arpa.  hostmaster.example.net.  (
    2009122102  ; Serial number (YYYYMMDDnn)
    3h          ; Refresh time
    1h          ; Retry time
    7d          ; Expire time
    3h          ; Default TTL
)

; Nameservers
    IN  NS  ns1.example.net.
    IN  NS  ns2.example.net.

; IPv6 pointers
$ORIGIN  4.0.3.0.2.0.1.0.2.0.0.2.ip6.arpa.

1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0  IN  PTR  test6to4.example.net.

Once the changes to DNS are done, we use dig (dnsutils package) to check that reverse resolution works as expected on our DNS server. In our example:

$ dig -x 2002:102:304::1 @DNS_SERVER +short
test6to4.example.net.

To end it all, we shall request delegation by visiting 6to4.nro.net using IPv6 from the very machine we're configuring:

$ w3m -6 http://6to4.nro.net/

We enter into the form the administrator's e-mail address (hostmaster@example.net) and the names of at least two name servers (ns1.example.net and ns2.example.net) for the reverse domain, a password to keep anyone in our machine from changing the data, and press Submit. That's it.

When changes in DNS have propagated (which may take hours), you should see the right reverse resolution e.g. in the output of ping6 (between parenthesis):

$ ping6 -c 4 test6to4.example.net
PING test6to4.example.net(test6to4.example.net) 56 data bytes
64 bytes from 2002:102:304::1 icmp_seq=1 ttl=64 time=0.033 ms
64 bytes from 2002:102:304::1 icmp_seq=2 ttl=64 time=0.053 ms
64 bytes from 2002:102:304::1 icmp_seq=3 ttl=64 time=0.052 ms
64 bytes from 2002:102:304::1 icmp_seq=4 ttl=64 time=0.049 ms

--- test6to4.example.net ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 2999ms
rtt min/avg/max/mdev = 0.033/0.046/0.053/0.011 ms

More, more, more!

Now that we know how to equip our server with a fully fledged IPv6 connection, it'd be nice to set up our home computer to access the Internet via IPv6. In some future post I'll describe how to give IPv6 connectivity to a machine behind a NAT-performing router with almost no headaches, using Teredo. However, this provides a temporary IPv6 address which may change to the will of our access provider or router.

So, the next step would be to configure our home network to form an IPv6 network which is fully accessible from the outside, e.g. usingSixXS tunnel broker. Fortunately, our pal Madduck has already described the full procedure with full detail in his page about IPv6 with Debian. There's even a howto on setting up an IPv6 tunnel and network using OpenWRT, though there seem to be some problems with processor overload. I'll tell you if I make up my mind.

See you on the next IPv6 trip!