Introduction
This HOWTO explains how to use OpenVPN for a IPv6 tunnel. This has only been tested on FreeBSD 7 as the server, and Mac OS X 10.5 as the client. It explicitly used the Gif interface that likely doesn't exist on other platforms.
The general idea is to use the routed Tun interface for OpenVPN, which doesn't support IPv6 natively. As a result we have to tunnel IPv6 traffic. We do this by setting up a Gif tunnel connecting between the VPN address of the client and server. The gif interface on the server will be assigned BASE_ADDR::1, and the client will be given BASE_ADDR::2. Note the below setup assumes you have a /48 block on your server, and are willing to hand out a /64 subnet to each client.
A couple of assumptions are made here. First that you already have a working IPv4 OpenVPN setup, with working authentication. If you don't take a look at OpenVPN's website for various HOWTO's.
Server
Openvpn.conf
port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key # This file should be kept secret
dh dh1024.pem
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "redirect-gateway"
push "dhcp-option DNS 208.67.222.222"
client-to-client
keepalive 10 120
tls-auth ta.key 0 # This file is secret
comp-lzo
user openvpn
group openvpn
persist-key
persist-tun
status openvpn-status.log
verb 3
mute-replay-warnings
mute 20
client-connect /usr/local/etc/openvpn/client_connect.sh
client-disconnect /usr/local/etc/openvpn/client_disconnect.sh
On the server you'll need to have two scripts for OpenVPN. Each will be added to your openvpn.conf with the following lines:
client-connect /usr/local/etc/openvpn/client_connect.sh
client-disconnect /usr/local/etc/openvpn/client_disconnect.sh
Ensure each of these scripts are executable by the openvpn user.
Client_connect.sh
#!/usr/local/bin/bash
# script that is run on the client when it creates a tunnel to the remote OpenVPN server
IPV6BASE=2607:1234:abcd
#Change this to match your server's MAC address.
SERVER_LLADDR=00:11:22:33:44:55
V6NET=$(echo ${ifconfig_pool_remote_ip} | cut -d. -f4)
GIFID="gif${V6NET}"
sudo ifconfig ${GIFID} create
sudo /sbin/ifconfig ${GIFID} tunnel ${ifconfig\_local} ${ifconfig_pool_remote_ip}
sudo /sbin/ifconfig ${GIFID} inet6 ${IPV6BASE}:${V6NET}::1/64
sudo /sbin/route add -inet6 ${IPV6BASE}:${V6NET}::/64 ${IPV6BASE}:${V6NET}::2
sudo /usr/sbin/ndp -d ${IPV6BASE}:${V6NET}::2
sudo /usr/sbin/ndp -s ${IPV6BASE}:${V6NET}::2 ${SERVER_LLADDR} proxy
exit 0
Client_disconnect.sh
#!/usr/local/bin/bash
#script that is run on the client when the client disconnects from the remote OpenVPN server
IPV6BASE=2607:1234:abcd
#Change this to match your server's MAC address.
SERVER_LLADDR=00:11:22:33:44:55
V6NET=$(echo ${ifconfig_pool_remote_ip} | cut -d. -f4)
GIFID="gif${V6NET}"
#By destroying the interface it takes care of all the routing and addressing
sudo /sbin/ifconfig ${GIFID} destroy
exit 0
Sudoers
Add this line to allow openvpn to run the following commands. This may not actually be necessary as OpenVPN may be running these scripts as root anyways.
openvpn ALL=(ALL) NOPASSWD: /sbin/route, /sbin/ifconfig, /usr/sbin/ndp
Client
Client Openvpn.conf
remote vpn.sysadminschronicles.com
proto udp
port 1194
dev tun
persist-tun
nobind
client
comp-lzo
persist-key
up-restart
#Needed so the client will explicitly tell the server when its exits, so the connection-disconnect script gets run immediately.
explicit-exit-notify
ca "openvpn/ca.crt"
cert "openvpn/ballen.crt"
key "openvpn/ballen.key"
ns-cert-type server
tls-auth openvpn/ta.key 1
up "openvpn/up.sh"
down "openvpn/down.sh"
These two scripts setup the client side of the IPv6 tunnel. Note we're using gif1 and not dynamically creating a new device. Ensure this device exists and create it if necessary, i.e. ifconfig gif1 create.
Up.sh
#!/usr/bin/env bash
INTERFACE=$1; shift;
TUN_MTU=$1; shift;
UDP_MTU=$1; shift;
LOCAL_IP=$1; shift;
REMOTE_IP=$1; shift;
MODUS=$1; shift;
#script that is run on the client when it creates a tunnel to the remote OpenVPN server
IPV6BASE=2607:1234:abcd
SERVER_IP=10.8.0.1
V6NET=$(echo ${LOCAL_IP} | cut -d. -f4)
GIFID="gif1"
sudo /sbin/ifconfig ${GIFID} tunnel ${LOCAL_IP} ${SERVER_IP}
sudo /sbin/ifconfig ${GIFID} inet6 ${IPV6BASE}:${V6NET}::2/64
sudo /sbin/route delete -inet6 default
sudo /sbin/route add -inet6 default ${IPV6BASE}:${V6NET}::1
exit 0
Down.sh
Note: this script down not destroy the gif interface but removes the IPv6 address, the tunnel, and IPv6 default route.
#!/usr/bin/env bash
INTERFACE=$1; shift;
TUN_MTU=$1; shift;
UDP_MTU=$1; shift;
LOCAL_IP=$1; shift;
REMOTE_IP=$1; shift;
MODUS=$1; shift;
# script that is run on the client when it creates a tunnel to the remote OpenVPN server
IPV6BASE=2607:1234:abcd
SERVER_IP=10.8.0.1
V6NET=$(echo ${LOCAL_IP} | cut -d. -f4)
GIFID="gif1"
sudo /sbin/route delete -inet6 default
sudo /sbin/ifconfig ${GIFID} inet6 ${IPV6BASE}:${V6NET}::2 -alias
sudo /sbin/ifconfig ${GIFID} deletetunnel
exit 0
Sudoers
Add this line to allow your user to run the following commands without a password.
userName ALL=(ALL) NOPASSWD: /sbin/route, /sbin/ifconfig
Conclusion
What you should see after the connection is established, is a gif interface on the server where the number of the interface matches the last IPv4 octet of the client's VPN IP address. It will have created a tunnel from 10.8.0.1 (unless otherwise specified) to the VPN client IP address. It will have assigned itself a IPv6 address that starts with your IPV6BASE, followed by a /64 subnet that is the same number as the last octet of the client's IPv4 address. It will be ::1 in that subnet. If you examine the NDP table with ndp -a you'll see and entry for the VPN's client's IPv6 address with the server's MAC address and a "p" marked under flags for proxy. This allows the IPv6 router to discovery the new subnet, via the server.
On the client side you will see the gif1 interface with a tunnel created from the VPN client IP address to the server's VPN IP address, 10.8.0.1. It will have a IPv6 address that starts with your IPV6BASE, followed by a /64 subnet that is the same number as the last octet of the client's IPv4 address. It will be ::2 in that subnet.
You should be able to ping (remember to use ping6) the client's gif interface, the server's gif interface, the server's public IPv6 address, and the server's gateway's IPv6. If you can do all that your client should have a live IPv6 connection.
Limitations
The gif interface reduces the MTU of the connection down to 1280. Even if you used OpenVPN's TAP interface, which supports IPv6 natively you still have to set MTU down to 1280.
If your VPN connection drops. The server will take a while to destroy its gif interface. The client will tear down its connection immediately.
For whatever reason NDP does not work as expected with Gif interfaces. A Neighbor solicitation from the server's router for your VPN client's IPv6 address will not be forwarded over the tunnel by the server. Hence the need to add a static entry for the client's IPv6 address in the server's NDP table.