Simple IPv6 over OpenVPN for Linux
Lindsay Haisley
fmouse@fmp.com
I was recently notified that my tunnel broker service (SixXS), which I've been using for years, will soon shut down. It seems that IPv6 is now "of age" and experimental v6 over v4 tunnel services are becoming redundant and unnecessary as more and more cloud hosting services offer full IPv6 support. One can often even get an IPv6 address from one's ISP, although in my case, it's a single dynamic address and not very useful for a subnet.

I run a small web and email hosting service using a cloud platform provided by Linode, and have for some time been using OpenVPN to connect our SOHO LAN to my Linode server using RFC 1918 addresses. The folks at Linode kindly provided me with a /64 IPv6 address block when I set up my account so that I and all my clients can have dedicated v6 addresses for their websites.

Subnetting for the purpose of provisioning our LAN with IPv6 addresses is anotther matter. Generally, an assignment of v6 addresses requires at least a /64 address block to support stateless autoconfiguratin (SLAAC) and other IPv6 features, so after some discussion with Linode's tech support I was given an additional /56 block of addresses, giving me enough /64 blocks for reassignment to my LAN, my laptop and any other host or group of hosts for which I might need v6 addresses.

Since I was already running OpenVPN on every server and gateway I have, it was the logical tool with which to set up my own tunnel service. After reviewing and trying a number of fairly complex OpenVPN v6 over v4 setups which I found online, I settled on a very simple configuration which works quite well.

The following discussion covers, in a slightly haphazard fashion, the setup of an IPv6 tunnel from a server to both a single host, such as a laptop, and to a LAN on which many hosts need IPv6 connectivity. The discussion and code comments should make it clear which files and configurations are needed for each situation. It's assumed that you're familiar with basic system administrative procedures such as moving, copying and changing the ownership and permissions mode of files, installing packages from your distribution, as well as basic principles of networking such as LAN topology and gateway configuration.

Prerequisistes .......
OpenVPN:
You'll need to have a version of OpenVPN which supports IPv6 installed on both ends of your tunnel. These days, this shouldn't be a problem. Full IPv6 transport and payload support is available in OpenVPN 2.2-RC2 and later versions. Unless your distribution and/or your version of OpenVPN is antique, by digital standards, you'll be fine. Since we're using TAP mode in this configuration, any reasonably recent version of OpenVPN will work as long as the carrier connection uses IPv4 endpoints.
Server:
You will need at minimum a /56 IPv6 routed address block provided to you by your hosting provider. This will give you 256 /64 blocks with which to work. A /64 is the smallest IPv6 address block recommended for assignment to a LAN. See this website at EtherealMind.com for a good discussion of this requirement. You'll also need the
ip(8)
utility. In Ubuntu Linux, this is part of the
iproute2
package which is popular with folks who work with networking.
Client:
If you're setting up a client tunnel endpoint with a single IPv6 address you'll need only OpenVPN and the
ip(8)
utility, as above. If your client tunnel endpoint is the gateway for a LAN and you plan to assign IPv6 addresses to your LAN hosts, you'll need the
radvd
package which will enable SLAAC (automatic) configuration of your LAN-connected hosts. More about that later.
Nitty Gritty, Server .......
We'll set up a very simple tunnel. Non-TLS security/encryption will be provided by a single OpenVPN shared key which need only be generated once and can be used for all your IPv6 tunnels. Generate your key with:
 openvpn --genkey --secret /etc/openvpn/v6secret.key
		
Be sure to make v6secret.key readable only by the root user (mode 600). If you need greater security, you can use dual-key TLS encryption instead of a single shared key. Instructions for this can be found in the OpenVPN documentation, but we're applying the KISS principle here to get you up and running. Copy
v6secret.key
to
/etc/openvpn/
on every client or gateway which will need an OpenVPN IPv6 tunnel to your server, and be sure to set the mode of each copy to 600, i.e. readable only by root.

Next you'll need to create two files on your server: an OpenVPN confuguration file and an tunnel-up executable script which OpenVPN will execute after it sets up this tunnel. The tunnel-up script must be executable by the OpenVPN user, in our case the root user, since modifications to the kernel address and routing tables must be made with elevated privilege. We won't need a tunnel takedown script since deleting the TAP interface will moot the TAP device address assignment and routing which we'll set up in the tunnel-up script and they'll be removed from the kernel.

/etc/openvpn/v6tunnel.conf
dev tap
port 1200
secret /etc/openvpn/v6secret.key
script-security 3

# MTU
fragment 1170
mssfix

# Scripts to run
up /etc/openvpn/v6tunnel-up.sh

# Logging
# log /etc/openvpn/v6tunnel.log
# verb 5

# Keepalive
ping 30
		
The port you select must be otherwise unused, and must agree with the port assignment on the client endpoint of your tunnel. The MTU settings are ones which I've determined to work well with my tunnels here. YMMV, of course, and you may wish to research these settings and adjust them to your own requirements. If you need to debug your setup uncomment the two lines following #logging. A logging level of 5 is quite chatty, but logs about everything you might need to know to solve any problem you might have.

Our other server-side file will be our tunnel-up script. We'll assume, for this discussion, that our /56 prefix is 2600:2c11:9c09:e100::. You'll use your own assigned prefix, of course.

/etc/openvpn/v6tunnel-up.sh

#!/bin/sh

ip link set dev $dev up
ip -6 addr add 2600:2c11:9c09:e101::1/64 dev $dev

# Use this line to route to a LAN
ip -6 route add 2600:2c11:9c09:e102::/64 via 2600:2c11:9c09:e101::2 dev $dev metric 100

# Use this line to route to a single host such as a laptop
# ip -6 route add 2600:2c11:9c09:e101::/64 via 2600:2c11:9c09:e101::2 dev $dev metric 100
exit 0
		
The address 2600:2c11:9c09:e101::1/64 is assigned to the TAP device on the server, and another address in this /64, 2600:2c11:9c09:e101::2, will be assigned to the client tunnel endpoint on your remote host or LAN gateway by its tunnel-up script. Note that if we're setting up an IPv6 tunnel to a LAN, we'll use two /64 subnets. Addresses in the 2nd /64 subnet, 2600:2c11:9c09:e102::/64, will be assigned to individual LAN hosts, as described below. This will be the subnet prefix for your LAN. If your tunnel will be to a single host, such as a laptop, you'll only need a single /64 subnet from your assigned /56 and you should comment out the 1st routing line and uncomment the 2nd, as noted in the code comments.
Packet Forwarding Considerations
You'll need to make one additional modification to the kernel configuration to enable your server to properly handle and route IPv6 packets from its public interface to your LAN or remote host. In Linux you should add the following lines to /etc/sysctl.conf:
net.ipv6.conf.eth0.accept_ra=2
net.ipv6.conf.all.forwarding=1	
		
If the public interface on your server is other than eth0, change it in the first line above. This line enables your server to accept routing advertisements even if forwarding is enabled on it. I found that failing to set this properly on my server disabled IPv6 connectivity to and from the server's public interface, effectively killing IPv6 altogether. A hint from one of Linode's sharper and more experienced tech support folks solved this problem.

The second line above simply allows forwarding of IPv6 packets between interfaces, and is comparable to the v4 kernel config used on all Linux gateways and routers.

You'll need to reboot your server to make these settings effective, or else issue the proper sysctl(8) commands from a root CLI prompt.

Nitty Gritty, Client .......
Your client-side configuration will be very similar to that on your server. If you're setting up an IPv6 tunnel for a LAN, you'll want both a tunnel-up and a tunnel-down script. For a single host, you can omit the tunnel-down script. The OpenVPN config file should look like the following:
/etc/openvpn/v6tunnel.conf
# Doodle!
remote server.example.com
float
port 1200
dev tap

# MTU - see man openvpn(8)
mssfix 1170
fragment 1170

# Security :)
secret /etc/openvpn/v6secret.key
script-security 3

# Set the interface and the route
up /etc/openvpn/v6tunnel-up.sh
# If routing for a LAN, you'll need a tunnel-down script as well. If
# routing to a single host, you can comment out the following line.
down /etc/openvpn/v6tunnel-down.sh

# Log me!
# log v6tunnel.log
# verb 5
ping 40
		
Put your server name (or its IPv4 address) in the remote line. The float directive allows flexibility if the IP address changes during the session, and is probably superfluous here, but is harmless if not needed. MTU settings were determined after testing, and should accomodate a connection with a standard MTU of 1500. The secret key must be the same one generated and used on the server endpoint discussed above and must be readable only by root (mode 600). The tunnel-up script is required, and the tunnel-down script is recommended if your client tunnel endpoint is a gateway for a LAN. Uncomment the log control lines if you want logging. If your client is on a masqueraded LAN, the ping directive keeps the connection open and the client endpoint accessible from your server.
/etc/openvpn/v6tunnel-up.sh
#!/bin/sh

ip link set dev $dev up
ip -6 addr add 2600:2c11:9c09:e101::2/64 dev $dev
ip -6 route add 2000::/3 via 2600:2c11:9c09:e101::1 metric 100 

# If routing for a LAN, include the following line. Otherwise, comment it out
ip -6 addr add 2600:2c11:9c09:e102::1/64 dev eth2
exit 0
		
Note that ths script must be executable (mode 744) and owned by root. After turning on the TAP interface with ip link set ..., we assign an address to it. Note that this address is the same as the "via" (routing) address in the server endpoint tunnel-up script, just as the "via" address in this script matches the assigned address of the server tunnel endpoint. This routing directive tells the client host to route any IPv6 packets whose destination is not local (2000::/3 pretty much catches everything) through the server-side tunnel endpoint. This is the equivalent of the default gateway address in v4 routing.

If your tunnel is providing IPv6 connectivity to a LAN, include the final ip -6 addr add ... line. The dev spec at the end of this line should be the name of the LAN interface of your gateway.

/etc/openvpn/v6tunnel-down.sh (required only if routing IPv6 to a LAN)
#!/bin/sh

ip -6 addr del 2600:2c11:9c09:e102::1/64 dev eth2
exit 0
		
As with the tunnel-up script, this script should be executable (mode 744, owner root). The dev target should be the name of the LAN interface of the gateway host.
Packet Forwarding Considerations (only needed for a LAN gateway)
As with the server, you'll need to add a line to /etc/sysctl.conf if you're tunneling IPv6 for a LAN. It's not needed if you're only setting up a tunnel for a single host.
Add to /etc/sysctl.conf
net.ipv6.conf.eth0.accept_ra=0
# The following line is only needed for a LAN gateway
net.ipv6.conf.all.forwarding=1
		
The interface name in the 1st line above should be the name of the WAN IPv4 interface of your gateway or host. Some ISPs, such as Spectrum (formerly Time/Warner) are using router advertisements to assign /128 dynamic IPv6 addresses to connected customers, and it's assumed that if you're setting up IPv6 over OpenVPN for this host you want the advantages of a fixed IP address, or the ability to route a /64 to your LAN. So we're disabling the ability of your WAN interface to accept these router advertisements and set up an IPv6 configuration which may conflict with your tunnel.

The 2nd line, enabling forwarding, is only necessary if you're configuring a LAN gateway.

Router advertisements on your LAN (only needed for a LAN gateway)
If all the pieces are in place, IPv6 "just works". An important part of this is the process of neighbor discovery, an essential component of which is a router advertisement daemon which broadcasts the gateway's address and the subnet prefix througout your LAN. Router advertisements enable a host newly connected to a network to assign to itself a unique IPv6 address, consistent with the subnet prefix, and to learn the IPv6 address of the LAN gateway, much as DHCP does for IPv4 networks. This daemon, called radvd, must be installed and running on your LAN gateway. Its configuration file is /etc/radvd.conf. You'll need to create or modify this file.
/etc/radvd.conf
interface eth2
{
	AdvSendAdvert on;

# These settings cause advertisements to be sent every 3-10 seconds.  This
# range is good for 6to4 with a dynamic IPv4 address, but can be greatly
# increased when not using 6to4 prefixes.
#
   MinRtrAdvInterval 5;
   MaxRtrAdvInterval 15;
   MinDelayBetweenRAs 5;
#
# Disable Mobile IPv6 support
#
#	AdvHomeAgentFlag off;

#
# example of a standard prefix
#
	prefix 2600:2c11:9c09:e102::/64
	{
		AdvOnLink on;
		AdvAutonomous on;
		AdvRouterAddr on;
	};

	RDNSS 2600:2c11:9c01::1 {
		AdvRDNSSLifetime 300;
		FlushRDNSS on;
	};
};	
		
The interface spec on the 1st line should be the LAN interface of your gateway. The prefix should be the subnet prefix assigned to this gateway in your server's tunnel-up script. The RDNSS stanza is optional and designates a name server identified by an IPv6 address. See the man page for radvd.conf for more details.

The radvd daemon may require restarting from time to time, and is guaranteed to die if the tunnel is taken down. It's possible to start the daemon from the v6tunnel-up.sh script but if it dies for any reason it must be restarted. I use monit(1) to keep the radvd daemon running. Configuration of monit is beyond the scope of this discussion.

---------------------------------------------------------------------------------------

FMP Computer Services
Lindsay Haisley, senior administrator
fmouse@fmp.com
PO Box 126
Leander, TX 78646
Phone: 512.496.7118