Markus Wernig

UNIX/Network Security Engineer
CCSA, CCSE
CISSP


PGP key transition note
GPG Key
 (in use after Aug. 9 2013)

old GPG Key
 (in use up to Aug. 9 2013)

Personal | Professional | IT related | Writings | Life de

How to set up IPv6 connectivity natively over an IPv4 IPSec tunnel

or

Set up your own 6in4 tunnel broker with Free Software

Abstract

This article shows how to configure an IPv4 IPSec tunnel, through which IPv6 packets can be forwarded. It describes how to configure the client and how to set up the tunnel broker, using a sample network layout. The purpose is to enable IPv6-capable hosts ("clients"), that are isolated in an IPv4-only network, to connect to the IPv6 internet.

Note: This is a modification of an earlier article which described a similar setup, in which a GRE tunnel was set up within the IPSec tunnel. I had not realized that IPSec was capable of natively encapsulating IPv6 packets. Thanks to Ryan Slack for pointing this out to me. Also I had not been aware of the fact that iked was able of assigning IPv6 addresses to the client via IKEv2.

Table of contents

  1. Introduction
  2. Problem description
  3. Solution approach
  4. Systems used
  5. Prerequisites
        For the client
        For the tunnel endpoint
  6. Sample network layout
  7. Setup
        The tunnel broker
        The client
  8. Start
        The IPSec tunnel
  9. Final steps
  10. Links

Introduction

With IPv6 slowly gaining more widespread application, many sites on the internet now offer their services over both IP protocols: IPv4 (the traditional way) and IPv6. This is possible because most (all?) internet carriers nowadays allow both protocols to be routed over their networks, so that, basically, the whole infrastructure of the internet is a multi-protocol transport layer, over which almost any protocol can be forwarded. Whereever segments exist over which a certain protocol cannot be transmitted, usually the edge routers of that segment make sure that the "offending" network packets are encapsulated within packets conforming with that particular segment's topology when entering the segment, and decapsulated again when leaving it.

I will not go into the specifics of packet encapsulation (also called "tunneling") here. It basically works by prepending a header and appending a trailer that contain routing/addressing information that is meaningful on the given segment, if the header/trailer of the original packet does not contain such information. The original packet usually is not modified, but merely embedded within the new packet. This additional information allows the new packet to traverse the network segment and is removed again once it reaches the segment's boundary (edge router). (See here for a brief introduction.)

You can think of it in an analogy: A Chinese businessman abroad in Europe wants to send a letter home to his family, who lives in rural China. Unfortunately, the post offices in Europe can only handle letters that have the addressee's name and address written in Latin characters, while the Chinese post office can only handle letters with addresses written in Chinese characters. So one solution would be to put the letter in an envelope that carries the address in Chinese characters, and then put that envelope into a second envelope that carries the address in Latin characters. On the second envelope he'd also write, in Chinese, "Remove this envelope when this letter enters China." So the European post office will understand the address on the outer envelope and put the letter on a plane to China, where the Chinese post office will remove the outer envelope and send the original letter onwards in the inner envelope. This is, more or less, how encapsulation works.

The problem:

Above I said that "the whole infrastructure of the internet is a multi-protocol transport layer". Well, unfortunately not all of it, not yet. While data centers and carrier networks are all well connected, most small office and home internet connections are still limited to the use of one protocol only: the traditional IPv4. Besides missing out on a technology that has been in active use on the internet for well over 10 years, this is increasingly causing problems for home customers. One reason is, paradoxically, that many home and office computers are absolutely capable of doing IPv6 (all modern operating systems support it natively). Now if a site is available over both IPv4 and IPv6, most computers will first try IPv6, and switch back to IPv4 only if that fails. In many systems that means waiting until the IPv6 attempt creates a timeout, which noticeably slows down every connection attempt. Some operating systems have implemented workarounds to deal with that situation, but that's all they are: workarounds... And that's just one of many possible problems.


Network Schema
Fig. 1: Schematic illustration of the problem: The client can't connect to any IPv6 Server on the internet, because IPv6 traffic (red) is not possible directly over the ISP's network and routers. Only IPv4 traffic (blue) is transported. Any IPv6 connection attempt will fail and may create noticeable delays before switching to IPv4, depending on the client's operating system.


The solution:

One possible solution is to use encapsulation (tunneling). This is what we'll be looking at in the rest of this article. Starting at the home computer (which must, of course, be cabable of doing IPv6), we'll encapsulate our IPv6 packets in IPv4 "envelopes" and send them to a router that is connected to a IPv6 capable network. As the "envelope protocol" we will use IPSec (ESP), which also makes sure the tunnel is authenticated and encrypted. The router will decapsulate the packets ("remove the envelope") and forward our original IPv6 packets to the internet.


Network Schema of solution
Fig. 2: Schematic illustration of the solution: The client establishes an IPSec tunnel with the tunnel endpoint over IPv4 (blue) and receives a "virtual" IPv6 address from the gateway. This IPSec tunnel is used to encapsulate all IPv6 traffic (red) from the client. The tunnel endpoint terminates (removes) the tunnel and sends on the client's IPv6 traffic to any IPv6 Server on the internet. From the ISP's point of view, still only IPv4 traffic (blue) is transported. IPv4 Servers on the internet are still reached via the "old" route over the ISP directly.


There are various ways to do this. If you search for "6in4 tunnel" or "6to4 tunnel" on the internet, you will find quite a lot of possibilities. Most of them assume that you have control over the home computer, but not over the router on the other side of the tunnel, who does the actual forwarding. Those routers are usually operated by networking companies, with whom you need to set up an agreement beforehand and who will then give you the access and configuration details. As of this writing, most of these companies (so called "tunnel brokers") offer their services for free (albeit with no guarantee that this remains free in the future). Currently, Hurricane Electric and SixXS are the most popular services. Both use different technologies to achieve their goal.

Of course, if you don't have a remote machine that is connected to the IPv6 internet and that you can configure as your tunnel endpoint, a public tunnel broker is most likely the way you need to go. Even if it means routing large portions of your internet traffic via the data centers of an US company (in the case of Hurricane Electric), which, in the wake of the NSA/Snowden case, leaves a bit of a bad taste in the mouth.

Now, given that I like to have control over my internet traffic and that I do have control over machines that are connected to the IPv6 internet, I decided to give it a try myself. As an IT engineer with a strong Free Software background, it was natural to try this with software tools that are freely availably and open source. Here's what I've used for this setup:

Systems used:

Home computer:

  • Gentoo Linux ~amd64
  • Kernel 3.16.0
  • StrongSwan
  • OpenSSL
Tunnel endpoint (router)
  • OpenBSD 5.6-current amd64
  • iked
  • LibreSSL
That's just my setup. Any other combination of Unix-like systems should be possible, but the configuration will vary widely from that presented here.

Prerequisites

  • For the home computer:
    • IPv6 and IPv4 dual-stack capability.
      Most modern Linux distributions should be fine. If compiling your own kernel, make sure at least the following options are set:

      CONFIG_NET=y
      CONFIG_INET=y
      CONFIG_IPV6=y

    • IPSec and IKEv2 capability.
      On Gentoo, I installed and used the strongswan package. On the kernel side, you will need at least these options set:

      CONFIG_INET_ESP=y
      CONFIG_INET_AH=y
      CONFIG_INET_IPCOMP=y
      CONFIG_NET_IPIP=y

    • We need IKEv2 to be able to dynamically assign a "virtual" IP address to the client (home computer).

    • A X.509 certificate (with corresponding private key and CA chain) for the home computer or connecting user. It must contain an e-mail address or a FQDN as Subject Alternative Name (both may, but should not be, fictious). In this article we assume that the client certificate has a Subject Alternative Name of email:client@my.domain.

    • An internet connection (IPv4) that can hide NAT (masquerade) and forward UDP packets (ports 500 and 4500) statefully (most home/WLAN routers shipped by ISPs can).

  • For the tunnel endpoint:
    • An IPv4/IPv6 dual-stack network segment (ie. a network than can transport both protocols), that has access to the IPv6 internet. In my case this is a subnet at my colocation provider. The IPv4 network need not be publicly reachable (in my case it is behind a NAT gateway firewall).

    • The possibility to install a dual-stack system (the tunnel endpoint) into that subnet (I used a kvm/qemu virtual machine on an existing host.)

    • At least two free (unused) IPv6 addresses that can be routed to the tunnel endpoint from the internet. (Those addresses could be from the same IPv6 subnet as the one that already exists in the dual-stack network segment, but this makes the routing setup a little more difficult. I've used a dedicated /120 IPv6 subnet from the /48 range assigned to me by my provider. That way I can later add different clients and assign a dedicated IPv6 address to each.) They will serve as client IPv6 address and client IPv6 default gateway, respectively.

    • Tunnel endpoint computer that is capable of IPSec and IKEv2
      Vanilla OpenBSD 4.8 or later fits the bill. I used 5.6-current, since much improvement has happened on the iked daemon since 4.8. (Note that -current releases in OpenBSD mean the current development branch. You might want to stick with the stable release instead, which is one minor number lower than -current, ie. 5.5 as of this writing.)

    • A X.509 certificate (with corresponding private key and CA chain) for the tunnel endpoint computer. It must contain the FQDN of the tunnel endpoint (ie. the hostname that the client connects to) as Subject Alternative Name. In this article we assume that the tunnel endpoint certificate has a Subject Alternative Name of DNS:tunnel.my.domain.

    • An internet connection (IPv4) that can NAT (static) and forward IPv4 UDP packets (ports 500 and 4500) statefully (most routers and firewalls can. In my case, the firewall at the colocation is also OpenBSD) as well as forward IPv6 traffic.

Network layout

The network addresses used in this article are only examples. You'll need to adjust them for your setup.

  • Home network
    IPv4 IPv6
    Network 192.168.0.0/24 N/A
    Home computer 192.168.0.2 N/A
    Default gateway (ISP/WLAN router) 192.168.0.1 N/A

  • Colocation network (dual-stack)
    IPv4 IPv6
    Network 192.168.100.0/24 2a00::0/120
    Tunnel endpoint computer 192.168.100.2 2a00::2
    Default gateway (firewall) 192.168.100.1 2a00::1

  • Dedicated network for IPv6 client connectivity
    IPv4 IPv6
    Network N/A 2a00::100/120
    Home computer N/A 2a00::101
    Tunnel endpoint computer N/A N/A
    Default gateway N/A N/A

  • Public IPv4 addresses
    IPv4 IPv6
    1) Hide NAT (home computer) 1.2.3.4 N/A
    2) Static NAT (tunnel endpoint) 5.6.7.8 N/A

  • 1) This is the public IPv4 address of the home ISP/WLAN router, with which usually all computers in the home network are "visible" on the internet. It is assigned by the ISP.
    2) This is the public IPv4 address at which the tunnel endpoint can be reached. In my case, this IP is routed to an outer firewall, which then translates ("NATs") all packets coming to that address on UDP ports 500 and 4500 into the tunnel endpoint's "private" address (192.168.100.2) and vice-versa for outgoing/reply packets.

  • DNS (optional)
    As I run my own DNS servers, I used my domains to set up the hostname-to-IP translations. In this article, we will use "my.domain" as external DNS domain and "my.dmz" as internal (colocation) domain.
    IPv4 IPv6
    tunnel.my.domain 5.6.7.8 N/A
    tunnel.my.dmz 192.168.100.2 2a00::2
    client.my.domain N/A 2a00::101
    client.my.dmz N/A 2a00::101

  • Certificates
    We will be using X.509 certificates for mutual authentication of client and gateway when setting up the IPSec tunnel. All certificates need to be complete with private/public key pair and CA chain. The following list shows the assumed file names and on which systems they need to be present. (The file names are used below in the the example commands and are merely an example.)
    Description File name Tunnel
    endpoint
    Home
    computer
    Gateway private key endpoint.key yes no
    Gateway public key local.pub yes no
    Gateway X.509 certificate endpoint.pem yes no
    Gateway X.509 CA chain endpoint-cachain.pem yes yes
    Client private key client.key no yes
    Client X.509 certificate client.pem no yes
    Client X.509 CA chain client-cachain.pem yes yes

The setup

Done with the preliminaries, we can now proceed to bring this all together and set up our tunnel.

Set up the tunnel endpoint (gateway)

I installed the gateway on a kvm/qemu virtual machine with a bridged interface that connects to the dual-stack colocation network.
I used OpenBSD 5.6-current amd64 and followed the default install procedure.
I deselected all X related packages as well as game and compiler packages (-x*, -game*, -comp*)

The networking setup I chose during install resulted in the following files:

/etc/hostname.vio0 3)

inet 192.168.100.2 255.255.255.0
inet6 2a00::2

3) The name "vio0" is derived automatically by OpenBSD from the type of interface hardware present. In my case, I had told kvm/qemu to emulate a virtio interface. If you use a different hardware emulation or install directly on a physical machine, this name will vary (see hostname.if(5) and ifconfig(8))

/etc/mygate 4)

192.168.100.1
2a00::1

4) This file configures the default gateways for IPv4 and IPv6. The firewall connected to the dual-stack colocation network has these two addresses.

Note: I am only giving the configuration files here. Most of the configuration can also be done via commands (ifconfig, route, ikectl etc.), so you might want to check out the (very comprehensive) OpenBSD manual pages.
Throughout the following it is assumed that you are logged in as root.

Next, I configured the necessary networking options in /etc/sysctl.conf (uncommented or added the following lines):

/etc/sysctl.conf

net.inet.ip.forwarding=1 # enable IPv4 forwarding
net.inet6.ip6.forwarding=1 # enable IPv6 forwarding
net.inet.ipcomp.enable=1 # enable IP compression

Next, we need to set up IPSec. We will be using X.509 certificates for mutual authentication (this is not strictly necessary, but since I also have my own X.509 PKI, I find this easier.)

Copy the tunnel endpoint's RSA private key (PEM or DER) to /etc/iked/private and set permissions:

cp endpoint.key /etc/iked/private/local.key
chmod 600 /etc/iked/private/local.key

Extract the tunnel endpoint's RSA public key to /etc/iked/local.pub:

openssl rsa -in /etc/iked/private/local.key -pubout -out /etc/iked/local.pub

Copy the tunnel endpoint's X.509 certificate to /etc/iked/certs:

cp endpoint.pem /etc/iked/certs/

Copy the tunnel endpoint's X.509 CA chain to /etc/iked/ca:

cp endpoint-cachain.pem /etc/iked/ca/

Copy the home computer's X.509 CA chain to /etc/iked/ca:

cp client-cachain.pem /etc/iked/ca/ 5)

5) Obviously, if the client's and endpoint's certificates have the same CA chain, you only need to copy it once.

Then, we need to configure iked, OpenBSD's Internet Key Exchange Daemon, which is capable of doing IKEv2 key negotiations and network setup (see iked(8) and iked.conf(5)).
We set up an IKEv2 key exchange here, that will result in the client (home computer) receiving a "virtual" IPv6 address within the tunnel to use for connecting to the internet. (This is similar in function to assigning a dynamic address via DHCP in IPv4.)
Note: If you don't want to or can't use IKEv2, you'll need isakmpd (see isakmpd(8)), which does IKEv1. But then much of the rest of this article will not apply to you.

/etc/iked.conf

# set up tunnel endpoint for road warrior to connect
ikev2 clientvpn \ 6)
  quick passive ipcomp esp inet \
  from ::/0 to 2a00::100/120 \ 7)
  peer 0.0.0.0/0 local 192.168.100.2 \ 7)
  srcid tunnel.my.domain dstid client@my.domain \
  config address 2a00::101 \
  tag ipsec-$id

6) "clientvpn" is just an arbitrary identifier string, you can use anything here.
7) According to iked.conf(5) it should read "any" instead of "0.0.0.0/0" and "::/0", but as of this writing, it is necessary to use "0.0.0.0/0" and "::/0", else iked will refuse to establish the connection and log an error along the lines of:
iked: pfkey_flow: unsupported address family 0
iked: ikev2_childsa_enable: failed to load flow
iked: ikev2_dispatch_cert: failed to send ike auth

Finally, we enable iked to be started on system startup, and we tell it to log much debug information to /var/log/daemon:

/etc/rc.conf.local

# override default settings from /etc/rc.conf
pf=NO # we assume that we already are behind a firewall, so not running a local one
iked_flags="-vvv -6" 8) # maybe remove "-vvv" once the setup is stable
sndiod_flags=NO # no sound needed
sendmail_flags=NO # don't want sendmail as daemon
inetd_flags=NO # don't want inetd

8) If we don't use the "-6" option for iked, it tells the kernel to silently drop any IPv6 connections (and neighbor solicitations etc.). This breaks our setup, and I personally consider it a bug, even if it is shortly explained in iked(8).

With this, the gateway setup is complete. You can now restart all affected services manually or (as I prefer) simply reboot.

Set up the client (home) computer

The client setup - as the gateway setup - is fairly straight-forward. First (and given that your kernel is prepared for IPSec as shown above) you need to install the strongswan package. On Gentoo, this is done by running

emerge strongswan

Make sure to carefully read the output and update any old configuration files (eg. from openswan or libreswan).
If you emerge strongswan with the non-root USE flag (the default), make sure all following files and directories are readable by the ipsec user.

Next, we need to configure StrongSwan IPSec.

Copy the home computer's RSA private key to /etc/ipsec.d/private and set permissions:

cp client.key /etc/ipsec.d/private/
chmod 600 /etc/ipsec.d/private/client.key

Copy the home computer's X.509 certificate to /etc/ipsec.d/certs:

cp client.pem /etc/ipsec.d/certs/

Copy the home computer's X.509 CA chain to /etc/ipsec.d/cacerts:

cp client-cachain.pem /etc/ipsec.d/cacerts/

Copy the tunnel endpoint's X.509 CA chain to /etc/ipsec.d/cacerts:

cp endpoint-cachain.pem /etc/ipsec.d/cacerts/ 9)

9) Obviously, if the client's and endpoint's certificates have the same CA chain, you only need to copy it once.

The following configuration options go into /etc/ipsec.conf:

/etc/ipsec.conf

config setup
  strictcrlpolicy=no
  charondebug="ike 4, enc 4, knl 4, cfg 2" #useful debugs

conn %default
  ikelifetime=1440m
  keylife=60m
  rekeymargin=3m
  compress=no
  mobike=yes

conn mytunnel 10)
  type=tunnel
  keyexchange=ikev2
  auto=add
  # client settings
  left=%defaultroute
  leftcert=client.pem
  leftid=user@my.domain
  leftauth=pubkey
  leftsourceip=%config
  # gateway settings
  right=5.6.7.8 11)
  rightid=tunnel.my.domain
  rightauth=pubkey
  rightsubnet=::/0

10) mytunnel is just an arbitrary identifier string. You can use anything here.
11) For stability's sake, you should use the IPv4 address of the gateway here, not its host name. If you use the host name and there is an IPv6 address associated with that host name in DNS together with its IPv4 address, this will create an unresolvable chicken-or-egg tie.

Bring up the IPSec tunnel

If everything is configured correctly, you should be able to bring up your IPSec connection with the following two commands:

ipsec start
ipsec up mytunnel

If all goes well, you should receive output along the lines of:

ipsec start
Starting strongSwan 5.2.0 IPsec [starter]...

ipsec up mytunnel
initiating IKE_SA mytunnel[1] to 5.6.7.8
generating IKE_SA_INIT request 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) ]
sending packet: from 192.168.0.2[500] to 5.6.7.8[500] (1132 bytes)
received packet: from 5.6.7.8[500] to 192.168.0.2[500] (457 bytes)
parsed IKE_SA_INIT response 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) CERTREQ ]
local host is behind NAT, sending keep alives
remote host is behind NAT
received cert request for "CN=my.domain CA"
sending cert request for "CN=my.domain CA"
authentication of 'client@my.domain' (myself) with RSA signature successful
sending end entity cert "CN=Client, E=client@my.domain"
establishing CHILD_SA mytunnel
generating IKE_AUTH request 1 [ IDi CERT N(INIT_CONTACT) CERTREQ IDr AUTH CPRQ(ADDR6 DNS6) SA TSi TSr N(MOBIKE_SUP) N(NO_ADD_ADDR) N(EAP_ONLY) ]
sending packet: from 192.168.0.2[4500] to 5.6.7.8[4500] (2220 bytes)
received packet: from 5.6.7.8[4500] to 192.168.0.2[4500] (1996 bytes)
parsed IKE_AUTH response 1 [ IDr CERT AUTH CPRP(ADDR6) SA TSi TSr ]
received end entity cert "CN=tunnel.my.domain"
using certificate "CN=tunnel.my.domain"
using trusted ca certificate "CN=my.domain CA"
checking certificate status of "CN=tunnel.my.domain"
certificate status is not available
reached self-signed root ca with a path length of 0
authentication of 'tunnel.my.domain' with RSA signature successful
IKE_SA mytunnel[1] established between 192.168.0.2[client@my.domain]...5.6.7.8[tunnel.my.domain]
scheduling reauthentication in 86173s
maximum IKE_SA lifetime 86353s
installing new virtual IP 2a00::101
CHILD_SA mytunnel{2} established with SPIs 12345678_i abcdef01_o and TS 2a00::101/128 === ::/0 connection 'mytunnel' established successfully

The charon IKE daemon from strongswan will set up the IPv6 address and also set up the default route for us. You can see this in /var/log/messages on the home computer:

Aug 29 14:18:51 client charon: 03[CFG] selecting traffic selectors for us:
Aug 29 14:18:51 client charon: 03[CFG] config: 2a00::101/128, received: 2a00::100/120 => match: 2a00::101/128
Aug 29 14:18:51 client charon: 03[CFG] selecting traffic selectors for other:
Aug 29 14:18:51 client charon: 03[CFG] config: ::/0, received: ::/0 => match: ::/0
[...]
Aug 29 14:18:51 client charon: 03[KNL] adding policy 2a00::101/128 === ::/0 out (mark 0/0x00000000)
[...]
Aug 29 14:18:51 client charon: 03[KNL] adding policy ::/0 === 2a00::101/128 in (mark 0/0x00000000)
[...]
Aug 29 14:18:51 client charon: 03[KNL] getting a local address in traffic selector 2a00::101/128
Aug 29 14:18:51 client charon: 03[KNL] using host 2a00::101
Aug 29 14:18:51 client charon: 03[KNL] using 192.168.0.1 as nexthop to reach 5.6.7.8/32
Aug 29 14:18:51 client charon: 03[KNL] 192.168.0.2 is on interface wlan0
Aug 29 14:18:51 client charon: 03[KNL] installing route: ::/0 via 192.168.0.1 src 2a00::101 dev wlan0
Aug 29 14:18:51 client charon: 03[KNL] getting iface index for wlan0
Aug 29 14:18:51 client charon: 03[KNL] policy 2a00::101/128 === ::/0 out (mark 0/0x00000000) already exists, increasing refcount
[...]

You can also see the new "virtual" ip addresses in the output of

ip addr show

In this example, the egress interface that was assigned the "virtual" IPv6 addresses is wlan0:

[...]
10: wlan0: mtu 1500 qdisc mq state UP group default qlen 1000
  link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff
  inet 192.168.0.2/24 brd 192.168.0.255 scope global wlan0
    valid_lft forever preferred_lft forever
  inet6 2a00::101/128 scope global deprecated 12)
    valid_lft forever preferred_lft 0sec
  inet6 fe80::aabb:ccdd:eeff/64 scope link
    valid_lft forever preferred_lft forever

12) The deprecated seems suspicious here, and I'm not sure why it is set that way by strongswan. The behaviour has been discussed on the StrongSwan mailing list, but it seems that a generic solution is still unclear. (See also my bug report.) While it does not seem to be a fatal error (IPv6 connections work as expected), it has a downside: The client OS will use this IPv6 interface only if explicitly requested to do so. If a site now has both IPv4 and IPv6 addresses, the OS will automatically prefer IPv4, because its IPv6 interface is "deprecated". I had to manually set the lifetime of the interface again:

ip -6 addr change 2a00::101/128 dev wlan0 valid_lft 0xffffffff preferred_lft 0xffffffff

After that, the IPv6 address on wlan0 is again fully usable:

ip addr show
[...]
10: wlan0: mtu 1500 qdisc mq state UP group default qlen 1000
  link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff
  inet 192.168.0.2/24 brd 192.168.0.255 scope global wlan0
    valid_lft forever preferred_lft forever
  inet6 2a00::101/128 scope global
    valid_lft forever preferred_lft forever
  inet6 fe80::aabb:ccdd:eeff/64 scope link
    valid_lft forever preferred_lft forever


We can also see that the default route was configured:

ip -6 route show

2a00::101 dev wlan0 proto kernel metric 256
fe80::/64 dev wlan0 proto kernel metric 256
ff00::/8 dev wlan0 metric 256
default dev wlan0 proto static src 2a00::101 metric 1024

I've written a small script to automate the client-side connection: Download.
You will need to edit it before using it.

ip6tunnel.sh

Usage:
ip6tunnel.sh [start|stop|status]

On the tunnel endpoint (gateway) you should see the IPSec SA and flow as follows:

ipsecctl -s all

The output should be along the lines of:

FLOWS:
flow esp in from 2a00::100/120 to ::/0 peer 1.2.3.4 srcid FQDN/tunnel.my.domain dstid UFQDN/client@my.domain type use
flow esp out from ::/0 to 2a00::100/120 peer 1.2.3.4 srcid FQDN/tunnel.my.domain dstid UFQDN/client@my.domain type require

SAD:
esp tunnel from 1.2.3.4 to 192.168.100.2 spi 0xabcdef01 auth hmac-sha1 enc aes
esp tunnel from 192.168.100.2 to 1.2.3.4 spi 0x12345678 auth hmac-sha1 enc aes

If any part of the setup so far fails, check /var/log/messages on the home computer and /var/log/daemon on the tunnel endpoint for errors and correct them.

That's it!

If all went well, all that's left to do is to ensure that:

  • Packets from the client 2a00::101 are allowed out to the internet via the colocation dual-stack network firewall (2a00::1). You might have to adjust your firewall rules for that.

    Since my firewall also runs on OpenBSD, this was a one-liner added to /etc/pf.conf

    # Allow ipv6-tunnel client out to internet
    pass log quick inet6 from 2a00::101 to any

  • Packets to 2a00::101 are routed to the tunnel endpoint on 2a00::2 (only the reply packets of established connections, of course!). This will most likely require an additional route for (at least) 2a00::101 via 2a00::2 on the firewall.

    On the OpenBSD firewall I just added the appropriate route command for the entire network 2a00::100/120 to the configuration file for the interface (em2 in my case) that connects to the dual-stack colocation network:

    /etc/hostname.em2

    inet 192.168.100.1 255.255.255.0 192.168.100.255
    inet6 2a00::1 120
    !route add -inet6 2a00::100/120 2a00::2


After activating the two changes above on the firewall, we should now be able to reach any IPv6 host through our tunnel and colocation network.

ping6 2a00::2

PING 2a00::2(2a00::2) 56 data bytes
64 bytes from 2a00::2: icmp_seq=1 ttl=64 time=18.2 ms
64 bytes from 2a00::2: icmp_seq=2 ttl=64 time=17.7 ms
64 bytes from 2a00::2: icmp_seq=3 ttl=64 time=17.7 ms

ping6 www.google.com

PING www.google.com(zrh04s06-in-x10.1e100.net) 56 data bytes
64 bytes from zrh04s06-in-x10.1e100.net: icmp_seq=1 ttl=57 time=21.6 ms
64 bytes from zrh04s06-in-x10.1e100.net: icmp_seq=2 ttl=57 time=21.3 ms
64 bytes from zrh04s06-in-x10.1e100.net: icmp_seq=3 ttl=57 time=17.3 ms

Links

Well, I hope the above was helpful to you. Finding out how to set this up, working around bugs and incompatibilities, took some days. But thanks to other people who had posted their respective setups and experiences on the internet, I was finally able to pull it all together. Also, the members of the OpenBSD community (the misc@openbsd.org mailing list) were crucial in debugging some iked issues. As mentioned above, Ryan Slack brought up the idea that IPv6 could be tunnelled natively over IPSec. Here are some of the helpful links I found:

Feel free to send any suggestions, questions and corrections to the webmaster link below (requires javascript).


Markus Wernig

webmaster wernig net