How can I setup OpenVPN with IPv4 and IPv6 using a tap device?
I've managed to setup OpenVPN for full IPv4 connectivity using tap0. Now I want to do the same for IPv6.
Addresses and network setup (note that my real prefix is replaced by 2001:db8):
2001:db8::100:0:0/96 my assigned IPv6 range
2001:db8::100:abc:0/112 OpenVPN IPv6 range
2001:db8::100:abc:1 tap0 (on server) (set as gateway on client)
2001:db8::100:abc:2 tap0 (on client)
2001:db8::1:2:3:4 gateway for server
Home laptop (tap0: 2001:db8::100:abc:2/112 gateway 2001:db8::100:abc:1/112)
| | | (running Kubuntu 10.10; OpenVPN 2.1.0-3ubuntu1)
| wifi | |
router |
| OpenVPN
INTERNET |
eth0 | /tap0
VPS (eth0:2001:db8::1:2:3:4/64 gateway 2001:db8::1)
(tap0: 2001:db8::100:abc:1/112)
(running Debian 6; OpenVPN 2.1.3-2)
The server has both native IPv4 and IPv6 connectivity, the client has only IPv4.
I can ping6 to and from my server over OpenVPN, but not to other machines (for example, ipv6.google.com
).
net.ipv6.conf.all.forwarding
is set to 1
, I've tried disabling net.ipv6.conf.all.accept_ra
as well, without luck.
Using tcpdump
on both the server and client, I can see that packets are actually transferred over tap0 to eth0. The router (2001:db8::1) send a neighbor solicitation for the client (2001:db8::100:abc:2) to eth0 after it receives the ICMP6 echo-request. The server does not respond to that solicitation, which causes the ICMP6 echo-request not be routed to the destination.
How can I make this IPv6 connection work?
Timothy Baldwins answer put me on the right track, although the answer was rather cryptic. IPv6 neighbor advertisements/solicitation is like ARP for IPv6. It's used to "see" other machines on the network. The router send a neighbor solicitation on which the machine (server or client) should reply on with a neighbor advertisement.
Even with net.ipv6.conf.all.forwarding
set to 1
, neighbor advertisements and solicitations are not forwarded. To make eth0 forward neighbor advertisements and solicitations, eth0 should be set as proxy for the IPv6 address of the client behind tap0, and proxying for neighbor stuff should be enabled for eth0:
echo 1 > /proc/sys/net/ipv6/conf/eth0/proxy_ndp
/sbin/ip -6 neigh add proxy 2001:db8::100:abc:2 dev eth0
Unfortunately, it's not possible to retrieve the list of proxies added, nor does ip -6
show error messages on repeatedly executing the command. Neither am I sure if "neigh del proxy" works, it does not give an error message and the C source is not really meaningful to me.
As I do not want to do everything manually, I've created a script which does the work for me.
Server configuration
IPv6 addresses are based on the IPv4 part (the 1 in 10.8.0.1). The prefix and netmask are stored in /etc/openvpn/variables
.
The next steps are made for setting up OpenVPN with encrypted IPv4/IPv6 connectivity to the Internet over a native IPv4 connection. RSA keys and tls-auth are used for authentication and MITM prevention.
/etc/openvpn/variables
contains variables which are used for the up script (run on startup, after creation of tap0 device) and client-connect script (run after the client has authenticated).
# this prefix is handled out by the provider, replace it with your own prefix
prefix=2001:db8::abc
# netmask, 2001:db9::abc:0000 - 2001:db9::abc:FFFF
prefixlen=112
/etc/openvpn/server-clientconnect.sh
is executed as root and makes sure that IPv6 is routed properly by adding the IPv6 address to the eth0 proxy. Because client-connect
is called after OpenVPN switched to the user specified by the User
setting, sudo
is needed to give the script sufficient permissions to run as root. Of course, variables should be checked before using, the number should be between and including 2 ad 254. (1 is the gateway, 255 the broadcast address).
#!/bin/sh
. /etc/openvpn/variables
if [ -z "$ifconfig_pool_remote_ip" ]; then
echo "Missing environment variable."
exit 1
fi
ipp=$(echo "$ifconfig_pool_remote_ip" | cut -d. -f4)
if ! [ "$ipp" -ge 2 -a "$ipp" -le 254 ] 2>/dev/null; then
echo "Invalid IP part."
exit 1
fi
hexipp=$(printf '%x' $ipp)
/sbin/ip -6 neigh add proxy $pfx:$hexipp dev eth0
To make this work, the user vpn
should be allowed to run the script while preserving $ifconfig_pool_remote_ip
which contains the remote network IPv4 address. Add the next lines to the sudoers file by executing sudo visudo
and appending:
Defaults:vpn env_keep=ifconfig_pool_remote_ip
vpn ALL=NOPASSWD: /etc/openvpn/server-clientconnect.sh
/etc/openvpn/server-up.sh
enables IPv4, IPv6 forwarding (eth0+tap0 did not work, it really had to be all
) and neighbor proxy on eth0. It adds the gateway address to servers tap0
too.
#!/bin/sh
. /etc/openvpn/variables
/sbin/ip -6 addr add $pfx:1/$pfxlen dev $dev
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
echo 1 > /proc/sys/net/ipv6/conf/eth0/proxy_ndp
Finally, the OpenVPN configuration file at /etc/openvpn/internet.conf
:
proto udp
dev tap
ca ca.crt
cert server.crt
key server.key
dh dh1024.pem
server 10.8.0.0 255.255.255.0
script-security 2
up /etc/openvpn/server-up.sh
client-connect "/usr/bin/sudo -u root /etc/openvpn/server-clientconnect.sh"
# encrypt all traffic
push "redirect-gateway def1"
# keep the same IP addresses each time
ifconfig-pool-persist ipp.txt
keepalive 10 120
tls-auth ta.key 0
cipher BF-CBC
comp-lzo
# OpenVPN will switch to this user, it is also used for sudo
user vpn
group vpn
persist-key
persist-tun
For completeness, permissions and ownerships of files in /etc/openvpn
:
drwx------ root root .
-rw------- root root ca.crt
-rw------- root root dh1024.pem
drwx------ root root easy-rsa <-- left from creation of keys
-rw------- root root ipp.txt
-rwx------ root root server-clientconnect.sh
-rw------- root root internet.conf
-rw------- root root variables
-rwx------ root root server-up.sh
-rw------- root root server.crt
-rw------- root root server.key
-rw------- root root ta.key
-rwx------ root root update-resolv-conf <-- this was installed by default
Firewall settings:
itpables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -s 10.8.0.0/24 -i tap0 -o eth0 -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
ip6tables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -A FORWARD -s 2001::db8::abc:0/112 -i tap0 -o eth0 -j ACCEPT
Configuration on client
/etc/openvpn/client.conf
:
client
dev tap
proto udp
remote 178.21.112.251 1194
script-security 2
up /etc/openvpn/client-up.sh
resolv-retry infinite
nobind
user nobody
group nogroup
persist-key
persist-tun
ca ca.crt
cert home.crt
key home.key
ns-cert-type server
tls-auth ta.key 1
cipher BF-CBC
comp-lzo
ta.key
and ca.key
are the same files from the server. home.key
and home.crt
are files, created on the server.
client-up.sh
adds the IPv6 address and route (based on IPv4 address):
#!/bin/sh
pfx='2001:db8::abc'
pfxlen=112
hexippart=`printf '%x' "$(echo $ifconfig_local | cut -d. -f4)"`
/sbin/ip -6 addr add $pfx:$hexippart/$pfxlen dev $dev
/sbin/ip -6 route add default via $pfx:1 dev $dev
The guide at http://www.ipsidixit.net/2010/03/24/239/ was very helpful and OpenVPN manual page is useful for information about various settings.