This document details how I setup an ipv6 tunnel broker system with OpenVPN


Why I did it
In the past I have used various ipv6 tunnel brokers but seemed to always be having connection problems. To get around this, and to learn about OpenVPN and IPv6 I decided to setup my own tunnel broker.
I have a Xen VM from Goscomb Technologies. As part of the package they also provide a /48 of ipv6 addresses for each VPS. This is good if you want to play with ipv6 to see how it works.

The Problem
There are different methods to tunnel ipv6 packets over an ipv4 network. One of the easiest is to use the sit device to create a tunnel between two ipv4 systems. This method does have some problems though -

The question then become -
How do you provide a static ipv4 address when you live on the other side of the world, only have a private ip address and only have ADSL with a dynamic public ipv4 address.

The Solution
The Open source OpenVPN Project is provided with most Linux distributions and can be easily installed. When I get a chance I will provide details on how I installed OpenVPN but in the mean time see your distribution for installation details.

When OpenVPN is running it can provide static ipv4 addresses at each endpoint. If my public ipv4 address changes then OpenVPN will still maintain the tunnel ipv4 addresses. Just what we want.

Yes I know that OpenVPN has a tun-ipv6 device that can carry native ipv6 packets. The problem is you currently can not have a server with a tun-ipv6 device. It must be a tap device in bridge mode. I do not want to do this because it means changing the network configuration on my remote server which I do not have console access to. If anything goes wrong I could easily be locked out of my machine.

The Update

I received an email from Ian who mentioned that he was having some problems with this setup. It seems that not all providers configure ipv6 the same and he had to ser up Neighbor Discovery Proxy to get his system working.
Here is what he said -
One thing stopped it working. Packets from the internet were not making it back to my VM and from there back down the tunnel. A tcpdump on the VM revealed 'Neighbor Discovery Packets' for my machine being broadcast by the ISPs router. As soon as I added 'Neighbor Discovery Proxy' on my VM for the machine at my home end of the tunnel, it worked perfectly. It's similar to Proxy ARP in ipv4, I read about it here:
and added this to my server's client-connect.sh script:

# config Neighbor Discovery Proxy (like proxy ARP)
# also requires 'echo 1 >/proc/sys/net/ipv6/conf/all/proxy_ndp'
# e.g. in rc.local or using sysctl
sudo /sbin/ip -6 neigh add proxy ${BASERANGE}:${V6NET}::2 dev eth0
It works like a charm.

The Config files
Here is the server.conf file I have running on the Xen VM from Goscomb -
# Local IP address for the server to listen on.
local 9.8.7.6

# set the port or use the standard 1194
port 56789

# udp is the recommended transport
proto udp

# there is a tun-ipv6 device but I want to use the tun device
dev tun

# the keys I generated during the OpenVPN install process
ca /etc/openvpn/keys/ca.crt
cert /etc/openvpn/keys/server.crt
key /etc/openvpn/keys/server.key  # This file should be kept secret

dh /etc/openvpn/keys/dh2048.pem

# the ip range that OpenVPN will use for its static ipv4 addresses
server 10.18.0.0 255.255.255.0

tls-auth ta.key 0 # This file is secret

comp-lzo

max-clients 10

keepalive 18 83

user ovpn
group nogroup

persist-key
persist-tun

status openvpn-status.log

verb 5

# I added these to setup and tear down the ipv6 tunnels when a client connects or disconnects. See below
client-connect /etc/openvpn/client-connect.sh
client-disconnect /etc/openvpn/client-disconnect.sh
And here is the client.conf I used at each client
client

dev tun

proto udp

remote 9.8.7.6 56789

resolv-retry infinite

nobind

keepalive 27 79

user ovpn
group nogroup

persist-key
persist-tun

ca /etc/openvpn/keys/ca.crt
cert /etc/openvpn/keys/client1.crt
key /etc/openvpn/keys/client1.key

ns-cert-type server

tls-auth ta.key 1

comp-lzo

verb 3

# create the ipv6 tunnel
up /etc/openvpn/up.sh
down /etc/openvpn/down.sh

# need this so when the client disconnects it tells the server so the server can remove the ipv6 tunnel the client was using
explicit-exit-notify


With the above configurations OpenVPN will create a tunnel with static ipv4 address at each end. We now use those ipv4 addresses to create the ipv6 network. The ipv4 network the OpenVPN server uses is in the 10.18.0.0/24 network and it will allocate an address in this range to each remote client. For example 10.18.0.14

As we have a /48 of ipv6 addresses to play with we can then allocate a /64 to each connecting client. A /64 by the way is the smallest network you are meant to allocate in the ipv6 world.

The range of ipv6 addresses you get from your provider will look something like 2001:f5a:53::/48
This means to get to a /64 for each remote client then we have to add some number to the above. Why not add the last octet from the ipv4 of the remote OpenVPN client.
For example

OpenVPN allocates the remote client an ipv4 address of 10.18.0.14

The ipv6 range is 2001:f5a:53::/48

We therefore allocate 2001::f5a:53:14::/64 to the remote client

From this we use 2001::f5a:53:14::1 as the local end of the ipv6 tunnel
and we use 2001::f5a:53:14::2 as the remote ipv6 address for the client

Yes we waste the rest of the /64 of ipv6 addresses

The Server Script files
This is the script we run on the server each time a client connects. OpenVPN is good because it provides various environment variables with information about the client that has connected.

You will notice that the following script uses sudo. This is because the script runs as the same user that OpenVPN runs as so it will not have access to the /sbin/ip command. To get around this we need to add the following to the /etc/sudoers file. This will allow the ovpn user to run only the /sbin/ip command

ovpn ALL=(ALL)  NOPASSWD: /sbin/ip
client-connect.sh script file
#!/bin/bash

# This is a script that is run each time a remote client connects
# to this openvpn server.
# it will setup the ipv6 tunnel depending on the ip address that was 
# given to the client

BASERANGE="2001:f5a:53"
# v6net is the last section of the ipv4 address that openvpn allocated
V6NET=$(echo ${ifconfig_pool_remote_ip} | awk -F. '{print $NF}')

SITID="sit${V6NET}"

# setup the sit between the local and remote openvpn addresses
sudo /sbin/ip tunnel add ${SITID} mode sit ttl 255 remote ${ifconfig_pool_remote_ip} local ${ifconfig_local}
sudo /sbin/ip link set dev ${SITID} up

# config routing for the new network
sudo /sbin/ip -6 addr add ${BASERANGE}:${V6NET}::1/64 dev ${SITID}
sudo /sbin/ip -6 route add ${BASERANGE}:${V6NET}::/64 via ${BASERANGE}:${V6NET}::2 dev ${SITID} metric 1

# log to syslog
echo "${script_type} client_ip:${trusted_ip} common_name:${common_name} local_ip:${ifconfig_local} \
remote_ip:${ifconfig_pool_remote_ip} sit:${SITID} ipv6net:${V6NET}" | /usr/bin/logger -t ovpn 


When the remote client disconnects from the OpenVPN server we need to clean up the ipv6 tunnel on the server.

client-disconnect.sh script

#!/bin/bash

# This is a script that is run each time a remote client disconnects
# to this openvpn server.

BASERANGE="2001:f5a:53"
# v6net is the last section of the ipv4 address that openvpn allocated
V6NET=$(echo ${ifconfig_pool_remote_ip} | awk -F. '{print $NF}')

SITID="sit${V6NET}"

sudo /sbin/ip -6 addr del ${BASERANGE}:${V6NET}::1/64 dev ${SITID}

# remove the sit between the local and remote openvpn addresses
sudo /sbin/ip link set dev ${SITID} down
sudo /sbin/ip tunnel del ${SITID} mode sit ttl 255 remote ${ifconfig_pool_remote_ip} local ${ifconfig_local}

# log to syslog
echo "${script_type} client_ip:${trusted_ip} common_name:${common_name} local_ip:${ifconfig_local} \
remote_ip:${ifconfig_pool_remote_ip} sit:${SITID} ipv6net:${V6NET} duration:${time_duration} \
received:${bytes_received} sent:${bytes_sent}" | /usr/bin/logger -t ovpn 
I have also added the following lines to /etc/rc.local on the server to setup things for OpenVPN. The firewall rules restrict the tunnel to only carry ipv6inipv4 packets
# allow forwarding of ipv6 packets
echo "1" >/proc/sys/net/ipv6/conf/all/forwarding

# openvpn tunnel will only accept ipv6-in-v4 packets
iptables -A INPUT -i tun0 ! -p 41 -j REJECT --reject-with icmp-host-unreachable
iptables -A OUTPUT -o tun0 ! -p 41 -j REJECT --reject-with icmp-host-unreachable

The Client Script files
At the client end we also need to setup and remove the tunnel

The up.sh script on the client

#!/bin/bash

# script that is run on the client when it creates a tunnel to the remote OpenVPN server
IPV6BASE=2001:f5a:53

V6NET=$(echo ${ifconfig_local} | awk -F. '{print $NF}')

# NOTE!!! The following has a hard coded ipv4 address because I can not find a way for the client to find
# out the server ipv4 end point of the OpenVPN tunnel
/sbin/ip tunnel add sit1 mode sit ttl 255 remote 10.18.0.1 local ${ifconfig_local}
/sbin/ip link set dev sit1 up
/sbin/ip -6 addr add ${IPV6BASE}:${V6NET}::2/64 dev sit1

/sbin/ip route add ::/0 via ${IPV6BASE}:${V6NET}::1

exit 0

And the following is the down.sh script that is run on the client when the OpenVPN tunnel is shut down
#!/bin/bash

IPV6BASE=2001:f5a:53
V6NET=$(echo ${ifconfig_local} | awk -F. '{print $NF}')

sudo /sbin/ip -6 addr del ${IPV6BASE}:${V6NET}:2/64 dev sit1
sudo /sbin/ip link set dev sit1 down
sudo /sbin/ip tunnel del sit1 mode sit ttl 255 remote 10.18.0.1 local ${ifconfig_local}

sudo /sbin/ip route del ::/0 via ${IPV6BASE}:${V6NET}:1

exit 0

The End Product
And that is about that.

Start OpenVPN on the server and connect from the clients and they should create devices like this on the client -

human@dave:~$ ifconfig tun0
tun0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
          inet addr:10.18.0.6  P-t-P:10.18.0.5  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:1184 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1207 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:100 
          RX bytes:1244833 (1.2 MB)  TX bytes:160282 (160.2 KB)


human@dave:~$ ifconfig sit1
sit1      Link encap:IPv6-in-IPv4  
          inet6 addr: 2001:f5a:53:6::2/64 Scope:Global
          inet6 addr: fe80::a12:6/128 Scope:Link
          UP POINTOPOINT RUNNING NOARP  MTU:1480  Metric:1
          RX packets:1184 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1207 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1221153 (1.2 MB)  TX bytes:136142 (136.1 KB)
And this on the server
sit6      Link encap:IPv6-in-IPv4  
          inet6 addr: 2001:f5a:53:6::1/64 Scope:Global
          inet6 addr: fe80::a12:1/128 Scope:Link
          UP POINTOPOINT RUNNING NOARP  MTU:1480  Metric:1
          RX packets:544 errors:0 dropped:0 overruns:0 frame:0
          TX packets:485 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:74953 (73.1 KB)  TX bytes:432683 (422.5 KB)

tun0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
          inet addr:10.18.0.1  P-t-P:10.18.0.2  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:97712 errors:0 dropped:0 overruns:0 frame:0
          TX packets:93189 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:100 
          RX bytes:10047601 (9.5 MB)  TX bytes:71844638 (68.5 MB)

Now you can surf the web 128 bits at a time - Have Fun.