summaryrefslogtreecommitdiff
path: root/net-misc
diff options
context:
space:
mode:
authorBertrand Jacquin <beber@meleeweb.net>2008-03-08 16:27:47 +0100
committerBertrand Jacquin <beber@meleeweb.net>2008-03-08 16:27:47 +0100
commit16e5a903a45f1132e431f1770ab8d599a4127283 (patch)
tree30103af89aeb911ade8524f094d83dc250b064e9 /net-misc
parentCorrect sysconfdir issues (diff)
downloadportage-16e5a903a45f1132e431f1770ab8d599a4127283.tar.xz
last openvpn release with ipv6 support
Diffstat (limited to 'net-misc')
-rw-r--r--net-misc/openvpn/Manifest8
-rwxr-xr-xnet-misc/openvpn/files/down.sh33
-rw-r--r--net-misc/openvpn/files/openvpn-2.1.conf18
-rwxr-xr-xnet-misc/openvpn/files/openvpn-2.1.init131
-rw-r--r--net-misc/openvpn/files/openvpn-2.1_rc6-udp6.patch2521
-rw-r--r--net-misc/openvpn/files/openvpn-2.1_rc7-tap.patch57
-rwxr-xr-xnet-misc/openvpn/files/up.sh82
-rw-r--r--net-misc/openvpn/openvpn-2.1_rc7.ebuild156
8 files changed, 3006 insertions, 0 deletions
diff --git a/net-misc/openvpn/Manifest b/net-misc/openvpn/Manifest
new file mode 100644
index 00000000..a6d3345b
--- /dev/null
+++ b/net-misc/openvpn/Manifest
@@ -0,0 +1,8 @@
+AUX down.sh 943 RMD160 db43a525c9eb2ccb538e938e0b7f4359af22e4de SHA1 261acc68a24108526345a7d117bba15dbcebaa6e SHA256 39debebcd8c899f20e6d355cbc8eaab46e28b83a9f6c33a94c065688a4f3d2c7
+AUX openvpn-2.1.conf 892 RMD160 687a747ed2f801b051438d02da8fcd44c6954484 SHA1 e65db7d972483c9391ef92d2931d9db7b69e4329 SHA256 330149a83684ddabe413d134d4c8efad4c88b18c2ab67165014deff5f7fffad2
+AUX openvpn-2.1.init 4084 RMD160 1f815a77c3d89ee55d568e8dce582020fd68fc39 SHA1 811db5279b360f8d95bfbcd460a85bbf2c65ecbf SHA256 d844103cd67717ce505e191a3b4a1835b7fafbb856de7540791cf0e5fe017ec9
+AUX openvpn-2.1_rc6-udp6.patch 76842 RMD160 533ca831972ec759b39b1d0f94380091d2214242 SHA1 83caf4224d06d4ccd26e990fe0e2388e4a0e600a SHA256 0886a848f7fe52d0394bf51da814f9772ada6c14c682396a8e36085c1cd99c48
+AUX openvpn-2.1_rc7-tap.patch 1474 RMD160 a200e42f1fd224e9d0f02eb59ab6371224245715 SHA1 1453666b79208d528880b95e324900605172a420 SHA256 f26a2ccad67a23c863241ca64c110c36601d70a0e6e0d6d626c02d176c36a2db
+AUX up.sh 2560 RMD160 34c2f0845a2c3b2eebb20da5a5c316e81cd1ef5a SHA1 d7026eeb7586fb931e5f938ca00fcc63c63923ad SHA256 3a7cedba5909fa06d0eb36b3426f0e5bc21bdc28943beaaa241ab651b4388218
+DIST openvpn-2.1_rc7.tar.gz 787379 RMD160 67e80d7bfb554046c71c95ec15d478504057f903 SHA1 f9f57a8ef3af8ab51924784ad2aab5d5f5798581 SHA256 c07fcbc41465bf508caff70dcc4238b8c95e9b2f35bec7bf891f7164027ecbf8
+EBUILD openvpn-2.1_rc7.ebuild 4472 RMD160 8305d69c254db1c55edaad7459169f31c5b5f7b3 SHA1 0868cf478f1ff2cf32f0354a72af6e1026ad6e53 SHA256 c513ac09ccf925b04c91609a1fe588f4c5bbebfb1dead66f0c22ea4f759b80f3
diff --git a/net-misc/openvpn/files/down.sh b/net-misc/openvpn/files/down.sh
new file mode 100755
index 00000000..1c70db0e
--- /dev/null
+++ b/net-misc/openvpn/files/down.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+# Copyright (c) 2006-2007 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# Contributed by Roy Marples (uberlord@gentoo.org)
+
+# If we have a service specific script, run this now
+if [ -x /etc/openvpn/"${SVCNAME}"-down.sh ] ; then
+ /etc/openvpn/"${SVCNAME}"-down.sh "$@"
+fi
+
+# Restore resolv.conf to how it was
+if [ "${PEER_DNS}" != "no" ]; then
+ if [ -x /sbin/resolvconf ] ; then
+ /sbin/resolvconf -d "${dev}"
+ elif [ -e /etc/resolv.conf-"${dev}".sv ] ; then
+ # Important that we copy instead of move incase resolv.conf is
+ # a symlink and not an actual file
+ cp /etc/resolv.conf-"${dev}".sv /etc/resolv.conf
+ rm -f /etc/resolv.conf-"${dev}".sv
+ fi
+fi
+
+if [ -n "${SVCNAME}" ]; then
+ # Re-enter the init script to start any dependant services
+ if /etc/init.d/"${SVCNAME}" --quiet status ; then
+ export IN_BACKGROUND=true
+ /etc/init.d/"${SVCNAME}" --quiet stop
+ fi
+fi
+
+exit 0
+
+# vim: ts=4 :
diff --git a/net-misc/openvpn/files/openvpn-2.1.conf b/net-misc/openvpn/files/openvpn-2.1.conf
new file mode 100644
index 00000000..72510c34
--- /dev/null
+++ b/net-misc/openvpn/files/openvpn-2.1.conf
@@ -0,0 +1,18 @@
+# OpenVPN automatically creates an /etc/resolv.conf (or sends it to
+# resolvconf) if given DNS information by the OpenVPN server.
+# Set PEER_DNS="no" to stop this.
+PEER_DNS="yes"
+
+# OpenVPN can run in many modes. Most people will want the init script
+# to automatically detect the mode and try and apply a good default
+# configuration and setup scripts. However, there are cases where the
+# OpenVPN configuration looks like a client, but it's really a peer or
+# something else. DETECT_CLIENT controls this behaviour.
+DETECT_CLIENT="yes"
+
+# If DETECT_CLIENT is no and you have your own scripts to re-enter the openvpn
+# init script (ie, it first becomes "inactive" and the script then starts the
+# script again to make it "started") then you can state this below.
+# In other words, unless you understand service dependencies and are a
+# competent shell scripter, don't set this.
+RE_ENTER="no"
diff --git a/net-misc/openvpn/files/openvpn-2.1.init b/net-misc/openvpn/files/openvpn-2.1.init
new file mode 100755
index 00000000..633cae00
--- /dev/null
+++ b/net-misc/openvpn/files/openvpn-2.1.init
@@ -0,0 +1,131 @@
+#!/sbin/runscript
+# Copyright 1999-2007 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+VPNDIR=${VPNDIR:-/etc/openvpn}
+VPN=${SVCNAME#*.}
+if [ -n "${VPN}" ] && [ ${SVCNAME} != "openvpn" ]; then
+ VPNPID="/var/run/openvpn.${VPN}.pid"
+else
+ VPNPID="/var/run/openvpn.pid"
+fi
+VPNCONF="${VPNDIR}/${VPN}.conf"
+
+depend() {
+ need localmount net
+ use dns
+ after bootmisc
+}
+
+checkconfig() {
+ # Linux has good dynamic tun/tap creation
+ if [ $(uname -s) = "Linux" ] ; then
+ if [ ! -e /dev/net/tun ]; then
+ if ! modprobe tun ; then
+ eerror "TUN/TAP support is not available" \
+ "in this kernel"
+ return 1
+ fi
+ fi
+ if [ -h /dev/net/tun ] && [ -c /dev/misc/net/tun ]; then
+ ebegin "Detected broken /dev/net/tun symlink, fixing..."
+ rm -f /dev/net/tun
+ ln -s /dev/misc/net/tun /dev/net/tun
+ eend $?
+ fi
+ return 0
+ fi
+
+ # Other OS's don't, so we rely on a pre-configured interface
+ # per vpn instance
+ local ifname=$(sed -n -e 's/[[:space:]]*dev[[:space:]][[:space:]]*\([^[:space:]]*\).*/\1/p' "${VPNCONF}")
+ if [ -z ${ifname} ] ; then
+ eerror "You need to specify the interface that this openvpn" \
+ "instance should use" \
+ "by using the dev option in ${VPNCONF}"
+ return 1
+ fi
+
+ if ! ifconfig "${ifname}" >/dev/null 2>/dev/null ; then
+ # Try and create it
+ echo > /dev/"${ifname}" >/dev/null
+ fi
+ if ! ifconfig "${ifname}" >/dev/null 2>/dev/null ; then
+ eerror "${VPNCONF} requires interface ${ifname}" \
+ "but that does not exist"
+ return 1
+ fi
+}
+
+start() {
+ # If we are re-called by the openvpn gentoo-up.sh script
+ # then we don't actually want to start openvpn
+ [ "${IN_BACKGROUND}" = "true" ] && return 0
+
+ ebegin "Starting ${SVCNAME}"
+
+ checkconfig || return 1
+
+ local args="" reenter=${RE_ENTER:-no}
+ # If the config file does not specify the cd option, we do
+ # But if we specify it, we override the config option which we do not want
+ if ! grep -q "^[ \t]*cd[ \t].*" "${VPNCONF}" ; then
+ args="${args} --cd ${VPNDIR}"
+ fi
+
+ # We mark the service as inactive and then start it.
+ # When we get an authenticated packet from the peer then we run our script
+ # which configures our DNS if any and marks us as up.
+ if [ "${DETECT_CLIENT:-yes}" = "yes" ] && \
+ grep -q "^[ \t]*remote[ \t].*" "${VPNCONF}" ; then
+ reenter="yes"
+ args="${args} --nobind --up-delay --up-restart"
+ args="${args} --up /etc/openvpn/up.sh"
+ args="${args} --down-pre --down /etc/openvpn/down.sh"
+
+ # Warn about setting scripts as we override them
+ if grep -Eq "^[ \t]*(up|down)[ \t].*" "${VPNCONF}" ; then
+ ewarn "WARNING: You have defined your own up/down scripts"
+ ewarn "As you're running as a client, we now force Gentoo specific"
+ ewarn "scripts to be run for up and down events."
+ ewarn "These scripts will call /etc/openvpn/${SVCNAME}-{up,down}.sh"
+ ewarn "where you can put your own code."
+ fi
+
+ # Warn about the inability to change ip/route/dns information when
+ # dropping privs
+ if grep -q "^[ \t]*user[ \t].*" "${VPNCONF}" ; then
+ ewarn "WARNING: You are dropping root privileges!"
+ ewarn "As such openvpn may not be able to change ip, routing"
+ ewarn "or DNS configuration."
+ fi
+ else
+ # So we're a server. Run as openvpn unless otherwise specified
+ grep -q "^[ \t]*user[ \t].*" "${VPNCONF}" || args="${args} --user openvpn"
+ grep -q "^[ \t]*group[ \t].*" "${VPNCONF}" || args="${args} --group openvpn"
+ fi
+
+ # Ensure that our scripts get the PEER_DNS variable
+ export PEER_DNS
+
+ [ "${reenter}" = "yes" ] && mark_service_inactive "${SVCNAME}"
+ start-stop-daemon --start --exec /usr/sbin/openvpn --pidfile "${VPNPID}" \
+ -- --config "${VPNCONF}" --writepid "${VPNPID}" --daemon ${args}
+ eend $? "Check your logs to see why startup failed"
+}
+
+stop() {
+ # If we are re-called by the openvpn gentoo-down.sh script
+ # then we don't actually want to stop openvpn
+ if [ "${IN_BACKGROUND}" = "true" ] ; then
+ mark_service_inactive "${SVCNAME}"
+ return 0
+ fi
+
+ ebegin "Stopping ${SVCNAME}"
+ start-stop-daemon --stop --quiet \
+ --exec /usr/sbin/openvpn --pidfile "${VPNPID}"
+ eend $?
+}
+
+# vim: set ts=4 :
diff --git a/net-misc/openvpn/files/openvpn-2.1_rc6-udp6.patch b/net-misc/openvpn/files/openvpn-2.1_rc6-udp6.patch
new file mode 100644
index 00000000..842839e5
--- /dev/null
+++ b/net-misc/openvpn/files/openvpn-2.1_rc6-udp6.patch
@@ -0,0 +1,2521 @@
+diff --git a/jjo-tests/libtest.sh b/jjo-tests/libtest.sh
+new file mode 100755
+index 0000000..ab0d4b5
+--- /dev/null
++++ b/jjo-tests/libtest.sh
+@@ -0,0 +1,115 @@
++#!/bin/bash
++
++typeset -i test_num=0 # increments w/each test_define
++typeset test_msg # prefix: "test nr#" for msgs
++typeset test_name # test name given
++typeset test_sanename # test name with all whitespace (and alike) replaced by '_'
++
++TEST_CLEANUP=":"
++
++say () {
++ echo "$@" >&3
++}
++err () {
++ echo "$@" >&4
++}
++debug () {
++ echo "$@" >&4
++}
++test_define() {
++ test_name="$*"
++ test_sanename="$(echo -n $test_name| tr -c '[A-Za-z0-9._]' _ )"
++ test_num=test_num+1
++ test_msg="test $test_num"
++ test_bg_cleanup
++}
++test_expect_success () {
++ local msg="$1"
++ test $# -ge 2 || { err "usage error: test_expect_failure msg cmd args ..."; return 1; }
++ say -n "$test_msg: -- $msg (expecting success) "
++ shift
++ (eval "$@" 4>&1) && { say -e "\n$test_msg: OK %%% $test_sanename" ; return 0 ; }
++ say -e "\n$test_msg: FAILED %%% $test_sanename"
++ return 1
++}
++
++test_expect_failure () {
++ local msg="$1"
++ test $# -ge 2 || { err "usage error: test_expect_failure msg cmd args ..."; return 1;}
++ say -n "$test_msg: -- $msg (expecting failure) "
++ shift
++ (eval "$@") && { say -e "\n$test_msg: FAILED %%% $test_sanename" >&3 ; return 0 ; }
++ say -e "\n$test_msg: OK %%% $test_sanename"
++ return 1
++}
++
++test_bg_egrep() {
++ local nsecs="$1"
++ local txt="$2"
++ local ret
++ shift 2
++ test_expect_success "$test_name" \
++ "set -m ; $@ &> $t/out-$test_sanename &
++ s=1;
++ for i in \$(seq 1 $nsecs);do
++ say -n '.'
++ kill -0 %1 || { egrep failed $t/out-$test_sanename >&4; break; }
++ o=\$(egrep \"$txt\" $t/out-$test_sanename ) && { debug \$o; s=0; break; }
++ sleep 1;
++ done;
++ kill %1
++ wait
++ exit \$s
++ " 2>/dev/null 4>$t/err
++ ret=$?
++ test $ret -eq 0 && return 0
++ say "$test_msg: see: $t/out-$test_sanename*"
++ return $ret
++}
++# run command in *current* shell ignoring stderr, evals args passed
++quiet2() {
++ #backup fd=2
++ exec 250>&2
++ exec 2>/dev/null
++ eval "$@"
++ #close auxfd
++ exec 2>&250
++ exec 250>&-
++}
++test_bg_prev(){ local out=$t/out-$test_sanename-prev; $@ >& $out & }
++test_bg_cleanup() { quiet2 "$TEST_CLEANUP;${@:-:};kill % 2>/dev/null;wait" ;}
++test_set_cleanup () { TEST_CLEANUP="${*:-:}" ;}
++
++t=/tmp
++exec 3>&1
++exec 4>&2
++
++#=========
++
++ln -sf $PWD/openvpn $t/openvpn-test
++export OPENVPN=$t/openvpn-test
++
++test_set_cleanup "killall $OPENVPN"
++
++trap 'test_bg_cleanup;exit' 0 2 15
++
++test_define "UDP6 loopback"
++test_bg_egrep 30 "Initialization Sequence Completed" ../jjo-tests/run-udp6-0-loopback-self.sh
++
++test_define "UDP6 loopback byname"
++test_bg_egrep 30 "Initialization Sequence Completed" ../jjo-tests/run-udp6-0-loopback-byname.sh
++
++test_define "TCP6 loopback"
++test_bg_prev ../jjo-tests/run-tcp6-0-loopback-server.sh
++test_bg_egrep 30 "Initialization Sequence Completed" ../jjo-tests/run-tcp6-0-loopback-client.sh
++
++test_define "TCP6 loopback byname"
++test_bg_prev ../jjo-tests/run-tcp6-0-loopback-server-byname.sh
++test_bg_egrep 30 "Initialization Sequence Completed" ../jjo-tests/run-tcp6-0-loopback-client-byname.sh
++
++test_define "UDP4 loopback"
++test_bg_egrep 30 "Initialization Sequence Completed" ../jjo-tests/run-udp4-0-loopback-self.sh
++
++test_define "TCP4 loopback"
++test_bg_prev ../jjo-tests/run-tcp4-0-loopback-server.sh
++test_bg_egrep 30 "Initialization Sequence Completed" ../jjo-tests/run-tcp4-0-loopback-client.sh
+diff --git a/jjo-tests/qemu-ifup-2.jjo.sh b/jjo-tests/qemu-ifup-2.jjo.sh
+new file mode 100755
+index 0000000..60418ad
+--- /dev/null
++++ b/jjo-tests/qemu-ifup-2.jjo.sh
+@@ -0,0 +1,12 @@
++#!/bin/sh -x
++#sudo sh -c "/sbin/ifconfig $1 hw ether 00:00:00:00:00:01; /sbin/ifconfig $1 192.168.254.1"
++export PATH=/sbin:/usr/sbin:$PATH
++NUM=2
++DEV=qemutun$NUM
++sudo sh -x <<EOF
++ip li set $1 name $DEV
++ip li set $DEV address 00:00:00:00:00:01
++ip li set $DEV up
++ip ad add 192.168.254.1 dev $DEV
++ip ro add 192.168.254.$NUM dev $DEV
++EOF
+diff --git a/jjo-tests/qemu-ifup-3.jjo.sh b/jjo-tests/qemu-ifup-3.jjo.sh
+new file mode 100755
+index 0000000..56c2f33
+--- /dev/null
++++ b/jjo-tests/qemu-ifup-3.jjo.sh
+@@ -0,0 +1,12 @@
++#!/bin/sh -x
++#sudo sh -c "/sbin/ifconfig $1 hw ether 00:00:00:00:00:01; /sbin/ifconfig $1 192.168.254.1"
++export PATH=/sbin:/usr/sbin:$PATH
++NUM=3
++DEV=qemutun$NUM
++sudo sh -x <<EOF
++ip li set $1 name $DEV
++ip li set $DEV address 00:00:00:00:00:01
++ip li set $DEV up
++ip ad add 192.168.254.1 dev $DEV
++ip ro add 192.168.254.$NUM dev $DEV
++EOF
+diff --git a/jjo-tests/run-afunix-0-loop.sh b/jjo-tests/run-afunix-0-loop.sh
+new file mode 100755
+index 0000000..ed50691
+--- /dev/null
++++ b/jjo-tests/run-afunix-0-loop.sh
+@@ -0,0 +1,4 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++rm -fv /tmp/o.s
++${OPENVPN} --proto unix-dgram --local /tmp/o.s --remote /tmp/o.s --dev tun --ifconfig 1.1.1.1 1.1.1.2 --secret ../openvpn.key $*
+diff --git a/jjo-tests/run-afunix-1-o1.sh b/jjo-tests/run-afunix-1-o1.sh
+new file mode 100755
+index 0000000..3f1df0f
+--- /dev/null
++++ b/jjo-tests/run-afunix-1-o1.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${OPENVPN} --proto unix-dgram --local /tmp/o1.s --remote /tmp/o2.s --dev tun --ifconfig 1.1.1.1 1.1.1.2 --secret ../openvpn.key $*
+diff --git a/jjo-tests/run-afunix-1-o2.sh b/jjo-tests/run-afunix-1-o2.sh
+new file mode 100755
+index 0000000..6e94213
+--- /dev/null
++++ b/jjo-tests/run-afunix-1-o2.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${OPENVPN} --proto unix-dgram --local /tmp/o2.s --remote /tmp/o1.s --dev tun --ifconfig 1.1.1.2 1.1.1.1 --secret ../openvpn.key $*
+diff --git a/jjo-tests/run-oxg-1.sh b/jjo-tests/run-oxg-1.sh
+new file mode 100755
+index 0000000..21fd02f
+--- /dev/null
++++ b/jjo-tests/run-oxg-1.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${OPENVPN} --dev tun --proto udp --remote localhost --lport 5020 --rport 1194 --secret ../openvpn.key --ifconfig 1.1.1.1 1.1.1.2 "$@"
+diff --git a/jjo-tests/run-oxg-2.sh b/jjo-tests/run-oxg-2.sh
+new file mode 100755
+index 0000000..aea6345
+--- /dev/null
++++ b/jjo-tests/run-oxg-2.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${OPENVPN} --dev tun --proto udp --remote localhost --lport 5020 --rport 1194 --secret ../openvpn.key --ifconfig 1.1.1.2 1.1.1.1 "$@"
+diff --git a/jjo-tests/run-tcp4-0-loopback-client.sh b/jjo-tests/run-tcp4-0-loopback-client.sh
+new file mode 100755
+index 0000000..956d437
+--- /dev/null
++++ b/jjo-tests/run-tcp4-0-loopback-client.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${OPENVPN} --dev null --proto tcp-client --remote localhost --rport 5011 --secret ../openvpn.key "$@"
+diff --git a/jjo-tests/run-tcp4-0-loopback-server.sh b/jjo-tests/run-tcp4-0-loopback-server.sh
+new file mode 100755
+index 0000000..0cab380
+--- /dev/null
++++ b/jjo-tests/run-tcp4-0-loopback-server.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${OPENVPN} --dev null --proto tcp-server --remote localhost --lport 5011 --secret ../openvpn.key "$@"
+diff --git a/jjo-tests/run-tcp6-0-loopback-client-byname.sh b/jjo-tests/run-tcp6-0-loopback-client-byname.sh
+new file mode 100755
+index 0000000..f8b2cdd
+--- /dev/null
++++ b/jjo-tests/run-tcp6-0-loopback-client-byname.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${OPENVPN} --dev null --proto tcp6-client --remote ip6-localhost --rport 5011 --secret ../openvpn.key "$@"
+diff --git a/jjo-tests/run-tcp6-0-loopback-client.sh b/jjo-tests/run-tcp6-0-loopback-client.sh
+new file mode 100755
+index 0000000..09851c5
+--- /dev/null
++++ b/jjo-tests/run-tcp6-0-loopback-client.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${OPENVPN} --dev null --proto tcp6-client --remote ::1 --rport 5011 --secret ../openvpn.key "$@"
+diff --git a/jjo-tests/run-tcp6-0-loopback-server-byname.sh b/jjo-tests/run-tcp6-0-loopback-server-byname.sh
+new file mode 100755
+index 0000000..8fead3c
+--- /dev/null
++++ b/jjo-tests/run-tcp6-0-loopback-server-byname.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${OPENVPN} --dev null --proto tcp6-server --local ip6-localhost --lport 5011 --secret ../openvpn.key "$@"
+diff --git a/jjo-tests/run-tcp6-0-loopback-server.sh b/jjo-tests/run-tcp6-0-loopback-server.sh
+new file mode 100755
+index 0000000..ad36555
+--- /dev/null
++++ b/jjo-tests/run-tcp6-0-loopback-server.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${OPENVPN} --dev null --proto tcp6-server --local :: --lport 5011 --secret ../openvpn.key "$@"
+diff --git a/jjo-tests/run-udp4-0-loopback-self.sh b/jjo-tests/run-udp4-0-loopback-self.sh
+new file mode 100755
+index 0000000..7a29a3d
+--- /dev/null
++++ b/jjo-tests/run-udp4-0-loopback-self.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${GDB} ${OPENVPN} --dev null --proto udp --remote localhost --port 5011 --secret ../openvpn.key "$@"
+diff --git a/jjo-tests/run-udp4-1-qemu-guest.sh b/jjo-tests/run-udp4-1-qemu-guest.sh
+new file mode 100755
+index 0000000..adcb641
+--- /dev/null
++++ b/jjo-tests/run-udp4-1-qemu-guest.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${GDB} ${OPENVPN} --dev tun --proto udp --remote 10.0.2.2 --port 5011 --secret ../openvpn.key --ifconfig 1.1.1.253 1.1.1.1
+diff --git a/jjo-tests/run-udp4-1-qemu-host.sh b/jjo-tests/run-udp4-1-qemu-host.sh
+new file mode 100755
+index 0000000..8db45f2
+--- /dev/null
++++ b/jjo-tests/run-udp4-1-qemu-host.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${GDB} ${OPENVPN} --dev tun --proto udp --float --port 5011 --secret ../openvpn.key --ifconfig 1.1.1.1 1.1.1.253
+diff --git a/jjo-tests/run-udp4-2-MH.sh b/jjo-tests/run-udp4-2-MH.sh
+new file mode 100755
+index 0000000..5358fbc
+--- /dev/null
++++ b/jjo-tests/run-udp4-2-MH.sh
+@@ -0,0 +1,4 @@
++#!/bin/sh -x
++#pass --local ADDR --remote ADDR
++: ${OPENVPN:=./openvpn}
++${GDB} ${OPENVPN} --dev tun --proto udp --port 5011 --secret ../openvpn.key --ifconfig-noexec "$@"
+diff --git a/jjo-tests/run-udp6-0-loopback-byname.sh b/jjo-tests/run-udp6-0-loopback-byname.sh
+new file mode 100755
+index 0000000..993eec9
+--- /dev/null
++++ b/jjo-tests/run-udp6-0-loopback-byname.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${GDB} ${OPENVPN} --dev null --proto udp6 --remote ip6-localhost --port 5011 --secret ../openvpn.key "$@"
+diff --git a/jjo-tests/run-udp6-0-loopback-self.sh b/jjo-tests/run-udp6-0-loopback-self.sh
+new file mode 100755
+index 0000000..469bc39
+--- /dev/null
++++ b/jjo-tests/run-udp6-0-loopback-self.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${GDB} ${OPENVPN} --dev null --proto udp6 --remote ::1 --port 5011 --secret ../openvpn.key "$@"
+diff --git a/jjo-tests/run-udp6-1-carpediem.sh b/jjo-tests/run-udp6-1-carpediem.sh
+new file mode 100755
+index 0000000..bcce35e
+--- /dev/null
++++ b/jjo-tests/run-udp6-1-carpediem.sh
+@@ -0,0 +1,4 @@
++#!/bin/sh -x
++REM6="fe80::2e0:7dff:fee3:ef17"
++: ${OPENVPN:=./openvpn}
++${GDB} ${OPENVPN} --dev tun --proto udp6 --remote "$REM6" --port 5010 --secret ../openvpn.key --ifconfig 1.1.1.253 1.1.1.1
+diff --git a/jjo-tests/run-udp6-1-cx.sh b/jjo-tests/run-udp6-1-cx.sh
+new file mode 100755
+index 0000000..ab69ea8
+--- /dev/null
++++ b/jjo-tests/run-udp6-1-cx.sh
+@@ -0,0 +1,3 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++${GDB} ${OPENVPN} --verb 5 --dev tun --proto udp6 --port 5010 --secret /home/jjo/src/juanjo/openvpn/openvpn.key --float --ifconfig 1.1.1.1 1.1.1.253
+diff --git a/jjo-tests/run-udp6-2-qemu-guest.sh b/jjo-tests/run-udp6-2-qemu-guest.sh
+new file mode 100755
+index 0000000..0e280a7
+--- /dev/null
++++ b/jjo-tests/run-udp6-2-qemu-guest.sh
+@@ -0,0 +1,8 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++case "$1" in
++--freebsd) shift;set -- ed0 "$@";;
++--openbsd) shift;set -- ne3 --dev tun0 "$@";;
++esac
++DEV=${1:?missing devname};shift
++${GDB} ${OPENVPN} --dev tun --proto udp6 --remote fe80::200:ff:fe00:1%$DEV --port 5010 --secret ../openvpn.key "$@" --ifconfig 1.1.1.253 1.1.1.1
+diff --git a/jjo-tests/run-udp6-2-qemu-host.sh b/jjo-tests/run-udp6-2-qemu-host.sh
+new file mode 100755
+index 0000000..500f0d3
+--- /dev/null
++++ b/jjo-tests/run-udp6-2-qemu-host.sh
+@@ -0,0 +1,4 @@
++#!/bin/sh -x
++: ${OPENVPN:=./openvpn}
++DEV=${1:?missing devname};shift
++${GDB} ${OPENVPN} --dev tun --proto udp6 --remote fe80::5054:ff:fe12:3456%$DEV --port 5010 --secret ../openvpn.key --ifconfig 1.1.1.1 1.1.1.253 "$@"
+diff --git a/openvpn/buffer.c b/openvpn/buffer.c
+index 64307ef..0650745 100644
+--- a/openvpn/buffer.c
++++ b/openvpn/buffer.c
+@@ -198,6 +198,19 @@ buf_printf (struct buffer *buf, const ch
+ }
+ }
+
++void buf_puts(struct buffer *buf, const char *str)
++{
++ uint8_t *ptr = BEND (buf);
++ int cap = buf_forward_capacity (buf);
++ if (cap > 0)
++ {
++ strncpynt ((char *)ptr,str, cap);
++ *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */
++ buf->len += (int) strlen ((char *)ptr);
++ }
++}
++
++
+ /*
+ * This is necessary due to certain buggy implementations of snprintf,
+ * that don't guarantee null termination for size > 0.
+diff --git a/openvpn/configure.ac b/openvpn/configure.ac
+index 8843a2c..8cea204 100644
+--- a/openvpn/configure.ac
++++ b/openvpn/configure.ac
+@@ -101,6 +101,12 @@ AC_ARG_ENABLE(multihome,
+ [MULTIHOME="yes"]
+ )
+
++AC_ARG_ENABLE(ipv6,
++ [ --disable-ipv6 Disable UDP/IPv6 support],
++ [PF_INET6="$enableval"],
++ [PF_INET6="yes"]
++)
++
+ AC_ARG_ENABLE(port-share,
+ [ --disable-port-share Disable TCP server port-share support (--port-share)],
+ [PORT_SHARE="$enableval"],
+@@ -406,6 +412,16 @@ LDFLAGS="$LDFLAGS -Wl,--fatal-warnings"
+ AC_CHECK_FUNCS(epoll_create, AC_DEFINE([HAVE_EPOLL_CREATE], 1, []))
+ LDFLAGS="$OLDLDFLAGS"
+
++dnl ipv6 support
++if test "$PF_INET6" = "yes"; then
++ AC_CHECKING([for struct sockaddr_in6 for IPv6 support])
++ AC_CHECK_TYPE(
++ [struct sockaddr_in6],
++ [AC_DEFINE(USE_PF_INET6, 1, [struct sockaddr_in6 is needed for IPv6 peer support])],
++ [],
++ [#include "syshead.h"])
++fi
++
+ dnl
+ dnl check for valgrind tool
+ dnl
+diff --git a/openvpn/helper.c b/openvpn/helper.c
+index d2b3d6f..459f00d 100644
+--- a/openvpn/helper.c
++++ b/openvpn/helper.c
+@@ -358,6 +358,10 @@ #endif /* P2MP_SERVER */
+
+ if (o->proto == PROTO_TCPv4)
+ o->proto = PROTO_TCPv4_CLIENT;
++#ifdef USE_PF_INET6
++ else if (o->proto == PROTO_TCPv6)
++ o->proto = PROTO_TCPv6_CLIENT;
++#endif
+ }
+
+ #endif /* P2MP */
+diff --git a/openvpn/init.c b/openvpn/init.c
+index 244f03c..0d929e4 100644
+--- a/openvpn/init.c
++++ b/openvpn/init.c
+@@ -703,7 +703,7 @@ #ifdef ENABLE_MANAGEMENT
+ const char *detail = "SUCCESS";
+ if (c->c1.tuntap)
+ tun_local = c->c1.tuntap->local;
+- tun_remote = htonl (c->c1.link_socket_addr.actual.dest.sa.sin_addr.s_addr);
++ tun_remote = htonl (c->c1.link_socket_addr.actual.dest.addr.in4.sin_addr.s_addr);
+ if (flags & ISC_ERRORS)
+ detail = "ERROR";
+ management_set_state (management,
+@@ -1105,7 +1105,7 @@ do_deferred_options (struct context *c,
+ #ifdef ENABLE_OCC
+ if (found & OPT_P_EXPLICIT_NOTIFY)
+ {
+- if (c->options.proto != PROTO_UDPv4 && c->options.explicit_exit_notification)
++ if (!proto_is_udp(c->options.proto) && c->options.explicit_exit_notification)
+ {
+ msg (D_PUSH, "OPTIONS IMPORT: --explicit-exit-notify can only be used with --proto udp");
+ c->options.explicit_exit_notification = 0;
+@@ -1200,12 +1200,21 @@ #endif
+ switch (c->options.proto)
+ {
+ case PROTO_UDPv4:
++#ifdef USE_PF_INET6
++ case PROTO_UDPv6:
++#endif
+ if (proxy)
+ sec = c->options.connect_retry_seconds;
+ break;
++#ifdef USE_PF_INET6
++ case PROTO_TCPv6_SERVER:
++#endif
+ case PROTO_TCPv4_SERVER:
+ sec = 1;
+ break;
++#ifdef USE_PF_INET6
++ case PROTO_TCPv6_CLIENT:
++#endif
+ case PROTO_TCPv4_CLIENT:
+ sec = c->options.connect_retry_seconds;
+ break;
+@@ -2292,7 +2301,7 @@ do_setup_fast_io (struct context *c)
+ #ifdef WIN32
+ msg (M_INFO, "NOTE: --fast-io is disabled since we are running on Windows");
+ #else
+- if (c->options.proto != PROTO_UDPv4)
++ if (!proto_is_udp(c->options.proto))
+ msg (M_INFO, "NOTE: --fast-io is disabled since we are not using UDP");
+ else
+ {
+@@ -2549,7 +2558,11 @@ init_instance (struct context *c, const
+ /* link_socket_mode allows CM_CHILD_TCP
+ instances to inherit acceptable fds
+ from a top-level parent */
++#ifdef USE_PF_INET6
++ if (c->options.proto == PROTO_TCPv4_SERVER || c->options.proto == PROTO_TCPv6_SERVER)
++#else
+ if (c->options.proto == PROTO_TCPv4_SERVER)
++#endif
+ {
+ if (c->mode == CM_TOP)
+ link_socket_mode = LS_MODE_TCP_LISTEN;
+@@ -2809,17 +2822,7 @@ inherit_context_child (struct context *d
+ {
+ CLEAR (*dest);
+
+- switch (src->options.proto)
+- {
+- case PROTO_UDPv4:
+- dest->mode = CM_CHILD_UDP;
+- break;
+- case PROTO_TCPv4_SERVER:
+- dest->mode = CM_CHILD_TCP;
+- break;
+- default:
+- ASSERT (0);
+- }
++ dest->mode = proto_is_dgram(src->options.proto)? CM_CHILD_UDP : CM_CHILD_TCP;
+
+ dest->gc = gc_new ();
+
+@@ -2924,7 +2927,7 @@ #endif
+ dest->c2.buffers_owned = false;
+
+ dest->c2.event_set = NULL;
+- if (src->options.proto == PROTO_UDPv4)
++ if (proto_is_dgram(src->options.proto))
+ do_event_set_init (dest, false);
+ }
+
+diff --git a/openvpn/manage.c b/openvpn/manage.c
+index 993afa2..2615137 100644
+--- a/openvpn/manage.c
++++ b/openvpn/manage.c
+@@ -1351,9 +1351,9 @@ man_settings_init (struct man_settings *
+ /*
+ * Initialize socket address
+ */
+- ms->local.sa.sin_family = AF_INET;
+- ms->local.sa.sin_addr.s_addr = 0;
+- ms->local.sa.sin_port = htons (port);
++ ms->local.addr.in4.sin_family = AF_INET;
++ ms->local.addr.in4.sin_addr.s_addr = 0;
++ ms->local.addr.in4.sin_port = htons (port);
+
+ /*
+ * Run management over tunnel, or
+@@ -1365,7 +1365,7 @@ man_settings_init (struct man_settings *
+ }
+ else
+ {
+- ms->local.sa.sin_addr.s_addr = getaddr
++ ms->local.addr.in4.sin_addr.s_addr = getaddr
+ (GETADDR_RESOLVE|GETADDR_WARN_ON_SIGNAL|GETADDR_FATAL, addr, 0, NULL, NULL);
+ }
+
+@@ -1634,7 +1634,7 @@ management_post_tunnel_open (struct mana
+ && man->connection.state == MS_INITIAL)
+ {
+ /* listen on our local TUN/TAP IP address */
+- man->settings.local.sa.sin_addr.s_addr = htonl (tun_local_ip);
++ man->settings.local.addr.in4.sin_addr.s_addr = htonl (tun_local_ip);
+ man_connection_init (man);
+ }
+
+diff --git a/openvpn/mroute.c b/openvpn/mroute.c
+index a7d1215..8d85518 100644
+--- a/openvpn/mroute.c
++++ b/openvpn/mroute.c
+@@ -175,25 +175,47 @@ bool mroute_extract_openvpn_sockaddr (st
+ const struct openvpn_sockaddr *osaddr,
+ bool use_port)
+ {
+- if (osaddr->sa.sin_family == AF_INET)
++ switch (osaddr->addr.sa.sa_family)
++ {
++ case AF_INET:
+ {
+ if (use_port)
+ {
+ addr->type = MR_ADDR_IPV4 | MR_WITH_PORT;
+ addr->netbits = 0;
+ addr->len = 6;
+- memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
+- memcpy (addr->addr + 4, &osaddr->sa.sin_port, 2);
++ memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4);
++ memcpy (addr->addr + 4, &osaddr->addr.in4.sin_port, 2);
+ }
+ else
+ {
+ addr->type = MR_ADDR_IPV4;
+ addr->netbits = 0;
+ addr->len = 4;
+- memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
++ memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4);
+ }
+ return true;
+ }
++#ifdef USE_PF_INET6
++ case AF_INET6:
++ if (use_port)
++ {
++ addr->type = MR_ADDR_IPV6 | MR_WITH_PORT;
++ addr->netbits = 0;
++ addr->len = 18;
++ memcpy (addr->addr, &osaddr->addr.in6.sin6_addr, 16);
++ memcpy (addr->addr + 16, &osaddr->addr.in6.sin6_port, 2);
++ }
++ else
++ {
++ addr->type = MR_ADDR_IPV6;
++ addr->netbits = 0;
++ addr->len = 16;
++ memcpy (addr->addr, &osaddr->addr.in6.sin6_addr, 16);
++ }
++ return true;
++#endif
++ }
+ return false;
+ }
+
+@@ -268,7 +290,37 @@ mroute_addr_print (const struct mroute_a
+ }
+ break;
+ case MR_ADDR_IPV6:
++#ifdef USE_PF_INET6
++ {
++ struct buffer buf;
++ struct sockaddr_in6 sin6;
++ int port;
++ char buf6[INET6_ADDRSTRLEN] = "";
++ memset(&sin6, 0, sizeof sin6);
++ sin6.sin6_family = AF_INET6;
++ buf_set_read (&buf, maddr.addr, maddr.len);
++ if (buf_read(&buf, &sin6.sin6_addr, sizeof (sin6.sin6_addr)))
++ {
++ if (getnameinfo((struct sockaddr *)&sin6, sizeof (struct sockaddr_in6),
++ buf6, sizeof (buf6), NULL, 0, NI_NUMERICHOST) != 0)
++ {
++ buf_printf (&out, "MR_ADDR_IPV6 getnameinfo() err");
++ break;
++ }
++ buf_puts (&out, buf6);
++ if (maddr.type & MR_WITH_NETBITS)
++ buf_printf (&out, "/%d", maddr.netbits);
++ if (maddr.type & MR_WITH_PORT)
++ {
++ port = buf_read_u16 (&buf);
++ if (port >= 0)
++ buf_printf (&out, ":%d", port);
++ }
++ }
++ }
++#else /* old pre IPV6 1-line code: */
+ buf_printf (&out, "IPV6");
++#endif
+ break;
+ default:
+ buf_printf (&out, "UNKNOWN");
+diff --git a/openvpn/mtcp.c b/openvpn/mtcp.c
+index 6ec9974..5011688 100644
+--- a/openvpn/mtcp.c
++++ b/openvpn/mtcp.c
+@@ -159,6 +159,7 @@ multi_tcp_instance_specific_init (struct
+ ASSERT (mi->context.c2.link_socket);
+ ASSERT (mi->context.c2.link_socket->info.lsa);
+ ASSERT (mi->context.c2.link_socket->mode == LS_MODE_TCP_ACCEPT_FROM);
++ ASSERT (mi->context.c2.link_socket->info.lsa->actual.dest.addr.sa.sa_family == AF_INET);
+ if (!mroute_extract_openvpn_sockaddr (&mi->real, &mi->context.c2.link_socket->info.lsa->actual.dest, true))
+ {
+ msg (D_MULTI_ERRORS, "MULTI TCP: TCP client address is undefined");
+diff --git a/openvpn/multi.c b/openvpn/multi.c
+index ee74a46..bfd79b3 100644
+--- a/openvpn/multi.c
++++ b/openvpn/multi.c
+@@ -990,8 +990,8 @@ multi_learn_in_addr_t (struct multi_cont
+ struct mroute_addr addr;
+
+ CLEAR (remote_si);
+- remote_si.sa.sin_family = AF_INET;
+- remote_si.sa.sin_addr.s_addr = htonl (a);
++ remote_si.addr.in4.sin_family = AF_INET;
++ remote_si.addr.in4.sin_addr.s_addr = htonl (a);
+ ASSERT (mroute_extract_openvpn_sockaddr (&addr, &remote_si, false));
+
+ if (netbits >= 0)
+@@ -2224,9 +2224,9 @@ management_callback_kill_by_addr (void *
+ int count = 0;
+
+ CLEAR (saddr);
+- saddr.sa.sin_family = AF_INET;
+- saddr.sa.sin_addr.s_addr = htonl (addr);
+- saddr.sa.sin_port = htons (port);
++ saddr.addr.in4.sin_family = AF_INET;
++ saddr.addr.in4.sin_addr.s_addr = htonl (addr);
++ saddr.addr.in4.sin_port = htons (port);
+ if (mroute_extract_openvpn_sockaddr (&maddr, &saddr, true))
+ {
+ hash_iterator_init (m->iter, &hi, true);
+@@ -2287,18 +2287,13 @@ tunnel_server (struct context *top)
+ {
+ ASSERT (top->options.mode == MODE_SERVER);
+
+- switch (top->options.proto) {
+- case PROTO_UDPv4:
+- tunnel_server_udp (top);
+- break;
+- case PROTO_TCPv4_SERVER:
+- tunnel_server_tcp (top);
+- break;
+- default:
+- ASSERT (0);
+- }
++ if (proto_is_dgram(top->options.proto))
++ tunnel_server_udp(top);
++ else
++ tunnel_server_tcp(top);
+ }
+
++
+ #else
+ static void dummy(void) {}
+ #endif /* P2MP_SERVER */
+diff --git a/openvpn/occ.c b/openvpn/occ.c
+index 6b136dc..3ec7784 100644
+--- a/openvpn/occ.c
++++ b/openvpn/occ.c
+@@ -375,7 +375,7 @@ process_received_occ_msg (struct context
+ c->c2.max_send_size_remote,
+ c->c2.max_recv_size_local);
+ if (!c->options.fragment
+- && c->options.proto == PROTO_UDPv4
++ && (proto_is_dgram(c->options.proto))
+ && c->c2.max_send_size_local > TUN_MTU_MIN
+ && (c->c2.max_recv_size_remote < c->c2.max_send_size_local
+ || c->c2.max_recv_size_local < c->c2.max_send_size_remote))
+diff --git a/openvpn/options.c b/openvpn/options.c
+index 94da98e..1490009 100644
+--- a/openvpn/options.c
++++ b/openvpn/options.c
+@@ -73,6 +73,12 @@ #endif
+ #ifdef USE_PTHREAD
+ " [PTHREAD]"
+ #endif
++#ifdef ENABLE_IP_PKTINFO
++ " [MH]"
++#endif
++#ifdef USE_PF_INET6
++ " [PF_INET6]"
++#endif
+ " built on " __DATE__
+ ;
+
+@@ -93,6 +99,9 @@ static const char usage_message[] =
+ "--mode m : Major mode, m = 'p2p' (default, point-to-point) or 'server'.\n"
+ "--proto p : Use protocol p for communicating with peer.\n"
+ " p = udp (default), tcp-server, or tcp-client\n"
++#ifdef USE_PF_INET6
++ " p = udp6, tcp6-server, or tcp6-client (IPv6)\n"
++#endif
+ "--connect-retry n : For --proto tcp-client, number of seconds to wait\n"
+ " between connection retries (default=%d).\n"
+ "--connect-timeout n : For --proto tcp-client, connection timeout (in seconds).\n"
+@@ -1421,10 +1430,10 @@ #endif
+ * Sanity check on TCP mode options
+ */
+
+- if (options->connect_retry_defined && options->proto != PROTO_TCPv4_CLIENT)
++ if (options->connect_retry_defined && options->proto != PROTO_TCPv4_CLIENT && options->proto != PROTO_TCPv6_CLIENT)
+ msg (M_USAGE, "--connect-retry doesn't make sense unless also used with --proto tcp-client");
+
+- if (options->connect_timeout_defined && options->proto != PROTO_TCPv4_CLIENT)
++ if (options->connect_timeout_defined && options->proto != PROTO_TCPv4_CLIENT && options->proto != PROTO_TCPv6_CLIENT)
+ msg (M_USAGE, "--connect-timeout doesn't make sense unless also used with --proto tcp-client");
+
+ /*
+@@ -1434,7 +1443,7 @@ #endif
+ msg (M_USAGE, "only one of --tun-mtu or --link-mtu may be defined (note that --ifconfig implies --link-mtu %d)", LINK_MTU_DEFAULT);
+
+ #ifdef ENABLE_OCC
+- if (options->proto != PROTO_UDPv4 && options->mtu_test)
++ if (!proto_is_udp(options->proto) && options->mtu_test)
+ msg (M_USAGE, "--mtu-test only makes sense with --proto udp");
+ #endif
+
+@@ -1479,7 +1488,8 @@ #endif
+ const char *remote = l->array[i].hostname;
+ const int remote_port = l->array[i].port;
+
+- if (string_defined_equal (options->local, remote)
++ if (proto_is_net(options->proto)
++ && string_defined_equal (options->local, remote)
+ && options->local_port == remote_port)
+ msg (M_USAGE, "--remote and --local addresses are the same");
+
+@@ -1508,7 +1518,7 @@ #endif
+ if (!options->remote_list && !options->bind_local)
+ msg (M_USAGE, "--nobind doesn't make sense unless used with --remote");
+
+- if (options->proto == PROTO_TCPv4_CLIENT && !options->local && !options->local_port_defined && !options->bind_defined)
++ if ((options->proto == PROTO_TCPv4_CLIENT||options->proto == PROTO_TCPv6_CLIENT) && !options->local && !options->local_port_defined && !options->bind_defined)
+ options->bind_local = false;
+
+ #ifdef ENABLE_SOCKS
+@@ -1565,16 +1575,16 @@ #endif
+ */
+
+ #ifdef ENABLE_FRAGMENT
+- if (options->proto != PROTO_UDPv4 && options->fragment)
++ if (!proto_is_udp(options->proto) && options->fragment)
+ msg (M_USAGE, "--fragment can only be used with --proto udp");
+ #endif
+
+ #ifdef ENABLE_OCC
+- if (options->proto != PROTO_UDPv4 && options->explicit_exit_notification)
++ if (!proto_is_udp(options->proto) && options->explicit_exit_notification)
+ msg (M_USAGE, "--explicit-exit-notify can only be used with --proto udp");
+ #endif
+
+- if (!options->remote_list && options->proto == PROTO_TCPv4_CLIENT)
++ if (!options->remote_list && (options->proto == PROTO_TCPv4_CLIENT||options->proto == PROTO_TCPv6_CLIENT))
+ msg (M_USAGE, "--remote MUST be used in TCP Client mode");
+
+ #ifdef ENABLE_HTTP_PROXY
+@@ -1592,7 +1602,8 @@ #ifdef ENABLE_SOCKS
+ msg (M_USAGE, "--socks-proxy can not be used in TCP Server mode");
+ #endif
+
+- if (options->proto == PROTO_TCPv4_SERVER && remote_list_len (options->remote_list) > 1)
++ if ((options->proto == PROTO_TCPv4_SERVER||options->proto == PROTO_TCPv6_SERVER)
++ && remote_list_len (options->remote_list) > 1)
+ msg (M_USAGE, "TCP server mode allows at most one --remote address");
+
+ #if P2MP_SERVER
+@@ -1617,7 +1628,7 @@ #endif
+ msg (M_USAGE, "--mode server only works with --dev tun or --dev tap");
+ if (options->pull)
+ msg (M_USAGE, "--pull cannot be used with --mode server");
+- if (!(options->proto == PROTO_UDPv4 || options->proto == PROTO_TCPv4_SERVER))
++ if (!(proto_is_udp(options->proto) || options->proto == PROTO_TCPv4_SERVER || options->proto == PROTO_TCPv6_SERVER))
+ msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server");
+ #if PORT_SHARE
+ if ((options->port_share_host || options->port_share_port) && options->proto != PROTO_TCPv4_SERVER)
+@@ -1645,9 +1656,9 @@ #endif
+ msg (M_USAGE, "--inetd cannot be used with --mode server");
+ if (options->ipchange)
+ msg (M_USAGE, "--ipchange cannot be used with --mode server (use --client-connect instead)");
+- if (!(options->proto == PROTO_UDPv4 || options->proto == PROTO_TCPv4_SERVER))
+- msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server");
+- if (options->proto != PROTO_UDPv4 && (options->cf_max || options->cf_per))
++ if (!(proto_is_dgram(options->proto) || options->proto == PROTO_TCPv4_SERVER || options->proto == PROTO_TCPv6_SERVER ))
++ msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server (also udp6/tcp6)");
++ if (!proto_is_udp(options->proto) && (options->cf_max || options->cf_per))
+ msg (M_USAGE, "--connect-freq only works with --mode server --proto udp. Try --max-clients instead.");
+ if (!(dev == DEV_TYPE_TAP || (dev == DEV_TYPE_TUN && options->topology == TOP_SUBNET)) && options->ifconfig_pool_netmask)
+ msg (M_USAGE, "The third parameter to --ifconfig-pool (netmask) is only valid in --dev tap mode");
+@@ -1724,7 +1735,7 @@ #ifdef USE_CRYPTO
+ /*
+ * Check consistency of replay options
+ */
+- if ((options->proto != PROTO_UDPv4)
++ if ((!proto_is_udp(options->proto))
+ && (options->replay_window != defaults.replay_window
+ || options->replay_time != defaults.replay_time))
+ msg (M_USAGE, "--replay-window only makes sense with --proto udp");
+@@ -1913,7 +1924,7 @@ #if P2MP
+ */
+ if (options->pull
+ && options->ping_rec_timeout_action == PING_UNDEF
+- && options->proto == PROTO_UDPv4)
++ && proto_is_udp(options->proto))
+ {
+ options->ping_rec_timeout = PRE_PULL_INITIAL_PING_RESTART;
+ options->ping_rec_timeout_action = PING_RESTART;
+diff --git a/openvpn/ps.c b/openvpn/ps.c
+index 49b1a2c..472c626 100644
+--- a/openvpn/ps.c
++++ b/openvpn/ps.c
+@@ -337,9 +337,9 @@ sock_addr_set (struct openvpn_sockaddr *
+ const int port)
+ {
+ CLEAR (*osaddr);
+- osaddr->sa.sin_family = AF_INET;
+- osaddr->sa.sin_addr.s_addr = htonl (addr);
+- osaddr->sa.sin_port = htons (port);
++ osaddr->addr.in4.sin_family = AF_INET;
++ osaddr->addr.in4.sin_addr.s_addr = htonl (addr);
++ osaddr->addr.in4.sin_port = htons (port);
+ }
+
+ static inline void
+diff --git a/openvpn/socket.c b/openvpn/socket.c
+index 70d8ffd..3074267 100644
+--- a/openvpn/socket.c
++++ b/openvpn/socket.c
+@@ -42,10 +42,16 @@
+ #include "memdbg.h"
+
+ const int proto_overhead[] = { /* indexed by PROTO_x */
+- IPv4_UDP_HEADER_SIZE,
++ 0,
++ IPv4_UDP_HEADER_SIZE, /* IPv4 */
+ IPv4_TCP_HEADER_SIZE,
+ IPv4_TCP_HEADER_SIZE,
+- IPv4_TCP_HEADER_SIZE
++#ifdef USE_PF_INET6
++ IPv6_UDP_HEADER_SIZE, /* IPv6 */
++ IPv6_TCP_HEADER_SIZE,
++ IPv6_TCP_HEADER_SIZE,
++ IPv6_TCP_HEADER_SIZE,
++#endif
+ };
+
+ /*
+@@ -260,20 +266,49 @@
+ struct openvpn_sockaddr *addr,
+ bool *changed)
+ {
+- if (host && addr)
+- {
+- const in_addr_t new_addr = getaddr (
+- GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE,
+- host,
+- 1,
+- NULL,
+- NULL);
+- if (new_addr && addr->sa.sin_addr.s_addr != new_addr)
++ switch(addr->addr.sa.sa_family) {
++ case AF_INET:
++ if (host && addr)
++ {
++ const in_addr_t new_addr = getaddr (
++ GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE,
++ host,
++ 1,
++ NULL,
++ NULL);
++ if (new_addr && addr->addr.in4.sin_addr.s_addr != new_addr)
+ {
+- addr->sa.sin_addr.s_addr = new_addr;
++ addr->addr.in4.sin_addr.s_addr = new_addr;
+ *changed = true;
+ }
+- }
++ }
++ break;
++#ifdef USE_PF_INET6
++ case AF_INET6: /* jjoFIXME: should adapt getaddr() for AF_INET6 */
++ if (host && addr)
++ {
++ struct addrinfo hints , *ai;
++ int err;
++ memset(&hints, 0, sizeof hints);
++ hints.ai_flags=AI_PASSIVE;
++ hints.ai_family=AF_INET6;
++ if ((err=getaddrinfo(host, NULL, &hints, &ai))==0)
++ {
++ struct sockaddr_in6 *sin6=(struct sockaddr_in6*)ai->ai_addr;
++ if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &addr->addr.in6.sin6_addr))
++ {
++ int port=addr->addr.in6.sin6_port; /* backup current port for easier copy, restore later */
++ addr->addr.in6=*sin6; /* ipv6 requires also eg. sin6_scope_id => easy to full copy*/
++ addr->addr.in6.sin6_port=port;
++ }
++ freeaddrinfo(ai);
++ }
++ }
++ break;
++#endif
++ default:
++ ASSERT(0);
++ }
+ }
+
+ static int
+@@ -531,6 +566,44 @@
+ return sd;
+ }
+
++#ifdef USE_PF_INET6
++static socket_descriptor_t
++create_socket_udp6 (const unsigned int flags)
++{
++ socket_descriptor_t sd;
++
++ if ((sd = socket (PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
++ msg (M_SOCKERR, "UDP: Cannot create UDP6 socket");
++#if ENABLE_IP_PKTINFO
++ else if (flags & SF_USE_IP_PKTINFO)
++ {
++ int pad = 1;
++ setsockopt (sd, IPPROTO_IPV6, IPV6_PKTINFO, (void*)&pad, sizeof(pad));
++ }
++#endif
++ return sd;
++}
++
++static socket_descriptor_t
++create_socket_tcp6 (void)
++{
++ socket_descriptor_t sd;
++
++ if ((sd = socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP)) < 0)
++ msg (M_SOCKERR, "Cannot create TCP6 socket");
++
++ /* set SO_REUSEADDR on socket */
++ {
++ int on = 1;
++ if (setsockopt (sd, SOL_SOCKET, SO_REUSEADDR,
++ (void *) &on, sizeof (on)) < 0)
++ msg (M_SOCKERR, "TCP: Cannot setsockopt SO_REUSEADDR on TCP6 socket");
++ }
++
++ return sd;
++}
++
++#endif
+ static void
+ create_socket (struct link_socket *sock)
+ {
+@@ -549,6 +622,17 @@
+ {
+ sock->sd = create_socket_tcp ();
+ }
++#ifdef USE_PF_INET6
++ else if (sock->info.proto == PROTO_TCPv6_SERVER
++ || sock->info.proto == PROTO_TCPv6_CLIENT)
++ {
++ sock->sd = create_socket_tcp6 ();
++ }
++ else if (sock->info.proto == PROTO_UDPv6)
++ {
++ sock->sd = create_socket_udp6 (sock->sockflags);
++ }
++#endif
+ else
+ {
+ ASSERT (0);
+@@ -586,7 +670,12 @@
+ struct link_socket_actual *act,
+ const bool nowait)
+ {
+- socklen_t remote_len = sizeof (act->dest.sa);
++ /* af_addr_size WILL return 0 in this case if AFs other than AF_INET
++ * are compiled because act is empty here.
++ * could use getsockname() to support later remote_len check
++ */
++ socklen_t remote_len_af = af_addr_size(act->dest.addr.sa.sa_family);
++ socklen_t remote_len = sizeof(act->dest.addr);
+ socket_descriptor_t new_sd = SOCKET_UNDEFINED;
+
+ CLEAR (*act);
+@@ -594,7 +683,7 @@
+ #ifdef HAVE_GETPEERNAME
+ if (nowait)
+ {
+- new_sd = getpeername (sd, (struct sockaddr *) &act->dest.sa, &remote_len);
++ new_sd = getpeername (sd, &act->dest.addr.sa, &remote_len);
+
+ if (!socket_defined (new_sd))
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "TCP: getpeername() failed");
+@@ -607,7 +696,7 @@
+ #endif
+ else
+ {
+- new_sd = accept (sd, (struct sockaddr *) &act->dest.sa, &remote_len);
++ new_sd = accept (sd, &act->dest.addr.sa, &remote_len);
+ }
+
+ #if 0 /* For debugging only, test the effect of accept() failures */
+@@ -623,7 +712,8 @@
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "TCP: accept(%d) failed", sd);
+ }
+- else if (remote_len != sizeof (act->dest.sa))
++ /* only valid if we have remote_len_af!=0 */
++ else if (remote_len_af && remote_len != remote_len_af)
+ {
+ msg (D_LINK_ERRORS, "TCP: Received strange incoming connection with unknown address length=%d", remote_len);
+ openvpn_close_socket (new_sd);
+@@ -724,7 +814,7 @@
+ {
+ struct gc_arena gc = gc_new ();
+
+- if (bind (sd, (struct sockaddr *) &local->sa, sizeof (local->sa)))
++ if (bind (sd, &local->addr.sa, af_addr_size(local->addr.sa.sa_family)))
+ {
+ const int errnum = openvpn_errno_socket ();
+ msg (M_FATAL, "%s: Socket bind failed on local address %s: %s",
+@@ -745,7 +835,7 @@
+
+ #ifdef CONNECT_NONBLOCK
+ set_nonblock (sd);
+- status = connect (sd, (struct sockaddr *) &remote->sa, sizeof (remote->sa));
++ status = connect (sd, &remote->addr.sa, af_addr_size(remote->addr.sa.sa_family));
+ if (status)
+ status = openvpn_errno_socket ();
+ if (status == EINPROGRESS)
+@@ -828,76 +918,99 @@
+ int retry = 0;
+
+ #ifdef CONNECT_NONBLOCK
+- msg (M_INFO, "Attempting to establish TCP connection with %s [nonblock]",
+- print_sockaddr (remote, &gc));
++ msg (M_INFO, "Attempting to establish TCP connection with %s [nonblock]",
++ print_sockaddr (remote, &gc));
+ #else
+- msg (M_INFO, "Attempting to establish TCP connection with %s",
+- print_sockaddr (remote, &gc));
++ msg (M_INFO, "Attempting to establish TCP connection with %s",
++ print_sockaddr (remote, &gc));
+ #endif
+
+ while (true)
+- {
+- int status;
++ {
++ int status;
+
+ #ifdef ENABLE_MANAGEMENT
+- if (management)
+- management_set_state (management,
+- OPENVPN_STATE_TCP_CONNECT,
+- NULL,
+- (in_addr_t)0,
+- (in_addr_t)0);
++ if (management)
++ management_set_state (management,
++ OPENVPN_STATE_TCP_CONNECT,
++ NULL,
++ (in_addr_t)0,
++ (in_addr_t)0);
+ #endif
+
+- status = openvpn_connect (*sd, remote, connect_timeout, signal_received);
++ status = openvpn_connect (*sd, remote, connect_timeout, signal_received);
+
+- get_signal (signal_received);
+- if (*signal_received)
+- goto done;
+-
+- if (!status)
+- break;
+-
+- msg (D_LINK_ERRORS,
+- "TCP: connect to %s failed, will try again in %d seconds: %s",
+- print_sockaddr (remote, &gc),
+- connect_retry_seconds,
+- strerror_ts (status, &gc));
+-
+- gc_reset (&gc);
+-
+- openvpn_close_socket (*sd);
+- *sd = SOCKET_UNDEFINED;
+-
+- if (connect_retry_max > 0 && ++retry >= connect_retry_max)
+- {
+- *signal_received = SIGUSR1;
+- goto done;
+- }
+-
+- openvpn_sleep (connect_retry_seconds);
+-
+- get_signal (signal_received);
+- if (*signal_received)
+- goto done;
++ get_signal (signal_received);
++ if (*signal_received)
++ goto done;
+
+- if (remote_list)
+- {
+- remote_list_next (remote_list);
+- remote_dynamic = remote_list_host (remote_list);
+- remote->sa.sin_port = htons (remote_list_port (remote_list));
+- *remote_changed = true;
+- }
++ if (!status)
++ break;
+
+- *sd = create_socket_tcp ();
+- if (bind_local)
+- socket_bind (*sd, local, "TCP Client");
+- update_remote (remote_dynamic, remote, remote_changed);
+- }
++ msg (D_LINK_ERRORS,
++ "TCP: connect to %s failed, will try again in %d seconds: %s",
++ print_sockaddr (remote, &gc),
++ connect_retry_seconds,
++ strerror_ts (status, &gc));
++
++
++ gc_reset (&gc);
++
++ openvpn_close_socket (*sd);
++ *sd = SOCKET_UNDEFINED;
++
++ if (connect_retry_max > 0 && ++retry >= connect_retry_max)
++ {
++ *signal_received = SIGUSR1;
++ goto done;
++ }
++
++ openvpn_sleep (connect_retry_seconds);
++
++ get_signal (signal_received);
++ if (*signal_received)
++ goto done;
++
++ switch(remote->addr.sa.sa_family) {
++ case AF_INET:
++ if (remote_list)
++ {
++ remote_list_next (remote_list);
++ remote_dynamic = remote_list_host (remote_list);
++ remote->addr.in4.sin_port = htons (remote_list_port (remote_list));
++ *remote_changed = true;
++ }
++
++ *sd = create_socket_tcp ();
++ if (bind_local)
++ socket_bind (*sd, local, "TCP Client");
++ update_remote (remote_dynamic, remote, remote_changed);
++ break;
++#ifdef USE_PF_INET6
++ case AF_INET6:
++ if (remote_list)
++ {
++ remote_list_next (remote_list);
++ remote_dynamic = remote_list_host (remote_list);
++ remote->addr.in6.sin6_port = htons (remote_list_port (remote_list));
++ *remote_changed = true;
++ }
++ *sd = create_socket_tcp6 ();
++ if (bind_local)
++ socket_bind (*sd, local, "TCP6 Client");
++ update_remote (remote_dynamic, remote, remote_changed);
++ break;
++#endif
++ default:
++ msg(M_FATAL, "Only TCP is supported for connection oriented, sa_family=%d",
++ remote->addr.sa.sa_family);
++ }
++ }
+
+- msg (M_INFO, "TCP connection established with %s",
+- print_sockaddr (remote, &gc));
++ msg (M_INFO, "TCP connection established with %s",
++ print_sockaddr (remote, &gc));
+
+- done:
++done:
+ gc_free (&gc);
+ }
+
+@@ -952,17 +1065,44 @@
+
+ /* resolve local address if undefined */
+ if (!addr_defined (&sock->info.lsa->local))
+- {
+- sock->info.lsa->local.sa.sin_family = AF_INET;
+- sock->info.lsa->local.sa.sin_addr.s_addr =
+- (sock->local_host ? getaddr (GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL,
+- sock->local_host,
+- 0,
+- NULL,
+- NULL)
+- : htonl (INADDR_ANY));
+- sock->info.lsa->local.sa.sin_port = htons (sock->local_port);
++ {
++ /* may return AF_{INET|INET6} guessed from local_host */
++ switch(addr_guess_family(sock->info.proto, sock->local_host)) {
++ case AF_INET:
++ sock->info.lsa->local.addr.in4.sin_family = AF_INET;
++ sock->info.lsa->local.addr.in4.sin_addr.s_addr =
++ (sock->local_host ? getaddr (GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL,
++ sock->local_host,
++ 0,
++ NULL,
++ NULL)
++ : htonl (INADDR_ANY));
++ sock->info.lsa->local.addr.in4.sin_port = htons (sock->local_port);
++ break;
++#ifdef USE_PF_INET6
++ case AF_INET6:
++ {
++ struct addrinfo hints , *ai;
++ int err;
++ memset(&hints, 0, sizeof hints);
++ hints.ai_flags=AI_PASSIVE;
++ hints.ai_family=AF_INET6;
++ /* if no local_host provided, ask for IN6ADDR_ANY ... */
++ if ((err=getaddrinfo(sock->local_host? sock->local_host : "::",
++ NULL, &hints, &ai))==0) {
++ sock->info.lsa->local.addr.in6 = *((struct sockaddr_in6*)(ai->ai_addr));
++ freeaddrinfo(ai);
++ } else {
++ msg (M_FATAL, "getaddrinfo() failed for local \"%s\": %s",
++ sock->local_host,
++ gai_strerror(err));
++ }
++ sock->info.lsa->local.addr.in6.sin6_port = htons (sock->local_port);
++ }
++ break;
++#endif
+ }
++ }
+
+ /* bind to local address/port */
+ if (sock->bind_local)
+@@ -986,103 +1126,128 @@
+ struct gc_arena gc = gc_new ();
+
+ if (!sock->did_resolve_remote)
++ {
++ /* resolve remote address if undefined */
++ if (!addr_defined (&sock->info.lsa->remote))
+ {
+- /* resolve remote address if undefined */
+- if (!addr_defined (&sock->info.lsa->remote))
+- {
+- sock->info.lsa->remote.sa.sin_family = AF_INET;
+- sock->info.lsa->remote.sa.sin_addr.s_addr = 0;
++ switch(addr_guess_family(sock->info.proto, sock->remote_host))
++ {
++ case AF_INET:
++ sock->info.lsa->remote.addr.in4.sin_family = AF_INET;
++ sock->info.lsa->remote.addr.in4.sin_addr.s_addr = 0;
+
+ if (sock->remote_host)
+- {
+- unsigned int flags = GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE;
+- int retry = 0;
+- bool status = false;
++ {
++ unsigned int flags = GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE;
++ int retry = 0;
++ bool status = false;
+
+- if (remote_list_len (sock->remote_list) > 1 && sock->resolve_retry_seconds == RESOLV_RETRY_INFINITE)
+- {
+- if (phase == 2)
+- flags |= (GETADDR_TRY_ONCE | GETADDR_FATAL);
+- retry = 0;
+- }
+- else if (phase == 1)
+- {
+- if (sock->resolve_retry_seconds)
+- {
+- retry = 0;
+- }
+- else
+- {
+- flags |= (GETADDR_FATAL | GETADDR_MENTION_RESOLVE_RETRY);
+- retry = 0;
+- }
+- }
+- else if (phase == 2)
+- {
+- if (sock->resolve_retry_seconds)
+- {
+- flags |= GETADDR_FATAL;
+- retry = sock->resolve_retry_seconds;
+- }
+- else
+- {
+- ASSERT (0);
+- }
+- }
++ if (remote_list_len (sock->remote_list) > 1 && sock->resolve_retry_seconds == RESOLV_RETRY_INFINITE)
++ {
++ if (phase == 2)
++ flags |= (GETADDR_TRY_ONCE | GETADDR_FATAL);
++ retry = 0;
++ }
++ else if (phase == 1)
++ {
++ if (sock->resolve_retry_seconds)
++ {
++ retry = 0;
++ }
+ else
+- {
+- ASSERT (0);
+- }
++ {
++ flags |= (GETADDR_FATAL | GETADDR_MENTION_RESOLVE_RETRY);
++ retry = 0;
++ }
++ }
++ else if (phase == 2)
++ {
++ if (sock->resolve_retry_seconds)
++ {
++ flags |= GETADDR_FATAL;
++ retry = sock->resolve_retry_seconds;
++ }
++ else
++ {
++ ASSERT (0);
++ }
++ }
++ else
++ {
++ ASSERT (0);
++ }
+
+- sock->info.lsa->remote.sa.sin_addr.s_addr = getaddr (
+- flags,
+- sock->remote_host,
+- retry,
+- &status,
+- signal_received);
+-
+- dmsg (D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d",
+- flags,
+- phase,
+- retry,
+- signal_received ? *signal_received : -1,
+- status);
++ sock->info.lsa->remote.addr.in4.sin_addr.s_addr = getaddr (
++ flags,
++ sock->remote_host,
++ retry,
++ &status,
++ signal_received);
++
++ dmsg (D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d",
++ flags,
++ phase,
++ retry,
++ signal_received ? *signal_received : -1,
++ status);
+
++ if (signal_received)
++ {
++ if (*signal_received)
++ goto done;
++ }
++ if (!status)
++ {
+ if (signal_received)
+- {
+- if (*signal_received)
+- goto done;
+- }
+- if (!status)
+- {
+- if (signal_received)
+- *signal_received = SIGUSR1;
+- goto done;
+- }
++ *signal_received = SIGUSR1;
++ goto done;
+ }
++ }
+
+- sock->info.lsa->remote.sa.sin_port = htons (sock->remote_port);
+- }
+-
+- /* should we re-use previous active remote address? */
+- if (link_socket_actual_defined (&sock->info.lsa->actual))
+- {
+- msg (M_INFO, "TCP/UDP: Preserving recently used remote address: %s",
+- print_link_socket_actual (&sock->info.lsa->actual, &gc));
+- if (remote_dynamic)
+- *remote_dynamic = NULL;
+- }
+- else
+- {
+- CLEAR (sock->info.lsa->actual);
+- sock->info.lsa->actual.dest = sock->info.lsa->remote;
+- }
++ sock->info.lsa->remote.addr.in4.sin_port = htons (sock->remote_port);
++ break;
+
+- /* remember that we finished */
+- sock->did_resolve_remote = true;
++#ifdef USE_PF_INET6
++ case AF_INET6: /* jjoFIXME: ipv6 signal logic */
++ {
++ struct addrinfo hints , *ai;
++ int err;
++ memset(&hints, 0, sizeof hints);
++ hints.ai_flags=0;
++ hints.ai_family=AF_INET6;
++ if ((err=getaddrinfo(sock->remote_host? sock->remote_host : "::" , NULL, &hints, &ai))==0) {
++ sock->info.lsa->remote.addr.in6 = *((struct sockaddr_in6*)(ai->ai_addr));
++ freeaddrinfo(ai);
++ } else {
++ msg (M_FATAL, "getaddrinfo() failed for remote \"%s\": %s",
++ sock->remote_host,
++ gai_strerror(err));
++ }
++ sock->info.lsa->remote.addr.in6.sin6_port = htons (sock->remote_port);
++ }
++ break;
++#endif
++ }
++ }
++ /* should we re-use previous active remote address? */
++ if (link_socket_actual_defined (&sock->info.lsa->actual))
++ {
++ msg (M_INFO, "TCP/UDP: Preserving recently used remote address: %s",
++ print_link_socket_actual (&sock->info.lsa->actual, &gc));
++ if (remote_dynamic)
++ *remote_dynamic = NULL;
++ }
++ else
++ {
++ CLEAR (sock->info.lsa->actual);
++ sock->info.lsa->actual.dest = sock->info.lsa->remote;
+ }
+
+- done:
++ /* remember that we finished */
++ sock->did_resolve_remote = true;
++ }
++
++done:
+ gc_free (&gc);
+ }
+
+@@ -1312,7 +1477,11 @@
+ goto done;
+
+ /* TCP client/server */
+- if (sock->info.proto == PROTO_TCPv4_SERVER)
++ if (sock->info.proto == PROTO_TCPv4_SERVER
++#ifdef USE_PF_INET6
++ ||sock->info.proto == PROTO_TCPv6_SERVER
++#endif
++ )
+ {
+ switch (sock->mode)
+ {
+@@ -1347,7 +1516,11 @@
+ ASSERT (0);
+ }
+ }
+- else if (sock->info.proto == PROTO_TCPv4_CLIENT)
++ else if (sock->info.proto == PROTO_TCPv4_CLIENT
++#ifdef USE_PF_INET6
++ ||sock->info.proto == PROTO_TCPv6_CLIENT
++#endif
++ )
+ {
+
+ #ifdef GENERAL_PROXY_SUPPORT
+@@ -1432,8 +1605,8 @@
+ sock->remote_port = sock->proxy_dest_port;
+ sock->did_resolve_remote = false;
+
+- sock->info.lsa->actual.dest.sa.sin_addr.s_addr = 0;
+- sock->info.lsa->remote.sa.sin_addr.s_addr = 0;
++ addr_zero_host(&sock->info.lsa->actual.dest);
++ addr_zero_host(&sock->info.lsa->remote);
+
+ resolve_remote (sock, 1, NULL, signal_received);
+
+@@ -1448,7 +1621,7 @@
+ if (remote_changed)
+ {
+ msg (M_INFO, "TCP/UDP: Dynamic remote address changed during TCP connection establishment");
+- sock->info.lsa->remote.sa.sin_addr.s_addr = sock->info.lsa->actual.dest.sa.sin_addr.s_addr;
++ addr_copy_host(&sock->info.lsa->remote, &sock->info.lsa->actual.dest);
+ }
+ }
+
+@@ -1620,13 +1793,20 @@
+ {
+ struct gc_arena gc = gc_new ();
+
+- msg (D_LINK_ERRORS,
+- "TCP/UDP: Incoming packet rejected from %s[%d], expected peer address: %s (allow this incoming source address/port by removing --remote or adding --float)",
+- print_link_socket_actual (from_addr, &gc),
+- (int)from_addr->dest.sa.sin_family,
+- print_sockaddr (&info->lsa->remote, &gc));
++ switch(from_addr->dest.addr.sa.sa_family)
++ {
++ case AF_INET:
++#ifdef USE_PF_INET6
++ case AF_INET6:
++#endif
++ msg (D_LINK_ERRORS,
++ "TCP/UDP: Incoming packet rejected from %s[%d], expected peer address: %s (allow this incoming source address/port by removing --remote or adding --float)",
++ print_link_socket_actual (from_addr, &gc),
++ (int)from_addr->dest.addr.sa.sa_family,
++ print_sockaddr (&info->lsa->remote, &gc));
++ break;
++ }
+ buf->len = 0;
+-
+ gc_free (&gc);
+ }
+
+@@ -1641,10 +1821,25 @@
+ {
+ const struct link_socket_addr *lsa = info->lsa;
+
++/*
++ * This logic supports "redirect-gateway" semantic, which
++ * makes sense only for PF_INET routes over PF_INET endpoints
++ *
++ * Maybe in the future consider PF_INET6 endpoints also ...
++ * by now just ignore it
++ *
++ */
++#if defined ( USE_PF_INET6 )
++ if(lsa->actual.dest.addr.sa.sa_family != AF_INET)
++ return 0;
++#else
++ ASSERT(lsa->actual.dest.addr.sa.sa_family == AF_INET);
++#endif
++
+ if (link_socket_actual_defined (&lsa->actual))
+- return ntohl (lsa->actual.dest.sa.sin_addr.s_addr);
++ return ntohl (lsa->actual.dest.addr.in4.sin_addr.s_addr);
+ else if (addr_defined (&lsa->remote))
+- return ntohl (lsa->remote.sa.sin_addr.s_addr);
++ return ntohl (lsa->remote.addr.in4.sin_addr.s_addr);
+ else
+ return 0;
+ }
+@@ -1871,27 +2066,58 @@
+ const unsigned int flags,
+ struct gc_arena *gc)
+ {
+- if (addr)
+- {
+- struct buffer out = alloc_buf_gc (64, gc);
+- const int port = ntohs (addr->sa.sin_port);
+-
+- mutex_lock_static (L_INET_NTOA);
+- buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]"));
+- mutex_unlock_static (L_INET_NTOA);
++ struct buffer out;
++ bool addr_is_defined;
++ if (!addr) {
++ return "[NULL]";
++ }
++ addr_is_defined = addr_defined (addr);
++ switch(addr->addr.sa.sa_family) {
++ case AF_INET:
++ {
++ const int port= ntohs (addr->addr.in4.sin_port);
++ out = alloc_buf_gc (128, gc);
++ buf_puts (&out, "[AF_INET]");
++ mutex_lock_static (L_INET_NTOA);
++ buf_puts (&out, (addr_is_defined ? inet_ntoa (addr->addr.in4.sin_addr) : "[undef]"));
++ mutex_unlock_static (L_INET_NTOA);
+
+- if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED)))
+- && port)
++ if (((flags & PS_SHOW_PORT) || (addr_is_defined && (flags & PS_SHOW_PORT_IF_DEFINED)))
++ && port)
+ {
+ if (separator)
+ buf_printf (&out, "%s", separator);
+
+ buf_printf (&out, "%d", port);
+ }
+- return BSTR (&out);
+- }
+- else
+- return "[NULL]";
++ }
++ break;
++#ifdef USE_PF_INET6
++ case AF_INET6:
++ {
++ const int port= ntohs (addr->addr.in6.sin6_port);
++ char buf[INET6_ADDRSTRLEN] = "[undef]";
++ out = alloc_buf_gc (128, gc);
++ buf_puts (&out, "[AF_INET6]");
++ if (addr_is_defined)
++ {
++ getnameinfo(&addr->addr.sa, sizeof (struct sockaddr_in6),
++ buf, sizeof (buf), NULL, 0, NI_NUMERICHOST);
++ buf_puts (&out, buf);
++ }
++ if (((flags & PS_SHOW_PORT) || (addr_is_defined && (flags & PS_SHOW_PORT_IF_DEFINED)))
++ && port)
++ {
++ if (separator)
++ buf_puts (&out, separator);
++
++ buf_printf (&out, "%d", port);
++ }
++ }
++ break;
++#endif
++ }
++ return BSTR (&out);
+ }
+
+ const char *
+@@ -1911,12 +2137,38 @@
+ struct buffer out = alloc_buf_gc (128, gc);
+ buf_printf (&out, "%s", print_sockaddr_ex (&act->dest, separator, flags, gc));
+ #if ENABLE_IP_PKTINFO
+- if ((flags & PS_SHOW_PKTINFO) && act->pi.ipi_spec_dst.s_addr)
++ if ((flags & PS_SHOW_PKTINFO) && addr_defined_ipi(act))
+ {
++ switch(act->dest.addr.sa.sa_family)
++ {
++ case AF_INET:
++ {
+ struct openvpn_sockaddr sa;
+ CLEAR (sa);
+- sa.sa.sin_addr = act->pi.ipi_spec_dst;
++ sa.addr.in4.sin_addr = act->pi.in4.ipi_spec_dst;
+ buf_printf (&out, " (via %s)", print_sockaddr_ex (&sa, separator, 0, gc));
++ }
++ break;
++#ifdef USE_PF_INET6
++ case AF_INET6:
++ {
++ struct sockaddr_in6 sin6;
++ char buf[INET6_ADDRSTRLEN] = "[undef]";
++ memset(&sin6, 0, sizeof sin6);
++ sin6.sin6_family = AF_INET6;
++ sin6.sin6_addr = act->pi.in6.ipi6_addr;
++ {
++ if (getnameinfo((struct sockaddr *)&sin6, sizeof (struct sockaddr_in6),
++ buf, sizeof (buf), NULL, 0, NI_NUMERICHOST) == 0)
++ buf_printf (&out, " (via %s)", buf);
++ else
++ buf_printf (&out, " (via [getnameinfo() err])");
++ }
++ }
++ break;
++#endif
++ }
++
+ }
+ #endif
+ return BSTR (&out);
+@@ -1952,21 +2204,37 @@
+ setenv_sockaddr (struct env_set *es, const char *name_prefix, const struct openvpn_sockaddr *addr, const bool flags)
+ {
+ char name_buf[256];
++ char buf[128];
+
+- if (flags & SA_IP_PORT)
+- openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip", name_prefix);
+- else
+- openvpn_snprintf (name_buf, sizeof (name_buf), "%s", name_prefix);
++ switch(addr->addr.sa.sa_family) {
++ case AF_INET:
++ if (flags & SA_IP_PORT)
++ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip", name_prefix);
++ else
++ openvpn_snprintf (name_buf, sizeof (name_buf), "%s", name_prefix);
+
+- mutex_lock_static (L_INET_NTOA);
+- setenv_str (es, name_buf, inet_ntoa (addr->sa.sin_addr));
+- mutex_unlock_static (L_INET_NTOA);
++ mutex_lock_static (L_INET_NTOA);
++ setenv_str (es, name_buf, inet_ntoa (addr->addr.in4.sin_addr));
++ mutex_unlock_static (L_INET_NTOA);
++
++ if ((flags & SA_IP_PORT) && addr->addr.in4.sin_port)
++ {
++ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix);
++ setenv_int (es, name_buf, ntohs (addr->addr.in4.sin_port));
++ }
++ break;
++#ifdef USE_PF_INET6
++ case AF_INET6:
++ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip6", name_prefix);
++ getnameinfo(&addr->addr.sa, sizeof (struct sockaddr_in6),
++ buf, sizeof(buf), NULL, 0, NI_NUMERICHOST);
++ setenv_str (es, name_buf, buf);
+
+- if ((flags & SA_IP_PORT) && addr->sa.sin_port)
+- {
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix);
+- setenv_int (es, name_buf, ntohs (addr->sa.sin_port));
+- }
++ setenv_int (es, name_buf, ntohs (addr->addr.in6.sin6_port));
++ break;
++#endif
++ }
+ }
+
+ void
+@@ -1976,7 +2244,8 @@
+ {
+ struct openvpn_sockaddr si;
+ CLEAR (si);
+- si.sa.sin_addr.s_addr = htonl (addr);
++ si.addr.in4.sin_family = AF_INET;
++ si.addr.in4.sin_addr.s_addr = htonl (addr);
+ setenv_sockaddr (es, name_prefix, &si, flags);
+ }
+ }
+@@ -1997,16 +2266,63 @@
+ struct proto_names {
+ const char *short_form;
+ const char *display_form;
++ bool is_dgram;
++ bool is_net;
++ sa_family_t proto_af;
+ };
+
+ /* Indexed by PROTO_x */
+-static const struct proto_names proto_names[] = {
+- {"udp", "UDPv4"},
+- {"tcp-server", "TCPv4_SERVER"},
+- {"tcp-client", "TCPv4_CLIENT"},
+- {"tcp", "TCPv4"}
++static const struct proto_names proto_names[PROTO_N] = {
++ {"proto-uninitialized", "proto-NONE",0,0, AF_UNSPEC},
++ {"udp", "UDPv4",1,1, AF_INET},
++ {"tcp-server", "TCPv4_SERVER",0,1, AF_INET},
++ {"tcp-client", "TCPv4_CLIENT",0,1, AF_INET},
++ {"tcp", "TCPv4",0,1, AF_INET},
++#ifdef USE_PF_INET6
++ {"udp6" ,"UDPv6",1,1, AF_INET6},
++ {"tcp6-server","TCPv6_SERVER",0,1, AF_INET6},
++ {"tcp6-client","TCPv6_CLIENT",0,1, AF_INET6},
++ {"tcp6" ,"TCPv6",0,1, AF_INET6},
++#endif
+ };
+
++bool
++proto_is_net(int proto)
++{
++ if (proto < 0 || proto >= PROTO_N)
++ ASSERT(0);
++ return proto_names[proto].is_net;
++}
++bool
++proto_is_dgram(int proto)
++{
++ if (proto < 0 || proto >= PROTO_N)
++ ASSERT(0);
++ return proto_names[proto].is_dgram;
++}
++bool
++proto_is_udp(int proto)
++{
++ if (proto < 0 || proto >= PROTO_N)
++ ASSERT(0);
++ return proto_names[proto].is_dgram&&proto_names[proto].is_net;
++}
++bool
++proto_is_tcp(int proto)
++{
++ if (proto < 0 || proto >= PROTO_N)
++ ASSERT(0);
++ return (!proto_names[proto].is_dgram)&&proto_names[proto].is_net;
++}
++
++sa_family_t
++proto_sa_family(int proto)
++{
++ if (proto < 0 || proto >= PROTO_N)
++ ASSERT(0);
++ return proto_names[proto].proto_af;
++}
++
+ int
+ ascii2proto (const char* proto_name)
+ {
+@@ -2046,6 +2362,46 @@
+ return BSTR (&out);
+ }
+
++int
++addr_guess_family(int proto, const char *name)
++{
++ sa_family_t ret;
++ if (proto) {
++ return proto_sa_family(proto); /* already stamped */
++ }
++#ifdef USE_PF_UNIX
++ else if (name && name[0] == '/') {
++ return AF_UNIX;
++ }
++#endif
++#ifdef USE_PF_INET6
++ else {
++ struct addrinfo hints , *ai;
++ int err;
++ memset(&hints, 0, sizeof hints);
++ hints.ai_flags=AI_NUMERICHOST;
++ if ((err=getaddrinfo(name, NULL, &hints, &ai))==0) {
++ ret=ai->ai_family;
++ freeaddrinfo(ai);
++ return ret;
++ }
++ }
++#endif
++ return AF_INET; /* default */
++}
++const char *
++addr_family_name (int af)
++{
++ switch (af) {
++ case AF_INET: return "AF_INET";
++ case AF_INET6: return "AF_INET6";
++#ifdef USE_PF_UNIX
++ case AF_UNIX: return "AF_UNIX";
++#endif
++ }
++ return "AF_UNSPEC";
++}
++
+ /*
+ * Given a local proto, return local proto
+ * if !remote, or compatible remote proto
+@@ -2060,10 +2416,15 @@
+ ASSERT (proto >= 0 && proto < PROTO_N);
+ if (remote)
+ {
+- if (proto == PROTO_TCPv4_SERVER)
+- return PROTO_TCPv4_CLIENT;
+- if (proto == PROTO_TCPv4_CLIENT)
+- return PROTO_TCPv4_SERVER;
++ switch (proto)
++ {
++ case PROTO_TCPv4_SERVER: return PROTO_TCPv4_CLIENT;
++ case PROTO_TCPv4_CLIENT: return PROTO_TCPv4_SERVER;
++#ifdef USE_PF_INET6
++ case PROTO_TCPv6_SERVER: return PROTO_TCPv6_CLIENT;
++ case PROTO_TCPv6_CLIENT: return PROTO_TCPv6_SERVER;
++#endif
++ }
+ }
+ return proto;
+ }
+@@ -2121,12 +2482,27 @@
+
+ #if ENABLE_IP_PKTINFO
+
+-struct openvpn_pktinfo
++struct openvpn_in4_pktinfo
+ {
+ struct cmsghdr cmsghdr;
+- struct in_pktinfo in_pktinfo;
++ struct in_pktinfo pi;
++};
++#ifdef USE_PF_INET6
++struct openvpn_in6_pktinfo
++{
++ struct cmsghdr cmsghdr;
++ struct in6_pktinfo pi6;
++};
++#endif
++
++union openvpn_pktinfo {
++ struct openvpn_in4_pktinfo cmsgpi;
++#ifdef USE_PF_INET6
++ struct openvpn_in6_pktinfo cmsgpi6;
++#endif
+ };
+
++
+ static socklen_t
+ link_socket_read_udp_posix_recvmsg (struct link_socket *sock,
+ struct buffer *buf,
+@@ -2134,15 +2510,15 @@
+ struct link_socket_actual *from)
+ {
+ struct iovec iov;
+- struct openvpn_pktinfo opi;
++ union openvpn_pktinfo opi;
+ struct msghdr mesg;
+- socklen_t fromlen = sizeof (from->dest.sa);
++ socklen_t fromlen = sizeof (from->dest.addr);
+
+ iov.iov_base = BPTR (buf);
+ iov.iov_len = maxsize;
+ mesg.msg_iov = &iov;
+ mesg.msg_iovlen = 1;
+- mesg.msg_name = &from->dest.sa;
++ mesg.msg_name = &from->dest.addr;
+ mesg.msg_namelen = fromlen;
+ mesg.msg_control = &opi;
+ mesg.msg_controllen = sizeof (opi);
+@@ -2159,9 +2535,21 @@
+ && cmsg->cmsg_len >= sizeof (opi))
+ {
+ struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
+- from->pi.ipi_ifindex = pkti->ipi_ifindex;
+- from->pi.ipi_spec_dst = pkti->ipi_spec_dst;
++ from->pi.in4.ipi_ifindex = pkti->ipi_ifindex;
++ from->pi.in4.ipi_spec_dst = pkti->ipi_spec_dst;
++ }
++#ifdef USE_PF_INET6
++ else if (cmsg != NULL
++ && CMSG_NXTHDR (&mesg, cmsg) == NULL
++ && cmsg->cmsg_level == IPPROTO_IPV6
++ && cmsg->cmsg_type == IPV6_PKTINFO
++ && cmsg->cmsg_len >= sizeof (struct openvpn_in6_pktinfo))
++ {
++ struct in6_pktinfo *pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg);
++ from->pi.in6.ipi6_ifindex = pkti6->ipi6_ifindex;
++ from->pi.in6.ipi6_addr = pkti6->ipi6_addr;
+ }
++#endif
+ }
+ return fromlen;
+ }
+@@ -2173,18 +2561,20 @@
+ int maxsize,
+ struct link_socket_actual *from)
+ {
+- socklen_t fromlen = sizeof (from->dest.sa);
+- from->dest.sa.sin_addr.s_addr = 0;
++ socklen_t fromlen = sizeof (from->dest.addr);
++ socklen_t expectedlen = af_addr_size(proto_sa_family(sock->info.proto));
++ addr_zero_host(&from->dest);
+ ASSERT (buf_safe (buf, maxsize));
+ #if ENABLE_IP_PKTINFO
+- if (sock->sockflags & SF_USE_IP_PKTINFO)
++ /* Both PROTO_UDPv4 and PROTO_UDPv6 */
++ if (proto_is_udp(sock->info.proto) && sock->sockflags & SF_USE_IP_PKTINFO)
+ fromlen = link_socket_read_udp_posix_recvmsg (sock, buf, maxsize, from);
+ else
+ #endif
+ buf->len = recvfrom (sock->sd, BPTR (buf), maxsize, 0,
+- (struct sockaddr *) &from->dest.sa, &fromlen);
+- if (fromlen != sizeof (from->dest.sa))
+- bad_address_length (fromlen, sizeof (from->dest.sa));
++ &from->dest.addr.sa, &fromlen);
++ if (buf->len >= 0 && expectedlen && fromlen != expectedlen)
++ bad_address_length (fromlen, expectedlen);
+ return buf->len;
+ }
+
+@@ -2221,26 +2611,52 @@
+ struct iovec iov;
+ struct msghdr mesg;
+ struct cmsghdr *cmsg;
+- struct in_pktinfo *pkti;
+- struct openvpn_pktinfo opi;
+
+ iov.iov_base = BPTR (buf);
+ iov.iov_len = BLEN (buf);
+ mesg.msg_iov = &iov;
+ mesg.msg_iovlen = 1;
+- mesg.msg_name = &to->dest.sa;
+- mesg.msg_namelen = sizeof (to->dest.sa);
+- mesg.msg_control = &opi;
+- mesg.msg_controllen = sizeof (opi);
+- mesg.msg_flags = 0;
+- cmsg = CMSG_FIRSTHDR (&mesg);
+- cmsg->cmsg_len = sizeof (opi);
+- cmsg->cmsg_level = SOL_IP;
+- cmsg->cmsg_type = IP_PKTINFO;
+- pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
+- pkti->ipi_ifindex = to->pi.ipi_ifindex;
+- pkti->ipi_spec_dst = to->pi.ipi_spec_dst;
+- pkti->ipi_addr.s_addr = 0;
++ switch (sock->info.lsa->remote.addr.sa.sa_family)
++ {
++ case AF_INET: {
++ struct openvpn_in4_pktinfo opi;
++ struct in_pktinfo *pkti;
++ mesg.msg_name = &to->dest.addr.sa;
++ mesg.msg_namelen = sizeof (struct sockaddr_in);
++ mesg.msg_control = &opi;
++ mesg.msg_controllen = sizeof (opi);
++ mesg.msg_flags = 0;
++ cmsg = CMSG_FIRSTHDR (&mesg);
++ cmsg->cmsg_len = sizeof (opi);
++ cmsg->cmsg_level = SOL_IP;
++ cmsg->cmsg_type = IP_PKTINFO;
++ pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
++ pkti->ipi_ifindex = to->pi.in4.ipi_ifindex;
++ pkti->ipi_spec_dst = to->pi.in4.ipi_spec_dst;
++ pkti->ipi_addr.s_addr = 0;
++ break;
++ }
++#ifdef USE_PF_INET6
++ case AF_INET6: {
++ struct openvpn_in6_pktinfo opi6;
++ struct in6_pktinfo *pkti6;
++ mesg.msg_name = &to->dest.addr.sa;
++ mesg.msg_namelen = sizeof (struct sockaddr_in6);
++ mesg.msg_control = &opi6;
++ mesg.msg_controllen = sizeof (opi6);
++ mesg.msg_flags = 0;
++ cmsg = CMSG_FIRSTHDR (&mesg);
++ cmsg->cmsg_len = sizeof (opi6);
++ cmsg->cmsg_level = IPPROTO_IPV6;
++ cmsg->cmsg_type = IPV6_PKTINFO;
++ pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg);
++ pkti6->ipi6_ifindex = to->pi.in6.ipi6_ifindex;
++ pkti6->ipi6_addr = to->pi.in6.ipi6_addr;
++ break;
++ }
++#endif
++ default: ASSERT(0);
++ }
+ return sendmsg (sock->sd, &mesg, 0);
+ }
+
+@@ -2384,7 +2800,7 @@
+ {
+ /* set destination address for UDP writes */
+ sock->writes.addr_defined = true;
+- sock->writes.addr = to->dest.sa;
++ sock->writes.addr = to->dest.addr.in4;
+ sock->writes.addrlen = sizeof (sock->writes.addr);
+
+ status = WSASendTo(
+@@ -2540,10 +2956,10 @@
+ {
+ if (io->addrlen != sizeof (io->addr))
+ bad_address_length (io->addrlen, sizeof (io->addr));
+- from->dest.sa = io->addr;
++ from->dest.addr.sa = io->addr;
+ }
+ else
+- CLEAR (from->dest.sa);
++ CLEAR (from->dest.addr.sa);
+ }
+
+ if (buf)
+diff --git a/openvpn/socket.h b/openvpn/socket.h
+index 28bf41f..435cf38 100644
+--- a/openvpn/socket.h
++++ b/openvpn/socket.h
+@@ -81,7 +81,13 @@ #define ntohps(x) ntohs(x)
+ struct openvpn_sockaddr
+ {
+ /*int dummy;*/ /* add offset to force a bug if sa not explicitly dereferenced */
+- struct sockaddr_in sa;
++ union {
++ struct sockaddr sa;
++ struct sockaddr_in in4;
++#ifdef USE_PF_INET6
++ struct sockaddr_in6 in6;
++#endif
++ } addr;
+ };
+
+ /* actual address of remote, based on source address of received packets */
+@@ -90,7 +96,12 @@ struct link_socket_actual
+ int dummy; /* JYFIXME -- add offset to force a bug if dest not explicitly dereferenced */
+ struct openvpn_sockaddr dest;
+ #if ENABLE_IP_PKTINFO
+- struct in_pktinfo pi;
++ union {
++ struct in_pktinfo in4;
++#ifdef USE_PF_INET6
++ struct in6_pktinfo in6;
++#endif
++ } pi;
+ #endif
+ };
+
+@@ -411,6 +422,14 @@ socket_descriptor_t create_socket_tcp (v
+ socket_descriptor_t socket_do_accept (socket_descriptor_t sd,
+ struct link_socket_actual *act,
+ const bool nowait);
++/*
++ * proto related
++ */
++bool proto_is_net(int proto);
++bool proto_is_dgram(int proto);
++bool proto_is_udp(int proto);
++bool proto_is_tcp(int proto);
++
+
+ /*
+ * DNS resolution
+@@ -436,23 +455,49 @@ in_addr_t getaddr (unsigned int flags,
+ * Transport protocol naming and other details.
+ */
+
+-#define PROTO_UDPv4 0
+-#define PROTO_TCPv4_SERVER 1
+-#define PROTO_TCPv4_CLIENT 2
+-#define PROTO_TCPv4 3
+-#define PROTO_N 4
++#if 0 /* PRE UDPv6/TCPv6 code */
++#define PROTO_NONE 0 /* catch for uninitialized */
++#define PROTO_UDPv4 1
++#define PROTO_TCPv4_SERVER 2
++#define PROTO_TCPv4_CLIENT 3
++#define PROTO_TCPv4 4
++#define PROTO_UDPv6 5
++#define PROTO_TCPv6_SERVER 6
++#define PROTO_TCPv6_CLIENT 7
++#define PROTO_TCPv6 8
++#define PROTO_N 9
++#endif /* if 0 */
++
++/*
++ * Use enum's instead of #define to allow for easier
++ * optional proto support
++ */
++enum proto_num {
++ PROTO_NONE, /* catch for uninitialized */
++ PROTO_UDPv4,
++ PROTO_TCPv4_SERVER,
++ PROTO_TCPv4_CLIENT,
++ PROTO_TCPv4,
++ PROTO_UDPv6,
++ PROTO_TCPv6_SERVER,
++ PROTO_TCPv6_CLIENT,
++ PROTO_TCPv6,
++ PROTO_N
++};
+
+ int ascii2proto (const char* proto_name);
+ const char *proto2ascii (int proto, bool display_form);
+ const char *proto2ascii_all (struct gc_arena *gc);
+ int proto_remote (int proto, bool remote);
++const char *addr_family_name(int af);
+
+ /*
+ * Overhead added to packets by various protocols.
+ */
+ #define IPv4_UDP_HEADER_SIZE 28
+ #define IPv4_TCP_HEADER_SIZE 40
+-#define IPv6_UDP_HEADER_SIZE 40
++#define IPv6_UDP_HEADER_SIZE 48
++#define IPv6_TCP_HEADER_SIZE 60
+
+ extern const int proto_overhead[];
+
+@@ -485,7 +530,7 @@ legal_ipv4_port (int port)
+ static inline bool
+ link_socket_proto_connection_oriented (int proto)
+ {
+- return proto == PROTO_TCPv4_SERVER || proto == PROTO_TCPv4_CLIENT;
++ return !proto_is_dgram(proto);
+ }
+
+ static inline bool
+@@ -500,7 +545,30 @@ link_socket_connection_oriented (const s
+ static inline bool
+ addr_defined (const struct openvpn_sockaddr *addr)
+ {
+- return addr->sa.sin_addr.s_addr != 0;
++ if (!addr) return 0;
++ switch (addr->addr.sa.sa_family) {
++ case AF_INET: return addr->addr.in4.sin_addr.s_addr != 0;
++#ifdef USE_PF_INET6
++ case AF_INET6: return !IN6_IS_ADDR_UNSPECIFIED(&addr->addr.in6.sin6_addr);
++#endif
++ default: return 0;
++ }
++}
++static inline bool
++addr_defined_ipi (const struct link_socket_actual *lsa)
++{
++#if ENABLE_IP_PKTINFO
++ if (!lsa) return 0;
++ switch (lsa->dest.addr.sa.sa_family) {
++ case AF_INET: return lsa->pi.in4.ipi_spec_dst.s_addr != 0;
++#ifdef USE_PF_INET6
++ case AF_INET6: return !IN6_IS_ADDR_UNSPECIFIED(&lsa->pi.in6.ipi6_addr);
++#endif
++ default: return 0;
++ }
++#else
++ ASSERT(0);
++#endif
+ }
+
+ static inline bool
+@@ -512,20 +580,50 @@ link_socket_actual_defined (const struct
+ static inline bool
+ addr_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2)
+ {
+- return a1->sa.sin_addr.s_addr == a2->sa.sin_addr.s_addr;
++ switch(a1->addr.sa.sa_family) {
++ case AF_INET:
++ return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr;
++#ifdef USE_PF_INET6
++ case AF_INET6:
++ return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &a2->addr.in6.sin6_addr);
++#endif
++ }
++ ASSERT(0);
++ return false;
+ }
+
+ static inline in_addr_t
+-addr_host (const struct openvpn_sockaddr *s)
++addr_host (const struct openvpn_sockaddr *addr)
+ {
+- return ntohl (s->sa.sin_addr.s_addr);
++ /*
++ * "public" addr returned is checked against ifconfig for
++ * possible clash: non sense for now given
++ * that we do ifconfig only IPv4
++ */
++#if defined(USE_PF_INET6)
++ if(addr->addr.sa.sa_family != AF_INET)
++ return 0;
++#else
++ ASSERT(addr->addr.sa.sa_family == AF_INET);
++#endif
++ return ntohl (addr->addr.in4.sin_addr.s_addr);
+ }
+
+ static inline bool
+ addr_port_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2)
+ {
+- return a1->sa.sin_addr.s_addr == a2->sa.sin_addr.s_addr
+- && a1->sa.sin_port == a2->sa.sin_port;
++ switch(a1->addr.sa.sa_family) {
++ case AF_INET:
++ return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr
++ && a1->addr.in4.sin_port == a2->addr.in4.sin_port;
++#ifdef USE_PF_INET6
++ case AF_INET6:
++ return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &a2->addr.in6.sin6_addr)
++ && a1->addr.in6.sin6_port == a2->addr.in6.sin6_port;
++#endif
++ }
++ ASSERT(0);
++ return false;
+ }
+
+ static inline bool
+@@ -538,6 +636,74 @@ addr_match_proto (const struct openvpn_s
+ : addr_port_match (a1, a2);
+ }
+
++static inline void
++addr_zero_host(struct openvpn_sockaddr *addr)
++{
++ switch(addr->addr.sa.sa_family) {
++ case AF_INET:
++ addr->addr.in4.sin_addr.s_addr = 0;
++ break;
++#ifdef USE_PF_INET6
++ case AF_INET6:
++ memset(&addr->addr.in6.sin6_addr, 0, sizeof (struct in6_addr));
++ break;
++#endif
++ }
++}
++
++static inline void
++addr_copy_sa(struct openvpn_sockaddr *dst, const struct openvpn_sockaddr *src)
++{
++ dst->addr = src->addr;
++}
++
++static inline void
++addr_copy_host(struct openvpn_sockaddr *dst, const struct openvpn_sockaddr *src)
++{
++ switch(src->addr.sa.sa_family) {
++ case AF_INET:
++ dst->addr.in4.sin_addr.s_addr = src->addr.in4.sin_addr.s_addr;
++ break;
++#ifdef USE_PF_INET6
++ case AF_INET6:
++ dst->addr.in6.sin6_addr = src->addr.in6.sin6_addr;
++ break;
++#endif
++ }
++}
++
++static inline bool
++addr_inet4or6(struct sockaddr *addr)
++{
++ return addr->sa_family == AF_INET || addr->sa_family == AF_INET6;
++}
++
++int addr_guess_family(int proto, const char *name);
++static inline int
++af_addr_size(sa_family_t af)
++{
++#if defined(USE_PF_INET6) || defined (USE_PF_UNIX)
++ switch(af) {
++ case AF_INET: return sizeof (struct sockaddr_in);
++#ifdef USE_PF_UNIX
++ case AF_UNIX: return sizeof (struct sockaddr_un);
++#endif
++#ifdef USE_PF_INET6
++ case AF_INET6: return sizeof (struct sockaddr_in6);
++#endif
++ default:
++#if 0
++ /* could be called from socket_do_accept() with empty addr */
++ msg (M_ERR, "Bad address family: %d\n", addr->sa_family);
++ ASSERT(0);
++#endif
++ return 0;
++ }
++#else /* only AF_INET */
++ return sizeof(struct sockaddr_in);
++#endif
++}
++
+ static inline bool
+ link_socket_actual_match (const struct link_socket_actual *a1, const struct link_socket_actual *a2)
+ {
+@@ -594,14 +760,18 @@ link_socket_verify_incoming_addr (struct
+ {
+ if (buf->len > 0)
+ {
+- if (from_addr->dest.sa.sin_family != AF_INET)
+- return false;
+- if (!link_socket_actual_defined (from_addr))
+- return false;
+- if (info->remote_float || !addr_defined (&info->lsa->remote))
+- return true;
+- if (addr_match_proto (&from_addr->dest, &info->lsa->remote, info->proto))
+- return true;
++ switch (from_addr->dest.addr.sa.sa_family) {
++#ifdef USE_PF_INET6
++ case AF_INET6:
++#endif
++ case AF_INET:
++ if (!link_socket_actual_defined (from_addr))
++ return false;
++ if (info->remote_float || !addr_defined (&info->lsa->remote))
++ return true;
++ if (addr_match_proto (&from_addr->dest, &info->lsa->remote, info->proto))
++ return true;
++ }
+ }
+ return false;
+ }
+@@ -707,7 +877,7 @@ link_socket_read (struct link_socket *so
+ int maxsize,
+ struct link_socket_actual *from)
+ {
+- if (sock->info.proto == PROTO_UDPv4)
++ if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */
+ {
+ int res;
+
+@@ -718,10 +888,10 @@ #else
+ #endif
+ return res;
+ }
+- else if (sock->info.proto == PROTO_TCPv4_SERVER || sock->info.proto == PROTO_TCPv4_CLIENT)
++ else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */
+ {
+ /* from address was returned by accept */
+- from->dest.sa = sock->info.lsa->actual.dest.sa;
++ addr_copy_sa(&from->dest, &sock->info.lsa->actual.dest);
+ return link_socket_read_tcp (sock, buf);
+ }
+ else
+@@ -776,13 +946,14 @@ #if ENABLE_IP_PKTINFO
+ struct buffer *buf,
+ struct link_socket_actual *to);
+
+- if (sock->sockflags & SF_USE_IP_PKTINFO)
++ if (proto_is_udp(sock->info.proto) && (sock->sockflags & SF_USE_IP_PKTINFO)
++ && addr_defined_ipi(to))
+ return link_socket_write_udp_posix_sendmsg (sock, buf, to);
+ else
+ #endif
+ return sendto (sock->sd, BPTR (buf), BLEN (buf), 0,
+- (struct sockaddr *) &to->dest.sa,
+- (socklen_t) sizeof (to->dest.sa));
++ (struct sockaddr *) &to->dest.addr.sa,
++ (socklen_t) af_addr_size(to->dest.addr.sa.sa_family));
+ }
+
+ static inline int
+@@ -813,11 +984,11 @@ link_socket_write (struct link_socket *s
+ struct buffer *buf,
+ struct link_socket_actual *to)
+ {
+- if (sock->info.proto == PROTO_UDPv4)
++ if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */
+ {
+ return link_socket_write_udp (sock, buf, to);
+ }
+- else if (sock->info.proto == PROTO_TCPv4_SERVER || sock->info.proto == PROTO_TCPv4_CLIENT)
++ else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */
+ {
+ return link_socket_write_tcp (sock, buf, to);
+ }
+diff --git a/openvpn/socks.c b/openvpn/socks.c
+index cc9d82f..7ec9a3e 100644
+--- a/openvpn/socks.c
++++ b/openvpn/socks.c
+@@ -175,9 +175,9 @@ recv_socks_reply (socket_descriptor_t sd
+
+ if (addr != NULL)
+ {
+- addr->sa.sin_family = AF_INET;
+- addr->sa.sin_addr.s_addr = htonl (INADDR_ANY);
+- addr->sa.sin_port = htons (0);
++ addr->addr.in4.sin_family = AF_INET;
++ addr->addr.in4.sin_addr.s_addr = htonl (INADDR_ANY);
++ addr->addr.in4.sin_port = htons (0);
+ }
+
+ while (len < 4 + alen + 2)
+@@ -264,8 +264,8 @@ recv_socks_reply (socket_descriptor_t sd
+ /* ATYP == 1 (IP V4 address) */
+ if (atyp == '\x01' && addr != NULL)
+ {
+- memcpy (&addr->sa.sin_addr, buf + 4, sizeof (addr->sa.sin_addr));
+- memcpy (&addr->sa.sin_port, buf + 8, sizeof (addr->sa.sin_port));
++ memcpy (&addr->addr.in4.sin_addr, buf + 4, sizeof (addr->addr.in4.sin_addr));
++ memcpy (&addr->addr.in4.sin_port, buf + 8, sizeof (addr->addr.in4.sin_port));
+ }
+
+
+@@ -383,8 +383,8 @@ socks_process_incoming_udp (struct buffe
+ if (atyp != 1) /* ATYP == 1 (IP V4) */
+ goto error;
+
+- buf_read (buf, &from->dest.sa.sin_addr, sizeof (from->dest.sa.sin_addr));
+- buf_read (buf, &from->dest.sa.sin_port, sizeof (from->dest.sa.sin_port));
++ buf_read (buf, &from->dest.addr.in4.sin_addr, sizeof (from->dest.addr.in4.sin_addr));
++ buf_read (buf, &from->dest.addr.in4.sin_port, sizeof (from->dest.addr.in4.sin_port));
+
+ return;
+
+@@ -416,8 +416,8 @@ socks_process_outgoing_udp (struct buffe
+ buf_write_u16 (&head, 0); /* RSV = 0 */
+ buf_write_u8 (&head, 0); /* FRAG = 0 */
+ buf_write_u8 (&head, '\x01'); /* ATYP = 1 (IP V4) */
+- buf_write (&head, &to->dest.sa.sin_addr, sizeof (to->dest.sa.sin_addr));
+- buf_write (&head, &to->dest.sa.sin_port, sizeof (to->dest.sa.sin_port));
++ buf_write (&head, &to->dest.addr.in4.sin_addr, sizeof (to->dest.addr.in4.sin_addr));
++ buf_write (&head, &to->dest.addr.in4.sin_port, sizeof (to->dest.addr.in4.sin_port));
+
+ return 10;
+ }
diff --git a/net-misc/openvpn/files/openvpn-2.1_rc7-tap.patch b/net-misc/openvpn/files/openvpn-2.1_rc7-tap.patch
new file mode 100644
index 00000000..0220194a
--- /dev/null
+++ b/net-misc/openvpn/files/openvpn-2.1_rc7-tap.patch
@@ -0,0 +1,57 @@
+Index: tun.c
+===================================================================
+--- tun.c (revision 2713)
++++ tun.c (revision 2715)
+@@ -1220,26 +1220,44 @@
+ {
+ if (tt)
+ {
+-#ifdef CONFIG_FEATURE_IPROUTE
+ if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig)
+ {
+ char command_line[256];
+ struct gc_arena gc = gc_new ();
+
++#ifdef CONFIG_FEATURE_IPROUTE
++ if (is_tun_p2p (tt))
++ {
++ openvpn_snprintf (command_line, sizeof (command_line),
++ "%s addr del dev %s local %s peer %s",
++ iproute_path,
++ tt->actual_name,
++ print_in_addr_t (tt->local, 0, &gc),
++ print_in_addr_t (tt->remote_netmask, 0, &gc)
++ );
++ }
++ else
++ {
++ openvpn_snprintf (command_line, sizeof (command_line),
++ "%s addr del dev %s %s/%d",
++ iproute_path,
++ tt->actual_name,
++ print_in_addr_t (tt->local, 0, &gc),
++ count_netmask_bits(print_in_addr_t (tt->remote_netmask, 0, &gc))
++ );
++ }
++#else
+ openvpn_snprintf (command_line, sizeof (command_line),
+- "%s addr del dev %s local %s peer %s",
+- iproute_path,
+- tt->actual_name,
+- print_in_addr_t (tt->local, 0, &gc),
+- print_in_addr_t (tt->remote_netmask, 0, &gc)
+- );
++ IFCONFIG_PATH "%s addr 0.0.0.0",
++ tt->actual_name
++ );
++#endif
+
+ msg (M_INFO, "%s", command_line);
+ system_check (command_line, NULL, S_FATAL, "Linux ip addr del failed");
+
+ gc_free (&gc);
+ }
+-#endif
+ close_tun_generic (tt);
+ free (tt);
+ }
diff --git a/net-misc/openvpn/files/up.sh b/net-misc/openvpn/files/up.sh
new file mode 100755
index 00000000..d0879b83
--- /dev/null
+++ b/net-misc/openvpn/files/up.sh
@@ -0,0 +1,82 @@
+#!/bin/sh
+# Copyright (c) 2006-2007 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# Contributed by Roy Marples (uberlord@gentoo.org)
+
+# Setup our resolv.conf
+# Vitally important that we use the domain entry in resolv.conf so we
+# can setup the nameservers are for the domain ONLY in resolvconf if
+# we're using a decent dns cache/forwarder like dnsmasq and NOT nscd/libc.
+# nscd/libc users will get the VPN nameservers before their other ones
+# and will use the first one that responds - maybe the LAN ones?
+# non resolvconf users just the the VPN resolv.conf
+
+# FIXME:- if we have >1 domain, then we have to use search :/
+# We need to add a flag to resolvconf to say
+# "these nameservers should only be used for the listed search domains
+# if other global nameservers are present on other interfaces"
+# This however, will break compatibility with Debians resolvconf
+# A possible workaround would be to just list multiple domain lines
+# and try and let resolvconf handle it
+
+if [ "${PEER_DNS}" != "no" ]; then
+ NS=
+ DOMAIN=
+ SEARCH=
+ i=1
+ while true ; do
+ eval opt=\$foreign_option_${i}
+ [ -z "${opt}" ] && break
+ if [ "${opt}" != "${opt#dhcp-option DOMAIN *}" ] ; then
+ if [ -z "${DOMAIN}" ] ; then
+ DOMAIN="${opt#dhcp-option DOMAIN *}"
+ else
+ SEARCH="${SEARCH:+ }${opt#dhcp-option DOMAIN *}"
+ fi
+ elif [ "${opt}" != "${opt#dhcp-option DNS *}" ] ; then
+ NS="${NS}nameserver ${opt#dhcp-option DNS *}\n"
+ fi
+ i=$((${i} + 1))
+ done
+
+ if [ -n "${NS}" ] ; then
+ DNS="# Generated by openvpn for interface ${dev}\n"
+ if [ -n "${SEARCH}" ] ; then
+ DNS="${DNS}search ${DOMAIN} ${SEARCH}\n"
+ else
+ DNS="${DNS}domain ${DOMAIN}\n"
+ fi
+ DNS="${DNS}${NS}"
+ if [ -x /sbin/resolvconf ] ; then
+ printf "${DNS}" | /sbin/resolvconf -a "${dev}"
+ else
+ # Preserve the existing resolv.conf
+ if [ -e /etc/resolv.conf ] ; then
+ cp /etc/resolv.conf /etc/resolv.conf-"${dev}".sv
+ fi
+ printf "${DNS}" > /etc/resolv.conf
+ chmod 644 /etc/resolv.conf
+ fi
+ fi
+fi
+
+# Below section is Gentoo specific
+# Quick summary - our init scripts are re-entrant and set the SVCNAME env var
+# as we could have >1 openvpn service
+
+if [ -n "${SVCNAME}" ]; then
+ # If we have a service specific script, run this now
+ if [ -x /etc/openvpn/"${SVCNAME}"-up.sh ] ; then
+ /etc/openvpn/"${SVCNAME}"-up.sh "$@"
+ fi
+
+ # Re-enter the init script to start any dependant services
+ if ! /etc/init.d/"${SVCNAME}" --quiet status ; then
+ export IN_BACKGROUND=true
+ /etc/init.d/${SVCNAME} --quiet start
+ fi
+fi
+
+exit 0
+
+# vim: ts=4 :
diff --git a/net-misc/openvpn/openvpn-2.1_rc7.ebuild b/net-misc/openvpn/openvpn-2.1_rc7.ebuild
new file mode 100644
index 00000000..326f8739
--- /dev/null
+++ b/net-misc/openvpn/openvpn-2.1_rc7.ebuild
@@ -0,0 +1,156 @@
+# Copyright 1999-2008 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: /var/cvsroot/gentoo-x86/net-misc/openvpn/openvpn-2.1_rc7.ebuild,v 1.1 2008/02/09 16:20:56 alonbl Exp $
+
+inherit eutils multilib
+
+DESCRIPTION="OpenVPN is a robust and highly flexible tunneling application compatible with many OSes."
+SRC_URI="http://openvpn.net/release/${P}.tar.gz"
+HOMEPAGE="http://openvpn.net/"
+
+LICENSE="GPL-2"
+SLOT="0"
+KEYWORDS="~alpha ~amd64 ~hppa ~ppc ~ppc64 ~sh ~sparc ~sparc-fbsd ~x86 ~x86-fbsd"
+IUSE="examples iproute2 ipv6 minimal pam passwordsave selinux ssl static pkcs11 threads userland_BSD"
+
+DEPEND=">=dev-libs/lzo-1.07
+ kernel_linux? (
+ iproute2? ( sys-apps/iproute2 ) !iproute2? ( sys-apps/net-tools )
+ )
+ !minimal? ( pam? ( virtual/pam ) )
+ selinux? ( sec-policy/selinux-openvpn )
+ ssl? ( >=dev-libs/openssl-0.9.6 )
+ pkcs11? ( >=dev-libs/pkcs11-helper-1.05 )"
+
+pkg_setup() {
+ if use iproute2 ; then
+ if built_with_use sys-apps/iproute2 minimal ; then
+ eerror "iproute2 support requires that sys-apps/iproute2 was not"
+ eerror "built with the minimal USE flag"
+ die "iproute2 support not available"
+ fi
+ fi
+}
+
+src_unpack() {
+ unpack ${A}
+ cd "${S}"
+
+ use ipv6 && epatch "${FILESDIR}/${PN}"-2.1_rc6-udp6.patch
+ epatch "${FILESDIR}/${P}-tap.patch"
+}
+
+src_compile() {
+ local myconf=""
+
+ if use minimal ; then
+ myconf="${myconf} --disable-plugins"
+ myconf="${myconf} --disable-pkcs11"
+ else
+ myconf="$(use_enable pkcs11)"
+ fi
+
+ econf ${myconf} \
+ $(use_enable ipv6) \
+ $(use_enable passwordsave password-save) \
+ $(use_enable ssl) \
+ $(use_enable ssl crypto) \
+ $(use_enable threads pthread) \
+ $(use_enable iproute2) \
+ || die "configure failed"
+
+ use static && sed -i -e '/^LIBS/s/LIBS = /LIBS = -static /' Makefile
+
+ emake || die "make failed"
+
+ if ! use minimal ; then
+ cd plugin
+ for i in $( ls 2>/dev/null ); do
+ [[ ${i} == "README" || ${i} == "examples" ]] && continue
+ [[ ${i} == "auth-pam" ]] && ! use pam && continue
+ einfo "Building ${i} plugin"
+ cd "${i}"
+ emake || die "make failed"
+ cd ..
+ done
+ cd ..
+ fi
+}
+
+src_install() {
+ make DESTDIR="${D}" install || die "make install failed"
+
+ # install documentation
+ dodoc AUTHORS ChangeLog PORTS README
+
+ # Empty dir
+ dodir /etc/openvpn
+ keepdir /etc/openvpn
+
+ # Install some helper scripts
+ exeinto /etc/openvpn
+ doexe "${FILESDIR}/up.sh"
+ doexe "${FILESDIR}/down.sh"
+
+ # Install the init script and config file
+ newinitd "${FILESDIR}/${PN}-2.1.init" openvpn
+ newconfd "${FILESDIR}/${PN}-2.1.conf" openvpn
+
+ # install examples, controlled by the respective useflag
+ if use examples ; then
+ # dodoc does not supportly support directory traversal, #15193
+ insinto /usr/share/doc/${PF}/examples
+ doins -r sample-{config-files,keys,scripts} contrib
+ prepalldocs
+ fi
+
+ # Install plugins and easy-rsa
+ if ! use minimal ; then
+ cd easy-rsa/2.0
+ make install "DESTDIR=${D}/usr/share/${PN}/easy-rsa"
+ cd ../..
+
+ exeinto "/usr/$(get_libdir)/${PN}"
+ doexe plugin/*/*.so
+ fi
+}
+
+pkg_postinst() {
+ # Add openvpn user so openvpn servers can drop privs
+ # Clients should run as root so they can change ip addresses,
+ # dns information and other such things.
+ enewgroup openvpn
+ enewuser openvpn "" "" "" openvpn
+
+ if [[ -n $(ls /etc/openvpn/*/local.conf 2>/dev/null) ]] ; then
+ ewarn "WARNING: The openvpn init script has changed"
+ ewarn ""
+ fi
+
+ einfo "The openvpn init script expects to find the configuration file"
+ einfo "openvpn.conf in /etc/openvpn along with any extra files it may need."
+ einfo ""
+ einfo "To create more VPNs, simply create a new .conf file for it and"
+ einfo "then create a symlink to the openvpn init script from a link called"
+ einfo "openvpn.newconfname - like so"
+ einfo " cd /etc/openvpn"
+ einfo " ${EDITOR##*/} foo.conf"
+ einfo " cd /etc/init.d"
+ einfo " ln -s openvpn openvpn.foo"
+ einfo ""
+ einfo "You can then treat openvpn.foo as any other service, so you can"
+ einfo "stop one vpn and start another if you need to."
+
+ if grep -Eq "^[ \t]*(up|down)[ \t].*" "${ROOT}/etc/openvpn"/*.conf 2>/dev/null ; then
+ ewarn ""
+ ewarn "WARNING: If you use the remote keyword then you are deemed to be"
+ ewarn "a client by our init script and as such we force up,down scripts."
+ ewarn "These scripts call /etc/openvpn/\$SVCNAME-{up,down}.sh where you"
+ ewarn "can move your scripts to."
+ fi
+
+ if ! use minimal ; then
+ einfo ""
+ einfo "plugins have been installed into /usr/$(get_libdir)/${PN}"
+ fi
+}