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 build an IPv6 tunnel over IPv4 GRE and IPSec

or

Set up your own 6in4 tunnel broker with Free Software

Abstract

This article shows how to configure an IPv4 GRE tunnel over IPSec, 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.

Update: In the meantime, I have found a way to forward the IPv6 traffic natively over IPSec. See the updated article here. Thanks to Ryan Slack for pointing this out.

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
        The GRE tunnel
        IPv6 forwarding
  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 a simple, multi-purpose tunneling protocol called GRE (Generic Routing Encapsulation, developed by Cisco). The router will decapsulate the packets ("remove the envelope") and forward our original IPv6 packets to the internet (for better security we will also be using IPSec for encryption and authentication of the tunnel).


Network Schema of solution
Fig. 2: Schematic illustration of the solution: The client establishes an IPSec tunnel with the tunnel endpoint over IPv4 (blue). Within that tunnel, a second tunnel is established using GRE. This GRE tunnel is used to encapsulate all IPv6 traffic (red) from the client. The tunnel endpoint terminates (removes) both tunnels 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
  • iproute2
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

    • GRE tunneling capability.
      Under Linux, that capability is either compiled into the kernel or provided by the module "ip_gre". To check if your kernel already supports GRE tunnels, run the command:

      ip tunnel

      (If you don't have the ip command, you need to install the iproute2 package.) You should see output like the following:

      gre0: gre/ip remote any local any ttl inherit nopmtudisc
      ip_vti0: ip/ip remote any local any ttl inherit nopmtudisc key 0
      sit0: ipv6/ip remote any local any ttl 64 nopmtudisc 6rd-prefix 2002::/16
      tunl0: ip/ip remote any local any ttl inherit nopmtudisc

      If you see the line starting with gre0:, you're fine. Else try to load the module first:

      modprobe ip_gre

      and run ip tunnel again. If you still don't get a gre0: line, you need to compile your own kernel. To compile GRE into the kernel, make sure at least the following options are set:

      CONFIG_NET_IPGRE_DEMUX=y
      CONFIG_NET_IPGRE=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). In theory, it would be possible to do without the "virtual" IP addresses for the GRE tunnel endpoints, but that would require a more complex IPSec setup. Besides, there appear to be some incompatibilities between strongswan and OpenBSD's iked that prevent this.

    • 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 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) IPv4 addresses that are from a dedicated network, which is not used at the tunnel site or in the home computer's network! They will serve as GRE tunnel endpoints.

    • 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 GRE, 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 UDP packets statefully (most routers and firewalls can. In my case, the firewall at the colocation is also OpenBSD).

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 GRE tunnel endpoints
    IPv4 IPv6
    Network 192.168.101.0/24 N/A
    Home computer 192.168.101.1 N/A
    Tunnel endpoint computer 192.168.101.2 N/A
    Default gateway N/A N/A

  • 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 2a00::102
    Default gateway (for home computer) N/A 2a00::102

  • 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 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 192.168.101.1 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))
Note: This file will be modified later on in the process.

/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.gre.allow=1 # enable GRE
net.inet.gre.wccp=1 # not sure if this is needed for non-Cisco devices
net.inet.ip.forwarding=1 # enable IPv4 forwarding
net.inet6.ip6.forwarding=1 # enable IPv6 forwarding
net.inet.ipcomp.enable=1 # enable IP compression

With this, we could configure the system as a GRE tunnel endpoint. But since we want GRE (which is not encrypted or authenticated) to run over IPSec, we need to set up IPSec first. 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" IPv4 address to use within the IPSec tunnel.
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.0.0.0/0 to 192.168.101.0/24 \ 7)
  peer 0.0.0.0/0 local 192.168.100.2 \ 7)
  srcid tunnel.my.domain dstid client@my.domain \
  config address 192.168.101.1 \
  config address 192.168.101.0/26 \ 8)
  # config netmask 255.255.255.0 \ 8)
  config protected-network 192.168.101.0/24 \
  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", but as of this writing, it is necessary to use "0.0.0.0/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
8) This should not be required if setting a fixed address in the line above. But strangely, strongswan fails to set up the flow if it is sent a fixed IP only. So we send it both, a fixed address of 192.168.101.1 and a random address from the address pool 192.168.101.0/26. Similarly, it fails if it is sent a netmask config entry (commented out above). With the configuration given above, strongswan will add both received IP addresses (the fixed one and the one from the pool) as host-only addresses to its egress interface (ie. with an IPv4 netmask of /32). Since we rely on knowing the assigned IPv4 address, we use the fixed one.

Now, since we told iked to negotiate tunnels from anywhere to 192.168.101.0/24, we need to give the tunnel endpoint (gateway) an IPv4 address in that network. This will be the endpoint address of the GRE tunnel, that we will establish through the IPSec tunnel.
We do this by adding an alias line to /etc/hostname.vio0, so that it now reads:

/etc/hostname.vio0

inet 192.168.100.2 255.255.255.0
inet alias 192.168.101.2 255.255.255.0
inet6 2a00::2

Next, we need to prepare the system for the GRE (encapsulated) tunnel, through which it will receive the IPv6 packets from the home computer to forward. Given the above network layout, we set 192.168.101.1 as the client's GRE tunnel endpoint, and 192.168.101.2 as the gateway's GRE tunnel endpoint. Additionally, we set 2a00::102 as the IPv6 address of the gateway's GRE tunnel interface, which the home computer can use as default gateway for all IPv6 traffic. We also tell the system to take both sides of the tunnel up, irrespective of the actual network state (link0, the local side, is always set to up).

/etc/hostname.gre0

inet6 2a00::102 120
tunnel 192.168.101.2 192.168.101.1
link1 up

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" 9) # 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

9) 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 and GRE 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/ 10)

10) 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
  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=192.168.101.0/24

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(ADDR DNS) 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(ADDR ADDR) 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 192.168.101.1
installing new virtual IP 192.168.101.55
connection 'mytunnel' established successfully

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" IPv4 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
  inet 192.168.101.1/32 scope global wlan0
    valid_lft forever preferred_lft forever
  inet 192.168.101.55/32 scope global wlan0
    valid_lft forever preferred_lft forever
  inet6 fe80::aabb:ccdd:eeff/64 scope link
    valid_lft forever preferred_lft forever

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 192.168.101.0/24 to 0.0.0.0/0 peer 1.2.3.4 srcid FQDN/tunnel.my.domain dstid UFQDN/client@my.domain type use
flow esp out from 0.0.0.0/0 to 192.168.101.0/24 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.

Bring up the GRE tunnel and set up IPv6 through it on the home computer

Once the IPSec tunnel is up, we can continue to set up our GRE tunnel over it. On the home computer (client) run the following commands:

Make sure GRE is available:

modprobe ip_gre

Set up GRE tunnel between local "virtual" address and tunnel endpoint (reachable only via IPSec):

ip tunnel add gre6in4 mode gre ttl 255 remote 192.168.101.2 local 192.168.101.1 pmtudisc

ip link set gre6in4 up

Assign IPv6 address to home computer. It will be "visible" on the IPv6 internet with this address:

ip -6 addr add 2a00::101 dev gre6in4

Set IPv6 default route for all IPv6 addresses via the tunnel.
Note: You can also choose to route a smaller portion of the IPv6 address space over the tunnel.

ip -6 route add ::/0 via 2a00::102 dev gre6in4 metric 1

ip addr show should now display an additional interface gre6in4:

19: gre6in4@NONE: mtu 1476 qdisc noqueue state UNKNOWN group default
  link/gre 192.168.101.1 peer 192.168.101.2
  inet6 2a00::101/120 scope global
    valid_lft forever preferred_lft forever
  inet6 fe80::ffee:ddcc:bbaa/64 scope link
    valid_lft forever preferred_lft forever

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::102

PING 2a00::102(2a00::102) 56 data bytes
64 bytes from 2a00::102: icmp_seq=1 ttl=64 time=18.2 ms
64 bytes from 2a00::102: icmp_seq=2 ttl=64 time=17.7 ms
64 bytes from 2a00::102: 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. 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