summaryrefslogtreecommitdiff
path: root/debian/patches
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches')
-rw-r--r--debian/patches/attemping_typo13
-rw-r--r--debian/patches/auth-pam_libpam_so_filename.patch16
-rw-r--r--debian/patches/client_hang_when_server_dont_push.patch53
-rw-r--r--debian/patches/close_socket_before_scripts.patch32
-rw-r--r--debian/patches/counter_type_for_bytes.patch15
-rw-r--r--debian/patches/debian_nogroup_for_sample_files.patch81
-rw-r--r--debian/patches/debian_openssl_vulnkeys.patch102
-rw-r--r--debian/patches/eurephia.patch81
-rw-r--r--debian/patches/ipv6-payload.patch4049
-rw-r--r--debian/patches/jjo-ipv6-support.patch4011
-rw-r--r--debian/patches/kfreebsd_support.patch29
-rw-r--r--debian/patches/manpage_dash_escaping.patch20
-rw-r--r--debian/patches/openvpn-pkcs11warn.patch28
-rw-r--r--debian/patches/remote_env.patch15
-rw-r--r--debian/patches/route_default_nil.patch15
-rw-r--r--debian/patches/series8
16 files changed, 8568 insertions, 0 deletions
diff --git a/debian/patches/attemping_typo b/debian/patches/attemping_typo
new file mode 100644
index 0000000..da3ec7e
--- /dev/null
+++ b/debian/patches/attemping_typo
@@ -0,0 +1,13 @@
+Index: openvpn-2.1.3/socket.c
+===================================================================
+--- openvpn-2.1.3.orig/socket.c 2010-09-29 13:08:31.548460785 +0200
++++ openvpn-2.1.3/socket.c 2010-09-29 13:11:08.149458043 +0200
+@@ -1894,7 +1894,7 @@
+
+ if (sb->len < 1 || sb->len > sb->maxlen)
+ {
+- msg (M_WARN, "WARNING: Bad encapsulated packet length from peer (%d), which must be > 0 and <= %d -- please ensure that --tun-mtu or --link-mtu is equal on both peers -- this condition could also indicate a possible active attack on the TCP link -- [Attemping restart...]", sb->len, sb->maxlen);
++ msg (M_WARN, "WARNING: Bad encapsulated packet length from peer (%d), which must be > 0 and <= %d -- please ensure that --tun-mtu or --link-mtu is equal on both peers -- this condition could also indicate a possible active attack on the TCP link -- [Attempting restart...]", sb->len, sb->maxlen);
+ stream_buf_reset (sb);
+ sb->error = true;
+ return false;
diff --git a/debian/patches/auth-pam_libpam_so_filename.patch b/debian/patches/auth-pam_libpam_so_filename.patch
new file mode 100644
index 0000000..b76779d
--- /dev/null
+++ b/debian/patches/auth-pam_libpam_so_filename.patch
@@ -0,0 +1,16 @@
+Description: Fix libpam.so filename to /lib/libpam.so.0 in pam plugin
+Author: Alberto Gonzalez Iniesta <agi@inittab.org>
+Bug-Debian: http://bugs.debian.org/306335
+Index: openvpn-2.2.1/plugin/auth-pam/auth-pam.c
+===================================================================
+--- openvpn-2.2.1.orig/plugin/auth-pam/auth-pam.c 2011-12-13 12:41:58.693936739 +0100
++++ openvpn-2.2.1/plugin/auth-pam/auth-pam.c 2011-12-13 12:42:00.146918574 +0100
+@@ -688,7 +688,7 @@
+ struct user_pass up;
+ int command;
+ #if DLOPEN_PAM
+- static const char pam_so[] = "libpam.so";
++ static const char pam_so[] = "libpam.so.0";
+ #endif
+
+ /*
diff --git a/debian/patches/client_hang_when_server_dont_push.patch b/debian/patches/client_hang_when_server_dont_push.patch
new file mode 100644
index 0000000..3e56613
--- /dev/null
+++ b/debian/patches/client_hang_when_server_dont_push.patch
@@ -0,0 +1,53 @@
+Description: When the client sends PUSH_REQUESTS, it waits until the server
+ sends PUSH_REPLY. If the server do not have anything to push to the client
+ nothing happens. The client will then regularly send new PUSH_REQUESTS until
+ it gets an answer, which results in not completing the connection negotiation.
+ This patch makes the server send an empty PUSH_REPLY when it has nothing
+ more to push to the client.
+Author: David Sommerseth <dazo@users.sourceforge.net>
+Origin: upstream, https://community.openvpn.net/openvpn/attachment/ticket/13/0001-Fixed-client-hang-when-server-don-t-PUSH-aka-the-NO_.patch
+Bug: https://community.openvpn.net/openvpn/ticket/13
+Reviewed-By: James Yonan <james@openvpn.net>
+
+Index: openvpn-2.1.3/push.c
+===================================================================
+--- openvpn-2.1.3.orig/push.c 2010-05-31 09:05:55.000000000 +0200
++++ openvpn-2.1.3/push.c 2010-09-29 13:15:46.788461606 +0200
+@@ -177,6 +177,7 @@
+ static char cmd[] = "PUSH_REPLY";
+ const int extra = 64; /* extra space for possible trailing ifconfig and push-continuation */
+ const int safe_cap = BCAP (&buf) - extra;
++ bool push_sent = false;
+
+ buf_printf (&buf, cmd);
+
+@@ -192,6 +193,7 @@
+ const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
+ if (!status)
+ goto fail;
++ push_sent = true;
+ multi_push = true;
+ buf_reset_len (&buf);
+ buf_printf (&buf, cmd);
+@@ -218,6 +220,21 @@
+ {
+ const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
+ if (!status)
++ goto fail;
++ push_sent = true;
++ }
++
++ /* If nothing have been pushed, send an empty push,
++ * as the client is expecting a response
++ */
++ if (!push_sent)
++ {
++ bool status = false;
++
++ buf_reset_len (&buf);
++ buf_printf (&buf, cmd);
++ status = send_control_channel_string (c, BSTR(&buf), D_PUSH);
++ if (!status)
+ goto fail;
+ }
+
diff --git a/debian/patches/close_socket_before_scripts.patch b/debian/patches/close_socket_before_scripts.patch
new file mode 100644
index 0000000..011a8e7
--- /dev/null
+++ b/debian/patches/close_socket_before_scripts.patch
@@ -0,0 +1,32 @@
+Description: Set socket's FD_CLOEXEC flag before calling up script
+ Moving the set_cloexec() call from link_socket_init_phase2() to
+ link_socket_init_phase1().
+Author: Julien Cristau <jcristau@debian.org>
+Bug-Debian: http://bugs.debian.org/367716
+
+Index: openvpn-2.2.0/socket.c
+===================================================================
+--- openvpn-2.2.0.orig/socket.c 2011-04-21 21:13:34.000000000 +0200
++++ openvpn-2.2.0/socket.c 2011-05-10 16:18:35.300018716 +0200
+@@ -1327,6 +1327,10 @@
+ resolve_bind_local (sock);
+ resolve_remote (sock, 1, NULL, NULL);
+ }
++
++ /* set socket file descriptor to not pass across execs, so that
++ scripts don't have access to it */
++ set_cloexec (sock->sd);
+ }
+
+ /* finalize socket initialization */
+@@ -1532,10 +1536,6 @@
+ /* set socket to non-blocking mode */
+ set_nonblock (sock->sd);
+
+- /* set socket file descriptor to not pass across execs, so that
+- scripts don't have access to it */
+- set_cloexec (sock->sd);
+-
+ #ifdef ENABLE_SOCKS
+ if (socket_defined (sock->ctrl_sd))
+ set_cloexec (sock->ctrl_sd);
diff --git a/debian/patches/counter_type_for_bytes.patch b/debian/patches/counter_type_for_bytes.patch
new file mode 100644
index 0000000..7763c9a
--- /dev/null
+++ b/debian/patches/counter_type_for_bytes.patch
@@ -0,0 +1,15 @@
+Index: openvpn-2.1.3/ssl.h
+===================================================================
+--- openvpn-2.1.3.orig/ssl.h 2011-03-11 12:35:32.000000000 +0100
++++ openvpn-2.1.3/ssl.h 2011-03-11 12:59:08.883318001 +0100
+@@ -378,8 +378,8 @@
+
+ struct buffer_list *paybuf;
+
+- int n_bytes; /* how many bytes sent/recvd since last key exchange */
+- int n_packets; /* how many packets sent/recvd since last key exchange */
++ counter_type n_bytes; /* how many bytes sent/recvd since last key exchange */
++ counter_type n_packets; /* how many packets sent/recvd since last key exchange */
+
+ /*
+ * If bad username/password, TLS connection will come up but 'authenticated' will be false.
diff --git a/debian/patches/debian_nogroup_for_sample_files.patch b/debian/patches/debian_nogroup_for_sample_files.patch
new file mode 100644
index 0000000..9325b4c
--- /dev/null
+++ b/debian/patches/debian_nogroup_for_sample_files.patch
@@ -0,0 +1,81 @@
+Description: Unpriviledged group in Debian is called nogroup instead of nobody
+Author: Alberto Gonzalez Iniesta <agi@inittab.org>
+Bug-Debian: http://bugs.debian.org/317987
+Index: openvpn-2.1_rc15/sample-config-files/server.conf
+===================================================================
+--- openvpn-2.1_rc15.orig/sample-config-files/server.conf 2009-04-30 12:58:46.468752631 +0200
++++ openvpn-2.1_rc15/sample-config-files/server.conf 2009-04-30 12:59:03.535984196 +0200
+@@ -260,7 +260,7 @@
+ # You can uncomment this out on
+ # non-Windows systems.
+ ;user nobody
+-;group nobody
++;group nogroup
+
+ # The persist options will try to avoid
+ # accessing certain resources on restart
+Index: openvpn-2.1_rc15/sample-config-files/tls-home.conf
+===================================================================
+--- openvpn-2.1_rc15.orig/sample-config-files/tls-home.conf 2009-04-30 12:58:46.484747045 +0200
++++ openvpn-2.1_rc15/sample-config-files/tls-home.conf 2009-04-30 12:59:03.536981682 +0200
+@@ -51,7 +51,7 @@
+ # "nobody" after initialization
+ # for extra security.
+ ; user nobody
+-; group nobody
++; group nogroup
+
+ # If you built OpenVPN with
+ # LZO compression, uncomment
+Index: openvpn-2.1_rc15/sample-config-files/static-home.conf
+===================================================================
+--- openvpn-2.1_rc15.orig/sample-config-files/static-home.conf 2009-04-30 12:58:46.505740620 +0200
++++ openvpn-2.1_rc15/sample-config-files/static-home.conf 2009-04-30 12:59:03.541035034 +0200
+@@ -40,7 +40,7 @@
+ # "nobody" after initialization
+ # for extra security.
+ ; user nobody
+-; group nobody
++; group nogroup
+
+ # If you built OpenVPN with
+ # LZO compression, uncomment
+Index: openvpn-2.1_rc15/sample-config-files/static-office.conf
+===================================================================
+--- openvpn-2.1_rc15.orig/sample-config-files/static-office.conf 2009-04-30 12:58:46.528733358 +0200
++++ openvpn-2.1_rc15/sample-config-files/static-office.conf 2009-04-30 12:59:03.549976933 +0200
+@@ -37,7 +37,7 @@
+ # "nobody" after initialization
+ # for extra security.
+ ; user nobody
+-; group nobody
++; group nogroup
+
+ # If you built OpenVPN with
+ # LZO compression, uncomment
+Index: openvpn-2.1_rc15/sample-config-files/client.conf
+===================================================================
+--- openvpn-2.1_rc15.orig/sample-config-files/client.conf 2009-04-30 12:58:46.548728609 +0200
++++ openvpn-2.1_rc15/sample-config-files/client.conf 2009-04-30 12:59:03.551976933 +0200
+@@ -59,7 +59,7 @@
+
+ # Downgrade privileges after initialization (non-Windows only)
+ ;user nobody
+-;group nobody
++;group nogroup
+
+ # Try to preserve some state across restarts.
+ persist-key
+Index: openvpn-2.1_rc15/sample-config-files/tls-office.conf
+===================================================================
+--- openvpn-2.1_rc15.orig/sample-config-files/tls-office.conf 2009-04-30 12:58:46.574722464 +0200
++++ openvpn-2.1_rc15/sample-config-files/tls-office.conf 2009-04-30 12:59:03.553977212 +0200
+@@ -51,7 +51,7 @@
+ # "nobody" after initialization
+ # for extra security.
+ ; user nobody
+-; group nobody
++; group nogroup
+
+ # If you built OpenVPN with
+ # LZO compression, uncomment
diff --git a/debian/patches/debian_openssl_vulnkeys.patch b/debian/patches/debian_openssl_vulnkeys.patch
new file mode 100644
index 0000000..b0d3045
--- /dev/null
+++ b/debian/patches/debian_openssl_vulnkeys.patch
@@ -0,0 +1,102 @@
+Index: openvpn-2.1.3/init.c
+===================================================================
+--- openvpn-2.1.3.orig/init.c 2010-07-21 21:08:41.000000000 +0200
++++ openvpn-2.1.3/init.c 2010-09-29 13:11:02.373457337 +0200
+@@ -1796,6 +1796,29 @@
+ const struct options *options = &c->options;
+ ASSERT (options->shared_secret_file);
+
++ /* CVE-2008-0166 (Debian weak key checks) */
++ /* Only check if we can actually read the key file. Unless the file does not
++ * exist in the first place, this should never happen (since static keys do
++ * not work with multi-client mode), but we test it anyway to be on the safe
++ * side and avoid wrong -vulnkey alerts. */
++ if (access (options->shared_secret_file, R_OK) == 0)
++ {
++ struct argv argv = argv_new ();
++ int ret;
++ argv_printf (&argv, "/usr/sbin/openvpn-vulnkey -q %s", options->shared_secret_file);
++ argv_msg (M_INFO, &argv);
++ ret = openvpn_execve (&argv, c->c2.es, 0);
++ if (WEXITSTATUS (ret) == 1)
++ {
++ msg (M_WARN, "******* WARNING *******: '%s' is a known vulnerable key. See 'man openvpn-vulnkey' for details.", options->shared_secret_file);
++ }
++ else if (WEXITSTATUS (ret) != 0)
++ {
++ msg (M_WARN, "******* WARNING *******: '%s' cannot be verified as a non-vulnerable key. See 'man openvpn-vulnkey' for details.", options->shared_secret_file);
++ }
++ argv_reset (&argv);
++ }
++
+ init_crypto_pre (c, flags);
+
+ /* Initialize packet ID tracking */
+@@ -1881,6 +1904,7 @@
+ do_init_crypto_tls_c1 (struct context *c)
+ {
+ const struct options *options = &c->options;
++ SSL *ssl;
+
+ if (!c->c1.ks.ssl_ctx)
+ {
+@@ -1920,6 +1944,59 @@
+ /* Initialize PRNG with config-specified digest */
+ prng_init (options->prng_hash, options->prng_nonce_secret_len);
+
++ /* CVE-2008-0166 (Debian weak key checks)
++ * Obtain the modulus and bits from the certificate that was initialized,
++ * and send that to openssl-vulnkey.
++ */
++ ssl = SSL_new(c->c1.ks.ssl_ctx);
++ if (ssl != NULL)
++ {
++ X509* cert = NULL;
++ char *bn;
++ int bits;
++
++ cert = SSL_get_certificate(ssl);
++ if (cert != NULL)
++ {
++ EVP_PKEY *pkey = X509_get_pubkey (cert);
++ if (pkey != NULL)
++ {
++ if (pkey->type == EVP_PKEY_RSA && pkey->pkey.rsa != NULL
++ && pkey->pkey.rsa->n != NULL)
++ {
++ bits = BN_num_bits(pkey->pkey.rsa->n);
++ bn = BN_bn2hex(pkey->pkey.rsa->n);
++ }
++ else if (pkey->type == EVP_PKEY_DSA && pkey->pkey.dsa != NULL
++ && pkey->pkey.dsa->p != NULL)
++ {
++ bits = BN_num_bits(pkey->pkey.dsa->p);
++ bn = BN_bn2hex(pkey->pkey.dsa->p);
++ }
++ if (bn != NULL)
++ {
++ int ret;
++ struct argv argv = argv_new ();
++ argv_printf (&argv, "/usr/bin/openssl-vulnkey -q -b %d -m %s", bits, bn);
++ OPENSSL_free(bn);
++ msg (M_INFO, "/usr/bin/openssl-vulnkey -q -b %d -m <modulus omitted>", bits);
++ ret = openvpn_execve (&argv, NULL, 0);
++ if (WEXITSTATUS (ret) == 1)
++ {
++ msg (M_WARN, "******* WARNING *******: '%s' is a known vulnerable key. See 'man openssl-vulnkey' for details.", options->priv_key_file);
++ }
++ else if (WEXITSTATUS (ret) != 0)
++ {
++ msg (M_WARN, "******* WARNING *******: '%s' cannot be verified as a non-vulnerable key. See 'man openssl-vulnkey' for details.", options->priv_key_file);
++ }
++ argv_reset (&argv);
++ }
++ EVP_PKEY_free (pkey);
++ }
++ }
++ SSL_free(ssl);
++ }
++
+ /* TLS handshake authentication (--tls-auth) */
+ if (options->tls_auth_file)
+ {
diff --git a/debian/patches/eurephia.patch b/debian/patches/eurephia.patch
new file mode 100644
index 0000000..78340df
--- /dev/null
+++ b/debian/patches/eurephia.patch
@@ -0,0 +1,81 @@
+Index: openvpn-2.2.0/README.eurephia
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ openvpn-2.2.0/README.eurephia 2011-05-10 16:33:23.900007905 +0200
+@@ -0,0 +1,24 @@
++
++ OpenVPN - eurephia version
++==============================
++
++This is the official OpenVPN version, patched with a
++patch to implement one needed feature for the eurephia
++plug-in.
++
++All this patch does is to provide the plug-in environment
++with a variable containing the SHA1 hash of the
++certificates in use for the session.
++
++eurephia is an authentication and security plug-in which
++enhances the security in OpenVPN even more. It provides
++user name/password authentication, automatic blacklisting
++of user account, certificates and IP addresses. In
++provides in addition automatic updates of the iptables
++firewall on Linux, with specific iptables profile per
++user and certificate.
++
++For more information about eurephia, have a look at:
++
++ http://www.eurephia.net/
++
+Index: openvpn-2.2.0/options.c
+===================================================================
+--- openvpn-2.2.0.orig/options.c 2011-05-10 16:30:14.928001206 +0200
++++ openvpn-2.2.0/options.c 2011-05-10 16:33:23.900007905 +0200
+@@ -10,6 +10,9 @@
+ * Additions for eurephia plugin done by:
+ * David Sommerseth <dazo@users.sourceforge.net> Copyright (C) 2009
+ *
++ * Additions for eurephia plugin done by:
++ * David Sommerseth <dazo@users.sourceforge.net> Copyright (C) 2009
++ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+@@ -85,6 +88,7 @@
+ #ifdef USE_PF_INET6
+ " [PF_INET6]"
+ #endif
++ " [eurephia]"
+ " built on " __DATE__
+ ;
+
+Index: openvpn-2.2.0/ssl.c
+===================================================================
+--- openvpn-2.2.0.orig/ssl.c 2011-04-21 21:13:34.000000000 +0200
++++ openvpn-2.2.0/ssl.c 2011-05-10 16:33:23.904007483 +0200
+@@ -11,6 +11,10 @@
+ * David Sommerseth <dazo@users.sourceforge.net> Copyright (C) 2008-2009
+ *
+ *
++ * Additions for eurephia plugin done by:
++ * David Sommerseth <dazo@users.sourceforge.net> Copyright (C) 2008-2009
++ *
++ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+@@ -388,6 +392,14 @@
+ }
+ }
+
++ /* export X509 cert SHA1 fingerprint */
++ {
++ struct gc_arena gc = gc_new ();
++ openvpn_snprintf (envname, sizeof(envname), "tls_digest_%d", ctx->error_depth);
++ setenv_str (opt->es, envname,
++ format_hex_ex(ctx->current_cert->sha1_hash, SHA_DIGEST_LENGTH, 0, 1, ":", &gc));
++ gc_free(&gc);
++ }
+ #if 0
+ static void
+ cert_hash_print (const struct cert_hash_set *chs, int msglevel)
diff --git a/debian/patches/ipv6-payload.patch b/debian/patches/ipv6-payload.patch
new file mode 100644
index 0000000..cdc396a
--- /dev/null
+++ b/debian/patches/ipv6-payload.patch
@@ -0,0 +1,4049 @@
+Description: IPv6 payload support
+Author: Gert Döring
+URL: http://www.greenie.net/ipv6/openvpn.html
+Index: openvpn-2.2.1/ChangeLog.IPv6
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ openvpn-2.2.1/ChangeLog.IPv6 2011-12-13 12:24:54.608739565 +0100
+@@ -0,0 +1,394 @@
++Do 31. Dez 15:32:40 CET 2009 Gert Doering
++
++ * Basic IPv6 p2mp functionality implemented
++
++ * new options:
++ - server-ipv6
++ - ifconfig-ipv6
++ - ifconfig-ipv6-pool
++ - route-ipv6
++ - iroute-ipv6
++
++ * modules touched:
++ - init.c: init & setup IPv6 route list & add/delete IPv6 routes
++ - tun.c: add "ifconfig" and "route" handling for IPv6
++ - multi.c: IPv6 ifconfig-pool assignments
++ put to route-hash table
++ push to client
++ - pool.c: extend pools to handle IPv4+IPv6, and also return IPv6 address
++ IPv6 address saved to file if ifconfig-pool-persist is set
++ (but ignored on read due to the way pools work)
++ - mroute.c: handle reading src/dst addresses from IPv6 packets
++ (so multi.c can check against route-hash table)
++ handle printing of IPv6 mroute_addr structure
++ - helper.c: implement "server-ipv6" macro (->ifconfig-ipv6, pool, ...)
++ - options.c: implement all the new options
++ add helper functions for IPv6 address handling
++ - forward.c: tell do_route() about IPv6 routes
++ - route.c: handle IPv6 route lists + route option lists
++ extend add_routes() to do IPv4 + IPv6 route lists
++ extend delete_routes() to do IPv4 + IPv6 route lists
++ implement add_route_ipv6(), delete_route_ipv6() to call
++ system-dependend external program to do the work
++ - push.c: handle pushing of "ifconfig-ipv6" option
++ - socket.c: helper function to check & print IPv6 address strings
++
++ * known issues:
++ - operating system support on all but Linux (ifconfig, route)
++ - route-ipv6 gateway handling
++ - iroute-ipv6 not implemented
++ - TAP support: ifconfig, routing (route needs gateway!)
++
++ * release as patch 20091231-1
++
++Thu Dec 31 17:02:08 CET 2009
++
++ * NetBSD port (NetBSD 3.1 on Sparc64)
++
++ * mroute.c, socket.c: make byte/word access to in6_addr more portable
++
++ * tun.c: fix IPv6 ifconfig arguments on NetBSD
++
++ still doesn't work on NetBSD 3.1, "ifconfig tun0 inet6..." errors with
++
++ ifconfig: SIOCAIFADDR: Address family not supported by protocol family
++
++ (sys/net/if_tun.c, needs to be revision 1.80 or later, NetBSD PR 32944,
++ included in NetBSD 4.0 and up)
++
++
++Fri Jan 1 14:07:15 CET 2010
++
++ * FreeBSD port (FreeBSD 6.3-p12 on i386)
++
++ * tun.c: implement IPv6 ifconfig setting for FreeBSD
++
++ * route.c: fix %s/%s argument to IPv6 route add/delete command for *BSD
++
++ * TEST SUCCESS: FreeBSD 6.3-p12, server-ipv6, route-ipv6, ccd/iroute-ipv6
++
++ * multi.c: implement setting and deleting of iroute-ipv6
++ (multi_add_iroutes(), multi_del_iroutes())
++ * mroute.c: add mroute_helper_add_iroute6(), mroute_helper_del_iroute6()
++ * mroute.h: add prototypes, increase MR_HELPER_NET_LEN to 129 (/0.../128)
++ * multi.c: zeroize host part of IPv6 iroutes in multi_learn_in6_addr()
++ * mroute.c: implement mroute_addr_mask_host_bits() for IPv6
++
++ * TEST SUCCESS: Linux 2.6.30 (Gentoo)/iproute2, server-ipv6, ccd/iroute-ipv6
++
++ * TEST SUCCESS: Linux 2.6.30 (Gentoo)/ifconfig, client-ipv6
++
++ * TEST FAIL: NetBSD 5.0, IPv6 client
++ - "ifconfig tun0 .../64" does not create a "connected" route
++ - adding routes fails
++
++ --> more work to do here.
++
++ * release as patch 20100101-1
++
++ * TEST FAIL:
++ FreeBSD 6.3-p12 server "--topology subnet"
++ Linux/ifconfig client
++ - BSD sends ICMP6 neighbor solicitations, which are ignored by Linux
++ - server tun interface is not in p2p mode, client tun interface *is*
++
++ * TEST SUCCESS: non-ipv6 enabled client -> "--server-ipv6" server
++ (warnings in the log file, but no malfunctions)
++
++
++Sat Jan 2 19:48:35 CET 2010
++
++ * tun.c: change "ipv6_support()", do not turn off tt->ipv6 unconditionally
++ if we don't know about OS IPv6 support - just log warning
++
++ * tun.c: implement "ifconfig inet6" setting for MacOS X / Darwin
++
++ * route.c: split *BSD system dependent part of add/delete_route_ipv6()
++ into FreeBSD/Dragonfly and NetBSD/Darwin/OpenBSD variants
++ ("2001:db8::/64" vs. "2001:db8:: --prefixlen 64").
++
++ * tun.c: on MacOS X, NetBSD and OpenBSD, explicitely set on-link route
++
++ * TEST SUCCESS: MacOS X, client-ipv6 with route-ipv6
++
++
++Sun Jan 3 10:55:31 CET 2010
++
++ * route.c: NetBSD fails with "-iface tun0", needs gateway address
++ (assume that the same syntax is needed for OpenBSD)
++
++ * route.h: introduce "remote_endpoint_ipv6" into "struct route_ipv6_list"
++
++ * init.c: pass "ifconfig_ipv6_remote" as gateway to init_route_ipv6_list()
++
++ * route.c:
++ - init_route_ipv6(): use "remote_endpoint_ipv6" as IPv6 gateway address
++ if no gateway was specified explicitely
++
++ - init_route_ipv6_list(): fill in "remote_endpoint_ipv6", if parseable
++
++ - get rid of "GATEWAY-LESS ROUTE6" warning
++
++ * route.c, add_route_ipv6()
++ - explicitely clear host bits of base address, to be able to more
++ easily set up "connected" /64 routes on NetBSD+Darwin
++
++ - split system-dependent part between Darwin and NetBSD/OpenBSD
++ (Darwin can use "-iface tun0", NetBSD/OpenBSD get gateway address)
++
++ - change Solaris comments from "known-broken" to "unknown"
++
++ * tun.c: rework NetBSD tunnel initialization and tun_read() / tun_write()
++ to work the same way OpenBSD and NetBSD do - tunnel is put into
++ "multi-af" mode, and all packet read/write activity is prepended by
++ a 32 bit value specifying the address family.
++
++ * TEST SUCCESS: NetBSD 5.0/Sparc64: client-ipv6 with route-ipv6
++
++ * TEST SUCCESS: MacOS X 10.5: client-ipv6 with route-ipv6
++
++ * (RE-)TEST SUCCESS: Linux/iproute2: server-ipv6
++ Linux/ifconfig: client-ipv6
++ FreeBSD 6.3: server-ipv6
++
++ * release as patch 20100103-1
++
++ * options.c: document all new options in "--help"
++
++ * tun.c: fix typo in Solaris-specific section
++
++ * socket.h, socket.c: change u_int32_t to uint32_t
++ (Solaris - and all the rest of the code uses "uintNN" anyway)
++
++Mon Jan 4 17:46:58 CET 2010
++
++ * socket.c: rework add_in6_addr() to use 32-bit access to struct in6_addr
++ (Solaris has no 16-bit values in union, but this is more elegant as well)
++
++ * tun.c: fix "ifconfig inet6" command for Solaris
++
++ * tun.c: make sure "tun0 inet6" is unplumbed first, cleanup leftovers
++
++ * route.c: add routes with "metric 0" on solaris, otherwise they just
++ don't work (someone who understands Solaris might want to fix this).
++
++ * Solaris "sort of" works now - ifconfig works, route add does not give
++ errors, "netstat -rn" looks right, but packets are discarded unless
++ the routes are installed with "metric 0". So we just use "metric 0"...
++
++ * CAVEAT: Solaris "ifconfig ... preferred" interferes with source address
++ selection. So if there are any active IPv6 interfaces configured with
++ "preferred", packets leaving out the tunnel will use the wrong source
++ IPv6 address. Not fixable from within OpenVPN.
++
++ * CAVEAT2: Solaris insists on doing DHCPv6 on tun0 interfaces by default,
++ so DHCPv6 solicitation packets will be seen. Since the server end has
++ no idea what to do with them, they are a harmless nuisance. Fixable
++ on the Solaris side via "ndpd.conf" (see ``man ifconfig'').
++
++ * release as patch 20100104-1
++
++Fri Jan 8 10:00:50 CET 2010
++
++ * import into git repository
++
++ * options.c: add sanity checks for most typical error cases
++ (--ifconfig-ipv6-pool configured with no --ifconfig-ipv6, etc)
++
++ * options.c: modify get_ipv6_addr() to be more flexible about netbits
++ (optional now, default to /64) and to return the address-without-netbits
++ string now (-> for options that want the IPv6 address in printable
++ form, but without /nn)
++
++ * options.c: modify --ifconfig-ipv6 to optionally accept /netbits,
++ you can do now "ifconfig-ipv6 2001:df8::1/64 2001:df8::2" or just
++ "ifconfig-ipv6 2001:df8::5 2001:df8::7", defaulting to /64
++
++ * options.h: add necessary structure elements for --ifconfig-ipv6-push
++
++ * options.c: implement "parse options" side of --ifconfig-ipv6-push
++
++Tue Jan 12 22:42:09 CET 2010
++
++ * tun.c: in TARGET_NETBSD #ifdef, distinguish between "old" code
++ (IPv4 only, but unmodified read/write) and "new" code (multi-af,
++ extra 32 bit AF on read/write of the tun interface) - pre-4.0
++ NetBSD systems don't have TUNSIFHEAD, no way to have common code.
++
++ * TEST SUCCESS: NetBSD 5.0/Sparc64: client-ipv6 with route-ipv6 (v4+v6)
++
++ * TEST SUCCESS: NetBSD 3.1/Sparc64: client-ipv6 with route-ipv6 (v4-only)
++
++Thu Jan 14 15:41:50 CET 2010
++
++ * multi.c: if "--ifconfig-push" is used together with "--ifconfig-ipv6-pool"
++ and no "--ifconfig-ipv6-push" is seen, issue warning - the current
++ implementation of pools has IPv6 tied to IPv4, so if v4 does not use
++ the pool, it breaks for IPv6. Not a *big* problem (since there is
++ enough v6, just give those users a static v6 address as well), but needs
++ to be pointed out clearly.
++
++ * release as patch 20100114-1
++
++Tue Feb 16 14:43:28 CET 2010
++
++ * options.c: print "IPv6 payload patch" release date in "--version"
++
++ * tun.c: undo change to init_tun() (moving "bool tun" and call to
++ "is_tun_p2p()" further up) - it wasn't needed and breaks "make check"
++
++ * git stuff: rebase on David Sommerseth's openvpn-testing git tree
++
++ * release as patch 20100216-1
++
++Fri Feb 26 19:59:01 CET 2010
++
++ * init.c: initialize tuntap->ipv6 in do_init_tun() (to make sure it's
++ always initialized early-enough, independent of the sequence of
++ do_ifconfig()/open_tun() [see ifconfig_order() in tun.h])
++
++ * tun.c, init.c: remove "bool ipv6" argument to tuncfg(), open_tun()
++ and open_tun_generic() - obsoleted by previous change
++
++ * tun.c: remove ipv6_support() - original purpose was unclear, and all
++ current platforms (except linux-very-old) fully support IPv6 now :-)
++
++ * tun.c: initial implementation of "netsh" IPv6-ifconfig for Win32
++
++ * RE-TEST SUCCESS: Linux/i386/ifconfig, client-tun/net30, v4+v6
++
++Sun Feb 28 17:05:57 CET 2010
++
++ * tun.c: NetBSD dependent part: correct destroying/re-creation of tun dev
++
++ * tun.c: move adding of "connected" IPv6 prefix to new helper function,
++ add_route_connected_v6_net()
++
++ * RE-TEST SUCCESS: NetBSD 5.0/Sparc64, client-tun/net30, v4+v6
++
++ * RE-TEST SUCCESS: NetBSD 3.1/Sparc64: client-tun/net30, v4-only
++
++ * RE-TEST SUCCESS: Linux/i386/iproute2: server-tun/net30, v4+v6
++
++ * tun.c: add #ifdef TARGET_DARWIN block for *_tun() functions, to
++ be able to modify close_tun() for unconfiguring IPv6
++
++ * tun.c: on close_tun() on MacOS X, need to de-configure "lo0" route for
++ configured IPv6 address
++
++ * RE-TEST SUCCESS: MacOS X (10.5)/i386: client-tun/net30, v4+v6
++
++ * route.c: implement ipv6 route adding / deletion via "netsh" for WIN32
++
++ * TEST FAIL: Windows XP fails, because the tun/tap driver does not
++ forward IPv6 frames kernel->userland if in "tun" mode
++
++ * options.c: set IPv6 version to 20100228-1
++
++ * release as patch 20100228-1
++
++Sun Mar 7 19:17:33 CET 2010
++
++ * options.c: set IPv6 version to 20100307-1
++
++ * TODO.IPv6: add note about OpenBSD TODO (#16)
++
++ * route.c: set (and remove) "magic next hop" fe80::8 for IPv6 routes on
++ Win32
++
++ * install-win32/settings.in: bump TAP driver version from 9.6 to 9.7
++ and TAP_RELDATE to "07/03/2010"
++
++ * tap-win32/proto.h: add data types and definitions needed for IPv6
++
++ * tap-win32/types.h: add m_UserToTap_IPv6 ethernet header for IPv6 packets
++
++ * tap-win32/tapdrvr.c: implement support for IPv6 in TUN mode:
++ - IPv6 packets User->OS need correct ether type
++ - IPv6 packets OS->User get correctly forwarded
++ - IPv6 neighbour discovery packets for "fe80::8" (magic address
++ installed as route-nexthop by OpenVPN.exe) get answered locally
++
++ * TEST SUCCESS: WindowsXP/32bit: client-tun/net30, v4+v6
++
++ * tun.c: if IPv6 requested in TUN mode, and TUN/TAP driver version
++ is older than 9.7, log warning and disable IPv6 (won't work anyway).
++
++ * release as patch 20100307-1
++
++Sat Jul 10 14:37:52 CEST 2010
++
++ * TEST SUCCESS: point-to-point tun mode with --ifconfig-ipv6 between
++ Solaris10/sparc and Linux (Michal Ludvig)
++ (using the whiteboard tun driver on Solaris, otherwise "no IPv6")
++
++Sun Aug 8 12:30:44 CEST 2010
++
++ * route.c: split NetBSD and OpenBSD parts of add_route_ipv6() and
++ delete_route_ipv6(), implement OpenBSD variant
++ (needs "-prefixlen nn" while NetBSD uses "/nn")
++
++ * tun.c: implement IPv6 ifconfig for OpenBSD
++
++ * tun.c: destroy tunX interface at tun_close() on OpenBSD (cleanup)
++
++ * TEST SUCCESS: OpenBSD 4.7: client-tun/net30, v4+v6
++
++Thu Sep 2 21:18:32 CEST 2010
++
++ * tun.c: the TAP binary in 2.2-beta3 has the IPv6 related changes, but
++ the version number is 9.8 now -> check for 9.8, not 9.7
++
++Wed Sep 22 22:20:37 CEST 2010
++
++ * tun.c: bugfix for Linux/iproute2/"topology subnet". Works :-)
++
++ * TEST SUCCESS: Linux/ifconfig: client-tun/net30+subnet, v4+v6
++
++ * TEST SUCCESS: Linux/iproute2: client-tun/net30+subnet, v4+v6
++
++ * options.c: tag as 20100922-1 so "allmerged" users can see IPv6 change
++
++Fri Sep 24 17:57:41 CEST 2010
++
++ * TEST SUCCESS: Linux/<both>: client-tap, v4+v6, ping6 on connected addr
++
++ * TEST FAIL: Linux/<both>: client-tap, v6, route6 (gateway missing)
++
++Do 21. Okt 19:36:49 CEST 2010
++
++ * t_client.sh.in: cherrypick commit f25fe91a40aa3f and 6f1e61b41be52
++ (proper exit codes to signal "SKIP" if we do not want to run)
++
++So 16. Jan 17:25:23 CET 2011
++
++ * tun.c, route.c: cherrypick 121755c2cb4891f and f0eac1a5979096c67
++ (TAP driver and "topology subnet" support for Solaris)
++
++ * tun.c: add IPv6 configuration for TAP interfaces (<device>:1 inet6)
++
++ * tun.c: on close_tun on Solaris, unplumb IPv6 TUN or TAP interfaces
++
++ * TEST SUCCESS: OpenSolaris: client-tun, v4+v6
++ TEST SUCCESS: OpenSolaris: client-tap, v4+v6, ping6 on connected addr
++ TEST FAIL: OpenSolaris: client-tap, v6, route6 (gateway missing)
++
++So 24. Apr 16:51:45 CEST 2011
++
++ * rebase to "beta2.2" branch (at 2.2RC2 tag)
++
++ * mroute.c: remove mroute_helper_lock/_unlock() calls for IPv6
++ * socket.c: remove locking with L_INET_NTOA mutex
++ (all the threading stuff got removed by David Sommerseth for 2.2)
++
++ * mroute.c: remove duplicate mroute_helper_add_iroute6() and
++ mroute_helper_del_iroute6() - "git rebase" artefact
++
++ * ChangeLog.IPv6 and TODO.IPv6: add to commit
++
++ * options.c: tag as 20110424-2 (2.2RC2)
++
++ * TEST SUCCESS: Linux/ifconfig: client-tun/net30+subnet, v4+v6
++
++ * TEST SUCCESS: Linux/iproute2: client-tun/net30+subnet, v4+v6
++
+Index: openvpn-2.2.1/README.IPv6
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ openvpn-2.2.1/README.IPv6 2011-12-13 12:24:54.608739565 +0100
+@@ -0,0 +1,8 @@
++This is an experimentally patched version of OpenVPN 2.1 with IPv6
++payload support.
++
++Go here for release notes and documentation:
++
++ http://www.greenie.net/ipv6/openvpn.html
++
++Gert Doering, 31.12.2009
+Index: openvpn-2.2.1/TODO.IPv6
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ openvpn-2.2.1/TODO.IPv6 2011-12-13 12:24:54.609739553 +0100
+@@ -0,0 +1,149 @@
++known issues for IPv6 payload support in OpenVPN
++-----------------------------------------------
++
++1.) "--topology subnet" doesn't work together with IPv6 payload on FreeBSD
++ (verified for FreeBSD server, Linux/ifconfig client, problems
++ with ICMP6 neighbor solicitations from BSD not being answered by Linux)
++
++2.) NetBSD IPv6 support doesn't work
++ ("connected" route is not auto-created, "route-ipv6" adding fails)
++
++ * fixed, 3.1.10 *
++
++3.) route deletion for IPv6 routes is not yet done
++
++ * fixed for configured routes, 3.1.10 *
++ * missing for manual-ifconfig-connected (NetBSD, Darwin, Win32)
++
++4.) do "ifconfig tun0 inet6 unplumb" or "ifconfig tun0 destroy" for
++ Solaris, *BSD, ... at program termination time, to clean up leftovers
++ (unless tunnel persistance is desired).
++
++ For Solaris, only the "ipv6 tun0" is affected, for the *BSDs all tun0
++ stay around.
++
++4a.) deconfigure IPv6 on tun interface on session termination, otherwise
++ one could end up with something like this (on NetBSD):
++
++tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
++ inet 10.9.0.18 -> 10.9.0.17 netmask 0xffffffff
++ inet6 fe80::a00:20ff:fece:d299%tun0 -> prefixlen 64 scopeid 0x3
++ inet6 2001:608:4:eff::2000:3 -> prefixlen 64
++ inet6 2001:608:4:eff::1:3 -> prefixlen 64
++
++ (pool was changed, previous address still active on tun0, breakage)
++
++ * semi-fixed for NetBSD, 28.2.10, always do tun0 destroy / tun0 create
++ before actual ifconfig -- tunnel still lingers after OpenVPN quits
++
++4b.) verify this - on FreeBSD, tun0 is auto-destroyed if created by
++ opening /dev/tun (and lingers if created by "ifconfig tun0 create")
++
++ -> use for persistant tunnels on not-linux?
++
++5.) add new option "ifconfig-ipv6-push"
++ (per-client static IPv6 assignment, -> radiusplugin, etc)
++
++ * implemented, 14.1.10 *
++
++6.) add new option "route-ipv6-gateway"
++
++7.) add "full" gateway handling for IPv6 in route.c
++ (right now, the routes are just sent down the tun interface, if the
++ operating system in questions supports that, without care for the
++ gateway address - which does not work for gateways that are supposed
++ to point elsewhere. Also, it doesn't work for TAP interfaces.
++
++8.) full IPv6 support for TAP interfaces
++ (main issue should be routes+gateway - and testing :-) )
++
++ test 2010/09/24: TAP itself works on linux/ifconfig+iproute2, but
++ route-via-tap doesn't work at all (route points to "tap0" which fails)
++
++17:51:14.075412 fe:ab:6e:c5:53:71 > 33:33:ff:00:00:01, ethertype IPv6 (0x86dd), length 86: 2001:608:4:a053::1:0 > ff02::1:ff00:1: ICMP6, neighbor solicitation, who has 2001:608:4:a001::1, length 32
++
++ how is iroute-via-tap supposed to work??
++
++9.) verify that iroute-ipv6 and route-ipv6 interact in the same way as
++ documented for iroute/route:
++
++ A's subnet, OpenVPN must push this route to all clients
++ EXCEPT for A, since the subnet is already owned by A.
++ OpenVPN accomplishes this by not
++ not pushing a route to a client
++ if it matches one of the client's iroutes.
++
++10.) extend "ifconfig-ipv6" to handle specification of /netbits, pushing
++ of /netbits, and correctly ifconfig'ing this
++ (default, if not specified: /64)
++
++11.) do not add ipv6-routes if tun-ipv6 is not set - complain instead
++
++ * done * 12.1.10
++
++12.) handle incoming [::] and [fe80:...] packets in tun-p2mp MULTI mode
++ (most likely those are DAD packets)
++ silently ignore DAD?
++ Or accept-and-forward iff (multicast && client2client)?
++ handle NS/NA
++
++13.) from Martin List-Petersen:
++
++ One thing, and I guess this requires modifications in
++ network-manager-openvpn: It also works, BUT ignores "push
++ route-ipv6-gateway" and "push route-ipv6 ...." (obviously routes pushed
++ from the server) entirely.
++
++14.) from ##openvpn-discussion:
++
++ new features should be #ifdef'ed
++
++ (check whether this is feasible at all)
++
++15.) IPv6 related environment variables
++
++ - document all of them in openvpn.8
++ - make sure that all existing IPv4 stuff has IPv6 counterparts
++
++16.) OpenBSD
++ - implement ifconfig/route for IPv6
++ - revert ifconfig/open_tun order to "normal" (separate commit!!!)
++ (openvpn-devel, Subject: OpenBSD)
++ - test
++
++17.) client-option (Elwood)
++ - ignore-v6-push-options yes/no
++ - ignore-v6-route-push ("as for IPv4 routes")
++
++18.) fail-save? "what if 'ip -6 addr add' fails" -> fail, or fallback to v4?
++ (-> recomment setting "ignore-v6-push-options yes")
++
++19.) safety check: if connecting over IPv6 (v6 transport) and the pushed
++ route-ipv6 network encompasses the server IPv6 address, make sure
++ we at least log a warning (until we can fiddle with external routing
++ to make this work correctly).
++
++20.) show "route add" / "route delete" commands for IPv6 in log file
++ (we show the "ifconfig" commands, so why not the routes?)
++
++ 2010-08-07: this is a null-feature - it's already there, but with
++ different debug level (M_INFO vs. D_ROUTE) so user
++ didn't notice
++
++21.) enable ipv6-only server operations
++ - decouple ipv6 pool handling from ipv4 pool
++ - make sure Rest of OpenVPN doesn't assume "there will always be IPv4"
++
++22.) implement --learn-address for IPv6
++
++23.) FreeBSD 8 seems to require explicit setting of the "ifconfig" IPv6
++ route, while FreeBSD 6+7 don't --> more testing, and code fix
++
++ workaround for the time being: just add
++
++ server-ipv6 2001:608:4:a051::/64
++ route-ipv6 2001:608:4:a051::/64
++
++ to the config
++
++ (problem + workaround applies both to tun and tap style devices)
+Index: openvpn-2.2.1/forward.c
+===================================================================
+--- openvpn-2.2.1.orig/forward.c 2011-06-24 08:13:38.000000000 +0200
++++ openvpn-2.2.1/forward.c 2011-12-13 12:24:54.611739529 +0100
+@@ -262,7 +262,8 @@
+ static void
+ check_add_routes_action (struct context *c, const bool errors)
+ {
+- do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->plugins, c->c2.es);
++ do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list,
++ c->c1.tuntap, c->plugins, c->c2.es);
+ update_time ();
+ event_timeout_clear (&c->c2.route_wakeup);
+ event_timeout_clear (&c->c2.route_wakeup_expire);
+Index: openvpn-2.2.1/helper.c
+===================================================================
+--- openvpn-2.2.1.orig/helper.c 2011-06-24 08:13:38.000000000 +0200
++++ openvpn-2.2.1/helper.c 2011-12-13 12:24:54.612739516 +0100
+@@ -142,6 +142,55 @@
+
+ #if P2MP
+ #if P2MP_SERVER
++
++ /*
++ *
++ * HELPER DIRECTIVE for IPv6
++ *
++ * server-ipv6 2001:db8::/64
++ *
++ * EXPANDS TO:
++ *
++ * tun-ipv6
++ * push "tun-ipv6"
++ * ifconfig-ipv6 2001:db8::1 2001:db8::2
++ * if !nopool:
++ * ifconfig-ipv6-pool 2001:db8::1:0/64
++ *
++ */
++ if ( o->server_ipv6_defined )
++ {
++ if ( ! o->server_defined )
++ {
++ msg (M_USAGE, "--server-ipv6 must be used together with --server");
++ }
++ if ( o->server_flags & SF_NOPOOL )
++ {
++ msg( M_USAGE, "--server-ipv6 is incompatible with 'nopool' option" );
++ }
++ if ( o->ifconfig_ipv6_pool_defined )
++ {
++ msg( M_USAGE, "--server-ipv6 already defines an ifconfig-ipv6-pool, so you can't also specify --ifconfig-pool explicitly");
++ }
++
++ /* local ifconfig is "base address + 1" and "+2" */
++ o->ifconfig_ipv6_local =
++ print_in6_addr( add_in6_addr( o->server_network_ipv6, 1), 0, &o->gc );
++ o->ifconfig_ipv6_remote =
++ print_in6_addr( add_in6_addr( o->server_network_ipv6, 2), 0, &o->gc );
++
++ /* pool starts at "base address + 0x10000" */
++ ASSERT( o->server_netbits_ipv6 < 96 ); /* want 32 bits */
++ o->ifconfig_ipv6_pool_defined = true;
++ o->ifconfig_ipv6_pool_base =
++ add_in6_addr( o->server_network_ipv6, 0x10000 );
++ o->ifconfig_ipv6_pool_netbits = o->server_netbits_ipv6;
++
++ o->tun_ipv6 = true;
++
++ push_option( o, "tun-ipv6", M_USAGE );
++ }
++
+ /*
+ *
+ * HELPER DIRECTIVE:
+Index: openvpn-2.2.1/init.c
+===================================================================
+--- openvpn-2.2.1.orig/init.c 2011-12-13 12:23:07.000000000 +0100
++++ openvpn-2.2.1/init.c 2011-12-13 12:24:54.615739477 +0100
+@@ -843,7 +843,7 @@
+ msg (M_FATAL|M_OPTERR,
+ "options --mktun or --rmtun should only be used together with --dev");
+ tuncfg (options->dev, options->dev_type, options->dev_node,
+- options->tun_ipv6, options->persist_mode,
++ options->persist_mode,
+ options->username, options->groupname, &options->tuntap_options);
+ if (options->persist_mode && options->lladdr)
+ set_lladdr(options->dev, options->lladdr, NULL);
+@@ -1066,6 +1066,8 @@
+ {
+ if (c->options.routes && !c->c1.route_list)
+ c->c1.route_list = new_route_list (c->options.max_routes, &c->gc);
++ if (c->options.routes_ipv6 && !c->c1.route_ipv6_list)
++ c->c1.route_ipv6_list = new_route_ipv6_list (c->options.max_routes, &c->gc);
+ }
+
+
+@@ -1108,6 +1110,45 @@
+ }
+ }
+
++static void
++do_init_route_ipv6_list (const struct options *options,
++ struct route_ipv6_list *route_ipv6_list,
++ bool fatal,
++ struct env_set *es)
++{
++ const char *gw = NULL;
++ int dev = dev_type_enum (options->dev, options->dev_type);
++ int metric = 0;
++
++ if (dev != DEV_TYPE_TUN )
++ msg( M_WARN, "IPv6 routes on TAP devices are going to fail on some platforms (need gateway spec)" ); /* TODO-GERT */
++
++ gw = options->ifconfig_ipv6_remote; /* default GW = remote end */
++#if 0 /* not yet done for IPv6 - TODO!*/
++ if ( options->route_ipv6_default_gateway ) /* override? */
++ gw = options->route_ipv6_default_gateway;
++#endif
++
++ if (options->route_default_metric)
++ metric = options->route_default_metric;
++
++ if (!init_route_ipv6_list (route_ipv6_list,
++ options->routes_ipv6,
++ gw,
++ metric,
++ es))
++ {
++ if (fatal)
++ openvpn_exit (OPENVPN_EXIT_STATUS_ERROR); /* exit point */
++ }
++ else
++ {
++ /* copy routes to environment */
++ setenv_routes_ipv6 (es, route_ipv6_list);
++ }
++}
++
++
+ /*
+ * Called after all initialization has been completed.
+ */
+@@ -1177,12 +1218,13 @@
+ void
+ do_route (const struct options *options,
+ struct route_list *route_list,
++ struct route_ipv6_list *route_ipv6_list,
+ const struct tuntap *tt,
+ const struct plugin_list *plugins,
+ struct env_set *es)
+ {
+- if (!options->route_noexec && route_list)
+- add_routes (route_list, tt, ROUTE_OPTION_FLAGS (options), es);
++ if (!options->route_noexec && ( route_list || route_ipv6_list ) )
++ add_routes (route_list, route_ipv6_list, tt, ROUTE_OPTION_FLAGS (options), es);
+
+ if (plugin_defined (plugins, OPENVPN_PLUGIN_ROUTE_UP))
+ {
+@@ -1239,11 +1281,16 @@
+ c->options.topology,
+ c->options.ifconfig_local,
+ c->options.ifconfig_remote_netmask,
++ c->options.ifconfig_ipv6_local,
++ c->options.ifconfig_ipv6_remote,
+ addr_host (&c->c1.link_socket_addr.local),
+ addr_host (&c->c1.link_socket_addr.remote),
+ !c->options.ifconfig_nowarn,
+ c->c2.es);
+
++ /* flag tunnel for IPv6 config if --tun-ipv6 is set */
++ c->c1.tuntap->ipv6 = c->options.tun_ipv6;
++
+ init_tun_post (c->c1.tuntap,
+ &c->c2.frame,
+ &c->options.tuntap_options);
+@@ -1275,6 +1322,8 @@
+ /* parse and resolve the route option list */
+ if (c->options.routes && c->c1.route_list && c->c2.link_socket)
+ do_init_route_list (&c->options, c->c1.route_list, &c->c2.link_socket->info, false, c->c2.es);
++ if (c->options.routes_ipv6 && c->c1.route_ipv6_list )
++ do_init_route_ipv6_list (&c->options, c->c1.route_ipv6_list, false, c->c2.es);
+
+ /* do ifconfig */
+ if (!c->options.ifconfig_noexec
+@@ -1291,7 +1340,7 @@
+
+ /* open the tun device */
+ open_tun (c->options.dev, c->options.dev_type, c->options.dev_node,
+- c->options.tun_ipv6, c->c1.tuntap);
++ c->c1.tuntap);
+
+ /* set the hardware address */
+ if (c->options.lladdr)
+@@ -1320,7 +1369,8 @@
+
+ /* possibly add routes */
+ if (!c->options.route_delay_defined)
+- do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->plugins, c->c2.es);
++ do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list,
++ c->c1.tuntap, c->plugins, c->c2.es);
+
+ /*
+ * Did tun/tap driver give us an MTU?
+@@ -1394,8 +1444,9 @@
+ #endif
+
+ /* delete any routes we added */
+- if (c->c1.route_list)
+- delete_routes (c->c1.route_list, c->c1.tuntap, ROUTE_OPTION_FLAGS (&c->options), c->c2.es);
++ if (c->c1.route_list || c->c1.route_ipv6_list )
++ delete_routes (c->c1.route_list, c->c1.route_ipv6_list,
++ c->c1.tuntap, ROUTE_OPTION_FLAGS (&c->options), c->c2.es);
+
+ /* actually close tun/tap device based on --down-pre flag */
+ if (!c->options.down_pre)
+Index: openvpn-2.2.1/init.h
+===================================================================
+--- openvpn-2.2.1.orig/init.h 2011-06-24 08:13:38.000000000 +0200
++++ openvpn-2.2.1/init.h 2011-12-13 12:24:54.615739477 +0100
+@@ -63,6 +63,7 @@
+
+ void do_route (const struct options *options,
+ struct route_list *route_list,
++ struct route_ipv6_list *route_ipv6_list,
+ const struct tuntap *tt,
+ const struct plugin_list *plugins,
+ struct env_set *es);
+Index: openvpn-2.2.1/misc.c
+===================================================================
+--- openvpn-2.2.1.orig/misc.c 2011-06-24 08:13:38.000000000 +0200
++++ openvpn-2.2.1/misc.c 2011-12-13 12:24:54.617739452 +0100
+@@ -1001,7 +1001,9 @@
+ {
+ const char *str = construct_name_value (name_tmp, val_tmp, &gc);
+ env_set_add (es, str);
+- /*msg (M_INFO, "SETENV_ES '%s'", str);*/
++#if DEBUG_VERBOSE_SETENV
++ msg (M_INFO, "SETENV_ES '%s'", str);
++#endif
+ }
+ else
+ env_set_del (es, name_tmp);
+Index: openvpn-2.2.1/mroute.c
+===================================================================
+--- openvpn-2.2.1.orig/mroute.c 2011-12-13 12:23:07.000000000 +0100
++++ openvpn-2.2.1/mroute.c 2011-12-13 12:24:54.618739440 +0100
+@@ -88,12 +88,33 @@
+ }
+ }
+
++static inline void
++mroute_get_in6_addr (struct mroute_addr *ma, const struct in6_addr src, unsigned int mask)
++{
++ if (ma)
++ {
++ ma->type = MR_ADDR_IPV6 | mask;
++ ma->netbits = 0;
++ ma->len = 16;
++ *(struct in6_addr *)ma->addr = src;
++ }
++}
++
+ static inline bool
+ mroute_is_mcast (const in_addr_t addr)
+ {
+ return ((addr & htonl(IP_MCAST_SUBNET_MASK)) == htonl(IP_MCAST_NETWORK));
+ }
+
++/* RFC 4291, 2.7, "binary 11111111 at the start of an address identifies
++ * the address as being a multicast address"
++ */
++static inline bool
++mroute_is_mcast_ipv6 (const struct in6_addr addr)
++{
++ return (addr.s6_addr[0] == 0xff);
++}
++
+ #ifdef ENABLE_PF
+
+ static unsigned int
+@@ -155,10 +176,29 @@
+ }
+ break;
+ case 6:
+- {
+- msg (M_WARN, "Need IPv6 code in mroute_extract_addr_from_packet");
+- break;
+- }
++ if (BLEN (buf) >= (int) sizeof (struct openvpn_ipv6hdr))
++ {
++ const struct openvpn_ipv6hdr *ipv6 = (const struct openvpn_ipv6hdr *) BPTR (buf);
++#if 0 /* very basic debug */
++ struct gc_arena gc = gc_new ();
++ msg( M_INFO, "IPv6 packet! src=%s, dst=%s",
++ print_in6_addr( ipv6->saddr, 0, &gc ),
++ print_in6_addr( ipv6->daddr, 0, &gc ));
++ gc_free (&gc);
++#endif
++
++ mroute_get_in6_addr (src, ipv6->saddr, 0);
++ mroute_get_in6_addr (dest, ipv6->daddr, 0);
++
++ if (mroute_is_mcast_ipv6 (ipv6->daddr))
++ ret |= MROUTE_EXTRACT_MCAST;
++
++ ret |= MROUTE_EXTRACT_SUCCEEDED;
++ }
++ break;
++ default:
++ msg (M_WARN, "IP packet with unknown IP version=%d seen",
++ OPENVPN_IPH_GET_VER (*BPTR(buf)));
+ }
+ }
+ return ret;
+@@ -274,14 +314,36 @@
+ * Zero off the host bits in an address, leaving
+ * only the network bits, using the netbits member of
+ * struct mroute_addr as the controlling parameter.
++ *
++ * TODO: this is called for route-lookup for every yet-unhashed
++ * destination address, so for lots of active net-iroutes, this
++ * might benefit from some "zeroize 32 bit at a time" improvements
+ */
+ void
+ mroute_addr_mask_host_bits (struct mroute_addr *ma)
+ {
+ in_addr_t addr = ntohl(*(in_addr_t*)ma->addr);
+- ASSERT ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4);
+- addr &= netbits_to_netmask (ma->netbits);
+- *(in_addr_t*)ma->addr = htonl (addr);
++ if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4)
++ {
++ addr &= netbits_to_netmask (ma->netbits);
++ *(in_addr_t*)ma->addr = htonl (addr);
++ }
++ else if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV6)
++ {
++ int byte = ma->len-1; /* rightmost byte in address */
++ int bits_to_clear = 128 - ma->netbits;
++
++ while( byte >= 0 && bits_to_clear > 0 )
++ {
++ if ( bits_to_clear >= 8 )
++ { ma->addr[byte--] = 0; bits_to_clear -= 8; }
++ else
++ { ma->addr[byte--] &= (~0 << bits_to_clear); bits_to_clear = 0; }
++ }
++ ASSERT( bits_to_clear == 0 );
++ }
++ else
++ ASSERT(0);
+ }
+
+ /*
+@@ -359,17 +421,24 @@
+ }
+ break;
+ case MR_ADDR_IPV6:
+- buf_printf (&out, "IPV6");
+- break;
+- default:
+- buf_printf (&out, "UNKNOWN");
+- break;
+- }
+- return BSTR (&out);
+- }
+- else
+- return "[NULL]";
+-}
++ {
++ buf_printf (&out, "%s",
++ print_in6_addr( *(struct in6_addr*)&maddr.addr, 0, gc));
++ if (maddr.type & MR_WITH_NETBITS)
++ {
++ buf_printf (&out, "/%d", maddr.netbits);
++ }
++ }
++ break;
++ default:
++ buf_printf (&out, "UNKNOWN");
++ break;
++ }
++ return BSTR (&out);
++ }
++ else
++ return "[NULL]";
++ }
+
+ /*
+ * mroute_helper's main job is keeping track of
+@@ -439,6 +508,40 @@
+ mroute_helper_regenerate (mh);
+ }
+ }
++
++/* this is a bit inelegant, we really should have a helper to that
++ * is only passed the netbits value, and not the whole struct iroute *
++ * - thus one helper could do IPv4 and IPv6. For the sake of "not change
++ * code unrelated to IPv4" this is left for later cleanup, for now.
++ */
++void
++mroute_helper_add_iroute6 (struct mroute_helper *mh,
++ const struct iroute_ipv6 *ir6)
++{
++ if (ir6->netbits >= 0)
++ {
++ ASSERT (ir6->netbits < MR_HELPER_NET_LEN);
++ ++mh->cache_generation;
++ ++mh->net_len_refcount[ir6->netbits];
++ if (mh->net_len_refcount[ir6->netbits] == 1)
++ mroute_helper_regenerate (mh);
++ }
++}
++
++void
++mroute_helper_del_iroute6 (struct mroute_helper *mh,
++ const struct iroute_ipv6 *ir6)
++{
++ if (ir6->netbits >= 0)
++ {
++ ASSERT (ir6->netbits < MR_HELPER_NET_LEN);
++ ++mh->cache_generation;
++ --mh->net_len_refcount[ir6->netbits];
++ ASSERT (mh->net_len_refcount[ir6->netbits] >= 0);
++ if (!mh->net_len_refcount[ir6->netbits])
++ mroute_helper_regenerate (mh);
++ }
++}
+
+ void
+ mroute_helper_free (struct mroute_helper *mh)
+Index: openvpn-2.2.1/mroute.h
+===================================================================
+--- openvpn-2.2.1.orig/mroute.h 2011-06-24 08:13:38.000000000 +0200
++++ openvpn-2.2.1/mroute.h 2011-12-13 12:24:54.618739440 +0100
+@@ -85,7 +85,7 @@
+ /*
+ * Number of bits in an address. Should be raised for IPv6.
+ */
+-#define MR_HELPER_NET_LEN 32
++#define MR_HELPER_NET_LEN 129
+
+ /*
+ * Used to help maintain CIDR routing table.
+@@ -127,6 +127,8 @@
+ void mroute_helper_free (struct mroute_helper *mh);
+ void mroute_helper_add_iroute (struct mroute_helper *mh, const struct iroute *ir);
+ void mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir);
++void mroute_helper_add_iroute6 (struct mroute_helper *mh, const struct iroute_ipv6 *ir6);
++void mroute_helper_del_iroute6 (struct mroute_helper *mh, const struct iroute_ipv6 *ir6);
+
+ /*
+ * Given a raw packet in buf, return the src and dest
+Index: openvpn-2.2.1/multi.c
+===================================================================
+--- openvpn-2.2.1.orig/multi.c 2011-12-13 12:23:07.000000000 +0100
++++ openvpn-2.2.1/multi.c 2011-12-13 12:24:54.621739404 +0100
+@@ -316,25 +316,18 @@
+ */
+ if (t->options.ifconfig_pool_defined)
+ {
+- if (dev == DEV_TYPE_TAP)
+- {
+- m->ifconfig_pool = ifconfig_pool_init (IFCONFIG_POOL_INDIV,
+- t->options.ifconfig_pool_start,
+- t->options.ifconfig_pool_end,
+- t->options.duplicate_cn);
+- }
+- else if (dev == DEV_TYPE_TUN)
+- {
+- m->ifconfig_pool = ifconfig_pool_init (
+- (t->options.topology == TOP_NET30) ? IFCONFIG_POOL_30NET : IFCONFIG_POOL_INDIV,
+- t->options.ifconfig_pool_start,
+- t->options.ifconfig_pool_end,
+- t->options.duplicate_cn);
+- }
+- else
+- {
+- ASSERT (0);
+- }
++ int pool_type = IFCONFIG_POOL_INDIV;
++
++ if ( dev == DEV_TYPE_TUN && t->options.topology == TOP_NET30 )
++ pool_type = IFCONFIG_POOL_30NET;
++
++ m->ifconfig_pool = ifconfig_pool_init (pool_type,
++ t->options.ifconfig_pool_start,
++ t->options.ifconfig_pool_end,
++ t->options.duplicate_cn,
++ t->options.ifconfig_ipv6_pool_defined,
++ t->options.ifconfig_ipv6_pool_base,
++ t->options.ifconfig_ipv6_pool_netbits );
+
+ /* reload pool data from file */
+ if (t->c1.ifconfig_pool_persist)
+@@ -429,10 +422,14 @@
+ struct multi_instance *mi)
+ {
+ const struct iroute *ir;
++ const struct iroute_ipv6 *ir6;
+ if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN)
+ {
+ for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
+ mroute_helper_del_iroute (m->route_helper, ir);
++
++ for ( ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next )
++ mroute_helper_del_iroute6 (m->route_helper, ir6);
+ }
+ }
+
+@@ -1078,6 +1075,37 @@
+ }
+ }
+
++static struct multi_instance *
++multi_learn_in6_addr (struct multi_context *m,
++ struct multi_instance *mi,
++ struct in6_addr a6,
++ int netbits, /* -1 if host route, otherwise # of network bits in address */
++ bool primary)
++{
++ struct mroute_addr addr;
++
++ addr.len = 16;
++ addr.type = MR_ADDR_IPV6;
++ addr.netbits = 0;
++ memcpy( &addr.addr, &a6, sizeof(a6) );
++
++ if (netbits >= 0)
++ {
++ addr.type |= MR_WITH_NETBITS;
++ addr.netbits = (uint8_t) netbits;
++ mroute_addr_mask_host_bits( &addr );
++ }
++
++ {
++ struct multi_instance *owner = multi_learn_addr (m, mi, &addr, 0);
++#ifdef MANAGEMENT_DEF_AUTH
++ if (management && owner)
++ management_learn_addr (management, &mi->context.c2.mda_context, &addr, primary);
++#endif
++ return owner;
++ }
++}
++
+ /*
+ * A new client has connected, add routes (server -> client)
+ * to internal routing table.
+@@ -1088,6 +1116,7 @@
+ {
+ struct gc_arena gc = gc_new ();
+ const struct iroute *ir;
++ const struct iroute_ipv6 *ir6;
+ if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN)
+ {
+ mi->did_iroutes = true;
+@@ -1107,6 +1136,22 @@
+
+ multi_learn_in_addr_t (m, mi, ir->network, ir->netbits, false);
+ }
++ for ( ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next )
++ {
++ if (ir6->netbits >= 0)
++ msg (D_MULTI_LOW, "MULTI: internal route %s/%d -> %s",
++ print_in6_addr (ir6->network, 0, &gc),
++ ir6->netbits,
++ multi_instance_string (mi, false, &gc));
++ else
++ msg (D_MULTI_LOW, "MULTI: internal route %s -> %s",
++ print_in6_addr (ir6->network, 0, &gc),
++ multi_instance_string (mi, false, &gc));
++
++ mroute_helper_add_iroute6 (m->route_helper, ir6);
++
++ multi_learn_in6_addr (m, mi, ir6->network, ir6->netbits, false);
++ }
+ }
+ gc_free (&gc);
+ }
+@@ -1192,21 +1237,37 @@
+ mi->context.c2.push_ifconfig_defined = true;
+ mi->context.c2.push_ifconfig_local = mi->context.options.push_ifconfig_local;
+ mi->context.c2.push_ifconfig_remote_netmask = mi->context.options.push_ifconfig_remote_netmask;
++
++ /* the current implementation does not allow "static IPv4, pool IPv6",
++ * (see below) so issue a warning if that happens - don't break the
++ * session, though, as we don't even know if this client WANTS IPv6
++ */
++ if ( mi->context.c1.tuntap->ipv6 &&
++ mi->context.options.ifconfig_ipv6_pool_defined &&
++ ! mi->context.options.push_ifconfig_ipv6_defined )
++ {
++ msg( M_INFO, "MULTI_sva: WARNING: if --ifconfig-push is used for IPv4, automatic IPv6 assignment from --ifconfig-ipv6-pool does not work. Use --ifconfig-ipv6-push for IPv6 then." );
++ }
+ }
+ else if (m->ifconfig_pool && mi->vaddr_handle < 0) /* otherwise, choose a pool address */
+ {
+ in_addr_t local=0, remote=0;
++ struct in6_addr remote_ipv6;
+ const char *cn = NULL;
+
+ if (!mi->context.options.duplicate_cn)
+ cn = tls_common_name (mi->context.c2.tls_multi, true);
+
+- mi->vaddr_handle = ifconfig_pool_acquire (m->ifconfig_pool, &local, &remote, cn);
++ mi->vaddr_handle = ifconfig_pool_acquire (m->ifconfig_pool, &local, &remote, &remote_ipv6, cn);
+ if (mi->vaddr_handle >= 0)
+ {
+ const int tunnel_type = TUNNEL_TYPE (mi->context.c1.tuntap);
+ const int tunnel_topology = TUNNEL_TOPOLOGY (mi->context.c1.tuntap);
+
++ msg( M_INFO, "MULTI_sva: pool returned IPv4=%s, IPv6=%s",
++ print_in_addr_t( remote, 0, &gc ),
++ print_in6_addr( remote_ipv6, 0, &gc ) );
++
+ /* set push_ifconfig_remote_netmask from pool ifconfig address(es) */
+ mi->context.c2.push_ifconfig_local = remote;
+ if (tunnel_type == DEV_TYPE_TAP || (tunnel_type == DEV_TYPE_TUN && tunnel_topology == TOP_SUBNET))
+@@ -1228,12 +1289,46 @@
+ else
+ msg (D_MULTI_ERRORS, "MULTI: no --ifconfig-pool netmask parameter is available to push to %s",
+ multi_instance_string (mi, false, &gc));
++
++ if ( mi->context.options.ifconfig_ipv6_pool_defined )
++ {
++ mi->context.c2.push_ifconfig_ipv6_local = remote_ipv6;
++ mi->context.c2.push_ifconfig_ipv6_remote =
++ mi->context.c1.tuntap->local_ipv6;
++ mi->context.c2.push_ifconfig_ipv6_netbits =
++ mi->context.options.ifconfig_ipv6_pool_netbits;
++ mi->context.c2.push_ifconfig_ipv6_defined = true;
++ }
+ }
+ else
+ {
+ msg (D_MULTI_ERRORS, "MULTI: no free --ifconfig-pool addresses are available");
+ }
+ }
++
++ /* IPv6 push_ifconfig is a bit problematic - since IPv6 shares the
++ * pool handling with IPv4, the combination "static IPv4, dynamic IPv6"
++ * will fail (because no pool will be allocated in this case).
++ * OTOH, this doesn't make too much sense in reality - and the other
++ * way round ("dynamic IPv4, static IPv6") or "both static" makes sense
++ * -> and so it's implemented right now
++ */
++ if ( mi->context.c1.tuntap->ipv6 &&
++ mi->context.options.push_ifconfig_ipv6_defined )
++ {
++ mi->context.c2.push_ifconfig_ipv6_local =
++ mi->context.options.push_ifconfig_ipv6_local;
++ mi->context.c2.push_ifconfig_ipv6_remote =
++ mi->context.options.push_ifconfig_ipv6_remote;
++ mi->context.c2.push_ifconfig_ipv6_netbits =
++ mi->context.options.push_ifconfig_ipv6_netbits;
++ mi->context.c2.push_ifconfig_ipv6_defined = true;
++
++ msg( M_INFO, "MULTI_sva: push_ifconfig_ipv6 %s/%d",
++ print_in6_addr( mi->context.c2.push_ifconfig_ipv6_local, 0, &gc ),
++ mi->context.c2.push_ifconfig_ipv6_netbits );
++ }
++
+ gc_free (&gc);
+ }
+
+@@ -1272,6 +1367,11 @@
+ SA_SET_IF_NONZERO);
+ }
+ }
++
++ /* TODO: I'm not exactly sure what these environment variables are
++ * used for, but if we have them for IPv4, we should also have
++ * them for IPv6, no?
++ */
+ }
+
+ /*
+@@ -1661,6 +1761,15 @@
+ print_in_addr_t (mi->context.c2.push_ifconfig_local, 0, &gc));
+ }
+
++ if (mi->context.c2.push_ifconfig_ipv6_defined)
++ {
++ multi_learn_in6_addr (m, mi, mi->context.c2.push_ifconfig_ipv6_local, -1, true);
++ /* TODO: find out where addresses are "unlearned"!! */
++ msg (D_MULTI_LOW, "MULTI: primary virtual IPv6 for %s: %s",
++ multi_instance_string (mi, false, &gc),
++ print_in6_addr (mi->context.c2.push_ifconfig_ipv6_local, 0, &gc));
++ }
++
+ /* add routes locally, pointing to new client, if
+ --iroute options have been specified */
+ multi_add_iroutes (m, mi);
+Index: openvpn-2.2.1/openvpn.8
+===================================================================
+--- openvpn-2.2.1.orig/openvpn.8 2011-12-13 12:24:20.000000000 +0100
++++ openvpn-2.2.1/openvpn.8 2011-12-13 12:24:54.628739315 +0100
+@@ -794,6 +794,8 @@
+ .B \-\-dev tunX.
+ A warning will be displayed
+ if no specific IPv6 TUN support for your OS has been compiled into OpenVPN.
++
++See below for further IPv6-related configuration options.
+ .\"*********************************************************
+ .TP
+ .B \-\-dev-node node
+@@ -4949,6 +4951,57 @@
+ .B \-\-verb
+ option can be used BEFORE this option to produce debugging information.
+ .\"*********************************************************
++.SS IPv6 Related Options
++.\"*********************************************************
++The following options exist to support IPv6 tunneling in peer-to-peer
++and client-server mode. As of now, this is just very basic
++documentation of the IPv6-related options. More documentation can be
++found on http://www.greenie.net/ipv6/openvpn.html.
++.TP
++.B --ifconfig-ipv6 ipv6addr/bits ipv6remote
++configure IPv6 address
++.B ipv6addr/bits
++on the ``tun'' device. The second parameter is used as route target for
++.B --route-ipv6
++if no gateway is specified.
++.TP
++.B --route-ipv6 ipv6addr/bits [gateway] [metric]
++setup IPv6 routing in the system to send the specified IPv6 network
++into OpenVPN's ``tun'' device
++.TP
++.B --server-ipv6 ipv6addr/bits
++convenience-function to enable a number of IPv6 related options at
++once, namely
++.B --ifconfig-ipv6, --ifconfig-ipv6-pool, --tun-ipv6
++and
++.B --push tun-ipv6
++Is only accepted if ``--mode server'' or ``--server'' is set.
++.TP
++.B --ifconfig-ipv6-pool ipv6addr/bits
++Specify an IPv6 address pool for dynamic assignment to clients. The
++pool starts at
++.B ipv6addr
++and increments by +1 for every new client (linear mode). The
++.B /bits
++setting controls the size of the pool.
++.TP
++.B --ifconfig-ipv6-push ipv6addr/bits ipv6remote
++for ccd/ per-client static IPv6 interface configuration, see
++.B --client-config-dir
++and
++.B --ifconfig-push
++for more details.
++.TP
++.B --iroute-ipv6 ipv6addr/bits
++for ccd/ per-client static IPv6 route configuration, see
++.B --iroute
++for more details how to setup and use this, and how
++.B --iroute
++and
++.B --route
++interact.
++
++.\"*********************************************************
+ .SH SCRIPTING AND ENVIRONMENTAL VARIABLES
+ OpenVPN exports a series
+ of environmental variables for use by user-defined scripts.
+Index: openvpn-2.2.1/openvpn.h
+===================================================================
+--- openvpn-2.2.1.orig/openvpn.h 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/openvpn.h 2011-12-13 12:24:54.629739303 +0100
+@@ -165,6 +165,9 @@
+ /* list of --route directives */
+ struct route_list *route_list;
+
++ /* list of --route-ipv6 directives */
++ struct route_ipv6_list *route_ipv6_list;
++
+ /* --status file */
+ struct status_output *status_output;
+ bool status_output_owned;
+@@ -417,6 +420,11 @@
+ in_addr_t push_ifconfig_local;
+ in_addr_t push_ifconfig_remote_netmask;
+
++ bool push_ifconfig_ipv6_defined;
++ struct in6_addr push_ifconfig_ipv6_local;
++ int push_ifconfig_ipv6_netbits;
++ struct in6_addr push_ifconfig_ipv6_remote;
++
+ /* client authentication state, CAS_SUCCEEDED must be 0 */
+ # define CAS_SUCCEEDED 0
+ # define CAS_PENDING 1
+Index: openvpn-2.2.1/options.c
+===================================================================
+--- openvpn-2.2.1.orig/options.c 2011-12-13 12:23:07.000000000 +0100
++++ openvpn-2.2.1/options.c 2011-12-13 12:24:54.635739227 +0100
+@@ -85,6 +85,7 @@
+ #ifdef USE_PF_INET6
+ " [PF_INET6]"
+ #endif
++ " [IPv6 payload 20110424-2 (2.2RC2)]"
+ " built on " __DATE__
+ ;
+
+@@ -181,6 +182,8 @@
+ " addresses outside of the subnets used by either peer.\n"
+ " TAP: configure device to use IP address l as a local\n"
+ " endpoint and rn as a subnet mask.\n"
++ "--ifconfig-ipv6 l r : configure device to use IPv6 address l as local\n"
++ " endpoint (as a /64) and r as remote endpoint\n"
+ "--ifconfig-noexec : Don't actually execute ifconfig/netsh command, instead\n"
+ " pass --ifconfig parms by environment to scripts.\n"
+ "--ifconfig-nowarn : Don't warn if the --ifconfig option on this side of the\n"
+@@ -191,6 +194,10 @@
+ " netmask default: 255.255.255.255\n"
+ " gateway default: taken from --route-gateway or --ifconfig\n"
+ " Specify default by leaving blank or setting to \"nil\".\n"
++ "--route-ipv6 network/bits [gateway] [metric] :\n"
++ " Add IPv6 route to routing table after connection\n"
++ " is established. Multiple routes can be specified.\n"
++ " gateway default: taken from --route-ipv6-gateway or --ifconfig\n"
+ "--max-routes n : Specify the maximum number of routes that may be defined\n"
+ " or pulled from a server.\n"
+ "--route-gateway gw|'dhcp' : Specify a default gateway for use with --route.\n"
+@@ -379,6 +386,7 @@
+ "\n"
+ "Multi-Client Server options (when --mode server is used):\n"
+ "--server network netmask : Helper option to easily configure server mode.\n"
++ "--server-ipv6 network/bits : Configure IPv6 server mode.\n"
+ "--server-bridge [IP netmask pool-start-IP pool-end-IP] : Helper option to\n"
+ " easily configure ethernet bridging server mode.\n"
+ "--push \"option\" : Push a config file option back to the peer for remote\n"
+@@ -392,10 +400,16 @@
+ "--ifconfig-pool-persist file [seconds] : Persist/unpersist ifconfig-pool\n"
+ " data to file, at seconds intervals (default=600).\n"
+ " If seconds=0, file will be treated as read-only.\n"
++ "--ifconfig-ipv6-pool base-IP/bits : set aside an IPv6 network block\n"
++ " to be dynamically allocated to connecting clients.\n"
+ "--ifconfig-push local remote-netmask : Push an ifconfig option to remote,\n"
+ " overrides --ifconfig-pool dynamic allocation.\n"
+ " Only valid in a client-specific config file.\n"
++ "--ifconfig-ipv6-push local/bits remote : Push an ifconfig-ipv6 option to\n"
++ " remote, overrides --ifconfig-ipv6-pool allocation.\n"
++ " Only valid in a client-specific config file.\n"
+ "--iroute network [netmask] : Route subnet to client.\n"
++ "--iroute-ipv6 network/bits : Route IPv6 subnet to client.\n"
+ " Sets up internal routes only.\n"
+ " Only valid in a client-specific config file.\n"
+ "--disable : Client is disabled.\n"
+@@ -880,6 +894,78 @@
+ return ret;
+ }
+
++/* helper: parse a text string containing an IPv6 address + netbits
++ * in "standard format" (2001:dba::/32)
++ * "/nn" is optional, default to /64 if missing
++ *
++ * return true if parsing succeeded, modify *network and *netbits
++ * return address part without "/nn" in *printable_ipv6 (if != NULL)
++ */
++bool
++get_ipv6_addr( const char * prefix_str, struct in6_addr *network,
++ unsigned int * netbits, char ** printable_ipv6, int msglevel )
++{
++ int rc;
++ char * sep, * endp;
++ int bits;
++ struct in6_addr t_network;
++
++ sep = strchr( prefix_str, '/' );
++ if ( sep == NULL )
++ {
++ bits = 64;
++ }
++ else
++ {
++ bits = strtol( sep+1, &endp, 10 );
++ if ( *endp != '\0' || bits < 0 || bits > 128 )
++ {
++ msg (msglevel, "IPv6 prefix '%s': invalid '/bits' spec", prefix_str);
++ return false;
++ }
++ }
++
++ /* temporary replace '/' in caller-provided string with '\0', otherwise
++ * inet_pton() will refuse prefix string
++ * (alternative would be to strncpy() the prefix to temporary buffer)
++ */
++
++ if ( sep != NULL ) *sep = '\0';
++
++ rc = inet_pton( AF_INET6, prefix_str, &t_network );
++
++ if ( rc == 1 && printable_ipv6 != NULL )
++ {
++ *printable_ipv6 = string_alloc( prefix_str, NULL );
++ }
++
++ if ( sep != NULL ) *sep = '/';
++
++ if ( rc != 1 )
++ {
++ msg (msglevel, "IPv6 prefix '%s': invalid IPv6 address", prefix_str);
++ return false;
++ }
++
++ if ( netbits != NULL )
++ {
++ *netbits = bits;
++ }
++ if ( network != NULL )
++ {
++ *network = t_network;
++ }
++ return true; /* parsing OK, values set */
++}
++
++static bool ipv6_addr_safe_hexplusbits( const char * ipv6_prefix_spec )
++{
++ struct in6_addr t_addr;
++ unsigned int t_bits;
++
++ return get_ipv6_addr( ipv6_prefix_spec, &t_addr, &t_bits, NULL, M_WARN );
++}
++
+ static char *
+ string_substitute (const char *src, int from, int to, struct gc_arena *gc)
+ {
+@@ -998,6 +1084,8 @@
+ #if P2MP_SERVER
+ msg (D_SHOW_PARMS, " server_network = %s", print_in_addr_t (o->server_network, 0, &gc));
+ msg (D_SHOW_PARMS, " server_netmask = %s", print_in_addr_t (o->server_netmask, 0, &gc));
++ msg (D_SHOW_PARMS, " server_network_ipv6 = %s", print_in6_addr (o->server_network_ipv6, 0, &gc) );
++ SHOW_INT (server_netbits_ipv6);
+ msg (D_SHOW_PARMS, " server_bridge_ip = %s", print_in_addr_t (o->server_bridge_ip, 0, &gc));
+ msg (D_SHOW_PARMS, " server_bridge_netmask = %s", print_in_addr_t (o->server_bridge_netmask, 0, &gc));
+ msg (D_SHOW_PARMS, " server_bridge_pool_start = %s", print_in_addr_t (o->server_bridge_pool_start, 0, &gc));
+@@ -1018,6 +1106,9 @@
+ msg (D_SHOW_PARMS, " ifconfig_pool_netmask = %s", print_in_addr_t (o->ifconfig_pool_netmask, 0, &gc));
+ SHOW_STR (ifconfig_pool_persist_filename);
+ SHOW_INT (ifconfig_pool_persist_refresh_freq);
++ SHOW_BOOL (ifconfig_ipv6_pool_defined);
++ msg (D_SHOW_PARMS, " ifconfig_ipv6_pool_base = %s", print_in6_addr (o->ifconfig_ipv6_pool_base, 0, &gc));
++ SHOW_INT (ifconfig_ipv6_pool_netbits);
+ SHOW_INT (n_bcast_buf);
+ SHOW_INT (tcp_queue_limit);
+ SHOW_INT (real_hash_size);
+@@ -1031,6 +1122,9 @@
+ SHOW_BOOL (push_ifconfig_defined);
+ msg (D_SHOW_PARMS, " push_ifconfig_local = %s", print_in_addr_t (o->push_ifconfig_local, 0, &gc));
+ msg (D_SHOW_PARMS, " push_ifconfig_remote_netmask = %s", print_in_addr_t (o->push_ifconfig_remote_netmask, 0, &gc));
++ SHOW_BOOL (push_ifconfig_ipv6_defined);
++ msg (D_SHOW_PARMS, " push_ifconfig_ipv6_local = %s/%d", print_in6_addr (o->push_ifconfig_ipv6_local, 0, &gc), o->push_ifconfig_ipv6_netbits );
++ msg (D_SHOW_PARMS, " push_ifconfig_ipv6_remote = %s", print_in6_addr (o->push_ifconfig_ipv6_remote, 0, &gc));
+ SHOW_BOOL (enable_c2c);
+ SHOW_BOOL (duplicate_cn);
+ SHOW_INT (cf_max);
+@@ -1085,6 +1179,25 @@
+ o->iroutes = ir;
+ }
+
++static void
++option_iroute_ipv6 (struct options *o,
++ const char *prefix_str,
++ int msglevel)
++{
++ struct iroute_ipv6 *ir;
++
++ ALLOC_OBJ_GC (ir, struct iroute_ipv6, &o->gc);
++
++ if ( get_ipv6_addr (prefix_str, &ir->network, &ir->netbits, NULL, msglevel ) < 0 )
++ {
++ msg (msglevel, "in --iroute-ipv6 %s: Bad IPv6 prefix specification",
++ prefix_str);
++ return;
++ }
++
++ ir->next = o->iroutes_ipv6;
++ o->iroutes_ipv6 = ir;
++}
+ #endif /* P2MP_SERVER */
+ #endif /* P2MP */
+
+@@ -1122,6 +1235,13 @@
+ options->routes = new_route_option_list (options->max_routes, &options->gc);
+ }
+
++void
++rol6_check_alloc (struct options *options)
++{
++ if (!options->routes_ipv6)
++ options->routes_ipv6 = new_route_ipv6_option_list (options->max_routes, &options->gc);
++}
++
+ #ifdef ENABLE_DEBUG
+ static void
+ show_connection_entry (const struct connection_entry *o)
+@@ -1212,6 +1332,9 @@
+ SHOW_STR (ifconfig_remote_netmask);
+ SHOW_BOOL (ifconfig_noexec);
+ SHOW_BOOL (ifconfig_nowarn);
++ SHOW_STR (ifconfig_ipv6_local);
++ SHOW_INT (ifconfig_ipv6_netbits);
++ SHOW_STR (ifconfig_ipv6_remote);
+
+ #ifdef HAVE_GETTIMEOFDAY
+ SHOW_INT (shaper);
+@@ -1915,8 +2038,10 @@
+ if (options->connection_list)
+ msg (M_USAGE, "<connection> cannot be used with --mode server");
+ #endif
++#if 0
+ if (options->tun_ipv6)
+ msg (M_USAGE, "--tun-ipv6 cannot be used with --mode server");
++#endif
+ if (options->shaper)
+ msg (M_USAGE, "--shaper cannot be used with --mode server");
+ if (options->inetd)
+@@ -1949,6 +2074,11 @@
+ msg (M_USAGE, "--up-delay cannot be used with --mode server");
+ if (!options->ifconfig_pool_defined && options->ifconfig_pool_persist_filename)
+ msg (M_USAGE, "--ifconfig-pool-persist must be used with --ifconfig-pool");
++ if (options->ifconfig_ipv6_pool_defined && !options->ifconfig_ipv6_local )
++ msg (M_USAGE, "--ifconfig-ipv6-pool needs --ifconfig-ipv6");
++ if (options->ifconfig_ipv6_local && !options->tun_ipv6 )
++ msg (M_INFO, "Warning: --ifconfig-ipv6 without --tun-ipv6 will not do IPv6");
++
+ if (options->auth_user_pass_file)
+ msg (M_USAGE, "--auth-user-pass cannot be used with --mode server (it should be used on the client side only)");
+ if (options->ccd_exclusive && !options->client_config_dir)
+@@ -1980,6 +2110,8 @@
+ */
+ if (options->ifconfig_pool_defined || options->ifconfig_pool_persist_filename)
+ msg (M_USAGE, "--ifconfig-pool/--ifconfig-pool-persist requires --mode server");
++ if (options->ifconfig_ipv6_pool_defined)
++ msg (M_USAGE, "--ifconfig-ipv6-pool requires --mode server");
+ if (options->real_hash_size != defaults.real_hash_size
+ || options->virtual_hash_size != defaults.virtual_hash_size)
+ msg (M_USAGE, "--hash-size requires --mode server");
+@@ -2525,6 +2657,8 @@
+ o->topology,
+ o->ifconfig_local,
+ o->ifconfig_remote_netmask,
++ o->ifconfig_ipv6_local,
++ o->ifconfig_ipv6_remote,
+ (in_addr_t)0,
+ (in_addr_t)0,
+ false,
+@@ -3850,6 +3984,30 @@
+ goto err;
+ }
+ }
++ else if (streq (p[0], "ifconfig-ipv6") && p[1] && p[2] )
++ {
++ unsigned int netbits;
++ char * ipv6_local;
++
++ VERIFY_PERMISSION (OPT_P_UP);
++ if ( get_ipv6_addr( p[1], NULL, &netbits, &ipv6_local, msglevel ) &&
++ ipv6_addr_safe( p[2] ) )
++ {
++ if ( netbits < 64 || netbits > 124 )
++ {
++ msg( msglevel, "ifconfig-ipv6: /netbits must be between 64 and 124, not '/%d'", netbits );
++ goto err;
++ }
++ options->ifconfig_ipv6_local = ipv6_local;
++ options->ifconfig_ipv6_netbits = netbits;
++ options->ifconfig_ipv6_remote = p[2];
++ }
++ else
++ {
++ msg (msglevel, "ifconfig-ipv6 parms '%s' and '%s' must be valid addresses", p[1], p[2]);
++ goto err;
++ }
++ }
+ else if (streq (p[0], "ifconfig-noexec"))
+ {
+ VERIFY_PERMISSION (OPT_P_UP);
+@@ -4650,6 +4808,26 @@
+ }
+ add_route_to_option_list (options->routes, p[1], p[2], p[3], p[4]);
+ }
++ else if (streq (p[0], "route-ipv6") && p[1])
++ {
++ VERIFY_PERMISSION (OPT_P_ROUTE);
++ rol6_check_alloc (options);
++ if (pull_mode)
++ {
++ if (!ipv6_addr_safe_hexplusbits (p[1]))
++ {
++ msg (msglevel, "route-ipv6 parameter network/IP '%s' must be a valid address", p[1]);
++ goto err;
++ }
++ if (p[2] && !ipv6_addr_safe (p[2]))
++ {
++ msg (msglevel, "route-ipv6 parameter gateway '%s' must be a valid address", p[2]);
++ goto err;
++ }
++ /* p[3] is metric, if present */
++ }
++ add_route_ipv6_to_option_list (options->routes_ipv6, p[1], p[2], p[3]);
++ }
+ else if (streq (p[0], "max-routes") && p[1])
+ {
+ int max_routes;
+@@ -4861,6 +5039,33 @@
+ }
+ }
+ }
++ else if (streq (p[0], "server-ipv6") && p[1] )
++ {
++ const int lev = M_WARN;
++ struct in6_addr network;
++ unsigned int netbits = 0;
++
++ VERIFY_PERMISSION (OPT_P_GENERAL);
++ if ( ! get_ipv6_addr (p[1], &network, &netbits, NULL, lev) )
++ {
++ msg (msglevel, "error parsing --server-ipv6 parameter");
++ goto err;
++ }
++ if ( netbits != 64 )
++ {
++ msg( msglevel, "--server-ipv6 settings: only /64 supported right now (not /%d)", netbits );
++ goto err;
++ }
++ options->server_ipv6_defined = true;
++ options->server_network_ipv6 = network;
++ options->server_netbits_ipv6 = netbits;
++
++ if (p[2]) /* no "nopool" options or similar for IPv6 */
++ {
++ msg (msglevel, "error parsing --server-ipv6: %s is not a recognized flag", p[3]);
++ goto err;
++ }
++ }
+ else if (streq (p[0], "server-bridge") && p[1] && p[2] && p[3] && p[4])
+ {
+ const int lev = M_WARN;
+@@ -4945,6 +5150,28 @@
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->topology = TOP_P2P;
+ }
++ else if (streq (p[0], "ifconfig-ipv6-pool") && p[1] )
++ {
++ const int lev = M_WARN;
++ struct in6_addr network;
++ unsigned int netbits = 0;
++
++ VERIFY_PERMISSION (OPT_P_GENERAL);
++ if ( ! get_ipv6_addr (p[1], &network, &netbits, NULL, lev ) )
++ {
++ msg (msglevel, "error parsing --ifconfig-ipv6-pool parameters");
++ goto err;
++ }
++ if ( netbits != 64 )
++ {
++ msg( msglevel, "--ifconfig-ipv6-pool settings: only /64 supported right now (not /%d)", netbits );
++ goto err;
++ }
++
++ options->ifconfig_ipv6_pool_defined = true;
++ options->ifconfig_ipv6_pool_base = network;
++ options->ifconfig_ipv6_pool_netbits = netbits;
++ }
+ else if (streq (p[0], "hash-size") && p[1] && p[2])
+ {
+ int real, virtual;
+@@ -5140,6 +5367,11 @@
+ }
+ option_iroute (options, p[1], netmask, msglevel);
+ }
++ else if (streq (p[0], "iroute-ipv6") && p[1])
++ {
++ VERIFY_PERMISSION (OPT_P_INSTANCE);
++ option_iroute_ipv6 (options, p[1], msglevel);
++ }
+ else if (streq (p[0], "ifconfig-push") && p[1] && p[2])
+ {
+ in_addr_t local, remote_netmask;
+@@ -5178,6 +5410,43 @@
+ goto err;
+ }
+ }
++ else if (streq (p[0], "ifconfig-ipv6-push") && p[1] )
++ {
++ struct in6_addr local, remote;
++ unsigned int netbits;
++
++ VERIFY_PERMISSION (OPT_P_INSTANCE);
++
++ if ( ! get_ipv6_addr( p[1], &local, &netbits, NULL, msglevel ) )
++ {
++ msg (msglevel, "cannot parse --ifconfig-ipv6-push addresses");
++ goto err;
++ }
++
++ if ( p[2] )
++ {
++ if ( !get_ipv6_addr( p[2], &remote, NULL, NULL, msglevel ) )
++ {
++ msg( msglevel, "cannot parse --ifconfig-ipv6-push addresses");
++ goto err;
++ }
++ }
++ else
++ {
++ if ( ! options->ifconfig_ipv6_local ||
++ ! get_ipv6_addr( options->ifconfig_ipv6_local, &remote,
++ NULL, NULL, msglevel ) )
++ {
++ msg( msglevel, "second argument to --ifconfig-ipv6-push missing and no global --ifconfig-ipv6 address set");
++ goto err;
++ }
++ }
++
++ options->push_ifconfig_ipv6_defined = true;
++ options->push_ifconfig_ipv6_local = local;
++ options->push_ifconfig_ipv6_netbits = netbits;
++ options->push_ifconfig_ipv6_remote = remote;
++ }
+ else if (streq (p[0], "disable"))
+ {
+ VERIFY_PERMISSION (OPT_P_INSTANCE);
+Index: openvpn-2.2.1/options.h
+===================================================================
+--- openvpn-2.2.1.orig/options.h 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/options.h 2011-12-13 12:24:54.636739214 +0100
+@@ -205,6 +205,9 @@
+ int topology; /* one of the TOP_x values from proto.h */
+ const char *ifconfig_local;
+ const char *ifconfig_remote_netmask;
++ const char *ifconfig_ipv6_local;
++ int ifconfig_ipv6_netbits;
++ const char *ifconfig_ipv6_remote;
+ bool ifconfig_noexec;
+ bool ifconfig_nowarn;
+ #ifdef HAVE_GETTIMEOFDAY
+@@ -326,6 +329,7 @@
+ bool route_delay_defined;
+ int max_routes;
+ struct route_option_list *routes;
++ struct route_ipv6_option_list *routes_ipv6; /* IPv6 */
+ bool route_nopull;
+ bool route_gateway_via_dhcp;
+ bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
+@@ -363,6 +367,9 @@
+ bool server_defined;
+ in_addr_t server_network;
+ in_addr_t server_netmask;
++ bool server_ipv6_defined; /* IPv6 */
++ struct in6_addr server_network_ipv6; /* IPv6 */
++ unsigned int server_netbits_ipv6; /* IPv6 */
+
+ # define SF_NOPOOL (1<<0)
+ # define SF_TCP_NODELAY_HELPER (1<<1)
+@@ -384,6 +391,11 @@
+ in_addr_t ifconfig_pool_netmask;
+ const char *ifconfig_pool_persist_filename;
+ int ifconfig_pool_persist_refresh_freq;
++
++ bool ifconfig_ipv6_pool_defined; /* IPv6 */
++ struct in6_addr ifconfig_ipv6_pool_base; /* IPv6 */
++ int ifconfig_ipv6_pool_netbits; /* IPv6 */
++
+ int real_hash_size;
+ int virtual_hash_size;
+ const char *client_connect_script;
+@@ -395,12 +407,17 @@
+ int n_bcast_buf;
+ int tcp_queue_limit;
+ struct iroute *iroutes;
++ struct iroute_ipv6 *iroutes_ipv6; /* IPv6 */
+ bool push_ifconfig_defined;
+ in_addr_t push_ifconfig_local;
+ in_addr_t push_ifconfig_remote_netmask;
+ bool push_ifconfig_constraint_defined;
+ in_addr_t push_ifconfig_constraint_network;
+ in_addr_t push_ifconfig_constraint_netmask;
++ bool push_ifconfig_ipv6_defined; /* IPv6 */
++ struct in6_addr push_ifconfig_ipv6_local; /* IPv6 */
++ int push_ifconfig_ipv6_netbits; /* IPv6 */
++ struct in6_addr push_ifconfig_ipv6_remote; /* IPv6 */
+ bool enable_c2c;
+ bool duplicate_cn;
+ int cf_max;
+@@ -723,6 +740,10 @@
+ unsigned int *option_types_found,
+ struct env_set *es);
+
++bool get_ipv6_addr( const char * prefix_str, struct in6_addr *network,
++ unsigned int * netbits, char ** printable_ipv6,
++ int msglevel );
++
+ /*
+ * inline functions
+ */
+Index: openvpn-2.2.1/pool.c
+===================================================================
+--- openvpn-2.2.1.orig/pool.c 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/pool.c 2011-12-13 12:24:54.637739202 +0100
+@@ -132,7 +132,10 @@
+ }
+
+ struct ifconfig_pool *
+-ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn)
++ifconfig_pool_init (int type, in_addr_t start, in_addr_t end,
++ const bool duplicate_cn,
++ const bool ipv6_pool, const struct in6_addr ipv6_base,
++ const int ipv6_netbits )
+ {
+ struct gc_arena gc = gc_new ();
+ struct ifconfig_pool *pool = NULL;
+@@ -157,11 +160,31 @@
+ ASSERT (0);
+ }
+
++ /* IPv6 pools are always "INDIV" type */
++ pool->ipv6 = ipv6_pool;
++
++ if ( pool->ipv6 )
++ {
++ pool->base_ipv6 = ipv6_base;
++ pool->size_ipv6 = ipv6_netbits>96? ( 1<<(128-ipv6_netbits) )
++ : IFCONFIG_POOL_MAX;
++
++ msg( D_IFCONFIG_POOL, "IFCONFIG POOL IPv6: (IPv4) size=%d, size_ipv6=%d, netbits=%d, base_ipv6=%s",
++ pool->size, pool->size_ipv6, ipv6_netbits,
++ print_in6_addr( pool->base_ipv6, 0, &gc ));
++
++ /* the current code is very simple and assumes that the IPv6
++ * pool is at least as big as the IPv4 pool, and we don't need
++ * to do separate math etc. for IPv6
++ */
++ ASSERT( pool->size < pool->size_ipv6 );
++ }
++
+ ALLOC_ARRAY_CLEAR (pool->list, struct ifconfig_pool_entry, pool->size);
+
+- msg (D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d",
++ msg (D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d, ipv6=%d",
+ print_in_addr_t (pool->base, 0, &gc),
+- pool->size);
++ pool->size, pool->ipv6 );
+
+ gc_free (&gc);
+ return pool;
+@@ -181,7 +204,7 @@
+ }
+
+ ifconfig_pool_handle
+-ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, const char *common_name)
++ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, struct in6_addr *remote_ipv6, const char *common_name)
+ {
+ int i;
+
+@@ -214,6 +237,12 @@
+ default:
+ ASSERT (0);
+ }
++
++ /* IPv6 pools are always INDIV (--linear) */
++ if ( pool->ipv6 && remote_ipv6 )
++ {
++ *remote_ipv6 = add_in6_addr( pool->base_ipv6, i );
++ }
+ }
+ return i;
+ }
+@@ -288,6 +317,19 @@
+ return ret;
+ }
+
++static struct in6_addr
++ifconfig_pool_handle_to_ipv6_base (const struct ifconfig_pool* pool, ifconfig_pool_handle hand)
++{
++ struct in6_addr ret = in6addr_any;
++
++ /* IPv6 pools are always INDIV (--linear) */
++ if (hand >= 0 && hand < pool->size_ipv6 )
++ {
++ ret = add_in6_addr( pool->base_ipv6, hand );
++ }
++ return ret;
++}
++
+ static void
+ ifconfig_pool_set (struct ifconfig_pool* pool, const char *cn, const in_addr_t addr, const bool fixed)
+ {
+@@ -317,9 +359,20 @@
+ if (e->common_name)
+ {
+ const in_addr_t ip = ifconfig_pool_handle_to_ip_base (pool, i);
+- status_printf (out, "%s,%s",
+- e->common_name,
+- print_in_addr_t (ip, 0, &gc));
++ if ( pool->ipv6 )
++ {
++ struct in6_addr ip6 = ifconfig_pool_handle_to_ipv6_base (pool, i);
++ status_printf (out, "%s,%s,%s",
++ e->common_name,
++ print_in_addr_t (ip, 0, &gc),
++ print_in6_addr (ip6, 0, &gc));
++ }
++ else
++ {
++ status_printf (out, "%s,%s",
++ e->common_name,
++ print_in_addr_t (ip, 0, &gc));
++ }
+ }
+ }
+ gc_free (&gc);
+@@ -409,6 +462,9 @@
+ int c = *BSTR(&in);
+ if (c == '#' || c == ';')
+ continue;
++ msg( M_INFO, "ifconfig_pool_read(), in='%s', TODO: IPv6",
++ BSTR(&in) );
++
+ if (buf_parse (&in, ',', cn_buf, buf_size)
+ && buf_parse (&in, ',', ip_buf, buf_size))
+ {
+@@ -416,6 +472,7 @@
+ const in_addr_t addr = getaddr (GETADDR_HOST_ORDER, ip_buf, 0, &succeeded, NULL);
+ if (succeeded)
+ {
++ msg( M_INFO, "succeeded -> ifconfig_pool_set()");
+ ifconfig_pool_set (pool, cn_buf, addr, persist->fixed);
+ }
+ }
+@@ -471,7 +528,7 @@
+ #else
+ cn = buf;
+ #endif
+- h = ifconfig_pool_acquire (p, &local, &remote, cn);
++ h = ifconfig_pool_acquire (p, &local, &remote, NULL, cn);
+ if (h < 0)
+ break;
+ msg (M_INFO | M_NOPREFIX, "IFCONFIG_POOL TEST pass 1: l=%s r=%s cn=%s",
+@@ -506,7 +563,7 @@
+ #else
+ cn = buf;
+ #endif
+- h = ifconfig_pool_acquire (p, &local, &remote, cn);
++ h = ifconfig_pool_acquire (p, &local, &remote, NULL, cn);
+ if (h < 0)
+ break;
+ msg (M_INFO | M_NOPREFIX, "IFCONFIG_POOL TEST pass 3: l=%s r=%s cn=%s",
+Index: openvpn-2.2.1/pool.h
+===================================================================
+--- openvpn-2.2.1.orig/pool.h 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/pool.h 2011-12-13 12:24:54.637739202 +0100
+@@ -52,6 +52,9 @@
+ int size;
+ int type;
+ bool duplicate_cn;
++ bool ipv6;
++ struct in6_addr base_ipv6;
++ unsigned int size_ipv6;
+ struct ifconfig_pool_entry *list;
+ };
+
+@@ -63,13 +66,13 @@
+
+ typedef int ifconfig_pool_handle;
+
+-struct ifconfig_pool *ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn);
++struct ifconfig_pool *ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn, const bool ipv6_pool, const struct in6_addr ipv6_base, const int ipv6_netbits );
+
+ void ifconfig_pool_free (struct ifconfig_pool *pool);
+
+ bool ifconfig_pool_verify_range (const int msglevel, const in_addr_t start, const in_addr_t end);
+
+-ifconfig_pool_handle ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, const char *common_name);
++ifconfig_pool_handle ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, struct in6_addr *remote_ipv6, const char *common_name);
+
+ bool ifconfig_pool_release (struct ifconfig_pool* pool, ifconfig_pool_handle hand, const bool hard);
+
+Index: openvpn-2.2.1/proto.h
+===================================================================
+--- openvpn-2.2.1.orig/proto.h 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/proto.h 2011-12-13 12:24:54.638739190 +0100
+@@ -108,6 +108,21 @@
+ };
+
+ /*
++ * IPv6 header
++ */
++struct openvpn_ipv6hdr {
++ uint8_t version_prio;
++ uint8_t flow_lbl[3];
++ uint16_t payload_len;
++ uint8_t nexthdr;
++ uint8_t hop_limit;
++
++ struct in6_addr saddr;
++ struct in6_addr daddr;
++};
++
++
++/*
+ * UDP header
+ */
+ struct openvpn_udphdr {
+Index: openvpn-2.2.1/push.c
+===================================================================
+--- openvpn-2.2.1.orig/push.c 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/push.c 2011-12-13 12:24:54.638739190 +0100
+@@ -189,8 +189,26 @@
+ const int safe_cap = BCAP (&buf) - extra;
+ bool push_sent = false;
+
++ msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap );
++
+ buf_printf (&buf, "%s", cmd);
+
++ if ( c->c2.push_ifconfig_ipv6_defined )
++ {
++ /* IPv6 is put into buffer first, could be lengthy */
++ /* TODO: push "/netbits" as well, to allow non-/64 subnet sizes
++ * (needs changes in options.c, options.h, and other places)
++ */
++ buf_printf( &buf, ",ifconfig-ipv6 %s %s",
++ print_in6_addr( c->c2.push_ifconfig_ipv6_local, 0, &gc),
++ print_in6_addr( c->c2.push_ifconfig_ipv6_remote, 0, &gc) );
++ if (BLEN (&buf) >= safe_cap)
++ {
++ msg (M_WARN, "--push ifconfig-ipv6 option is too long");
++ goto fail;
++ }
++ }
++
+ while (e)
+ {
+ if (e->enable)
+Index: openvpn-2.2.1/route.c
+===================================================================
+--- openvpn-2.2.1.orig/route.c 2011-12-13 12:24:33.000000000 +0100
++++ openvpn-2.2.1/route.c 2011-12-13 12:24:54.641739154 +0100
+@@ -35,10 +35,12 @@
+ #include "socket.h"
+ #include "manage.h"
+ #include "win32.h"
++#include "options.h"
+
+ #include "memdbg.h"
+
+ static void delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
++static void delete_route_ipv6 (const struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
+ static void get_bypass_addresses (struct route_bypass *rb, const unsigned int flags);
+
+ #ifdef ENABLE_DEBUG
+@@ -68,6 +70,15 @@
+ return ret;
+ }
+
++struct route_ipv6_option_list *
++new_route_ipv6_option_list (const int max_routes, struct gc_arena *a)
++{
++ struct route_ipv6_option_list *ret;
++ ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_option_list, struct route_ipv6_option, max_routes, a);
++ ret->capacity = max_routes;
++ return ret;
++}
++
+ struct route_option_list *
+ clone_route_option_list (const struct route_option_list *src, struct gc_arena *a)
+ {
+@@ -95,6 +106,15 @@
+ return ret;
+ }
+
++struct route_ipv6_list *
++new_route_ipv6_list (const int max_routes, struct gc_arena *a)
++{
++ struct route_ipv6_list *ret;
++ ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_list, struct route_ipv6, max_routes, a);
++ ret->capacity = max_routes;
++ return ret;
++}
++
+ static const char *
+ route_string (const struct route *r, struct gc_arena *gc)
+ {
+@@ -311,6 +331,68 @@
+ return false;
+ }
+
++static bool
++init_route_ipv6 (struct route_ipv6 *r6,
++ const struct route_ipv6_option *r6o,
++ const struct route_ipv6_list *rl6 )
++{
++ r6->option = r6o;
++ r6->defined = false;
++
++ if ( !get_ipv6_addr( r6o->prefix, &r6->network, &r6->netbits, NULL, M_WARN ))
++ goto fail;
++
++ /* gateway */
++ if (is_route_parm_defined (r6o->gateway))
++ {
++ if ( inet_pton( AF_INET6, r6o->gateway, &r6->gateway ) != 1 )
++ {
++ msg( M_WARN, PACKAGE_NAME "ROUTE6: cannot parse gateway spec '%s'", r6o->gateway );
++ }
++ }
++ else if (rl6->remote_endpoint_defined)
++ {
++ r6->gateway = rl6->remote_endpoint_ipv6;
++ }
++ else
++ {
++ msg (M_WARN, PACKAGE_NAME " ROUTE6: " PACKAGE_NAME " needs a gateway parameter for a --route-ipv6 option and no default was specified by either --route-ipv6-gateway or --ifconfig-ipv6 options");
++ goto fail;
++ }
++
++ /* metric */
++
++ r6->metric_defined = false;
++ r6->metric = 0;
++ if (is_route_parm_defined (r6o->metric))
++ {
++ r6->metric = atoi (r6o->metric);
++ if (r6->metric < 0)
++ {
++ msg (M_WARN, PACKAGE_NAME " ROUTE: route metric for network %s (%s) must be >= 0",
++ r6o->prefix,
++ r6o->metric);
++ goto fail;
++ }
++ r6->metric_defined = true;
++ }
++ else if (rl6->default_metric_defined)
++ {
++ r6->metric = rl6->default_metric;
++ r6->metric_defined = true;
++ }
++
++ r6->defined = true;
++
++ return true;
++
++ fail:
++ msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve route for host/network: %s",
++ r6o->prefix);
++ r6->defined = false;
++ return false;
++}
++
+ void
+ add_route_to_option_list (struct route_option_list *l,
+ const char *network,
+@@ -331,6 +413,23 @@
+ }
+
+ void
++add_route_ipv6_to_option_list (struct route_ipv6_option_list *l,
++ const char *prefix,
++ const char *gateway,
++ const char *metric)
++{
++ struct route_ipv6_option *ro;
++ if (l->n >= l->capacity)
++ msg (M_FATAL, PACKAGE_NAME " ROUTE: cannot add more than %d IPv6 routes -- please increase the max-routes option in the client configuration file",
++ l->capacity);
++ ro = &l->routes_ipv6[l->n];
++ ro->prefix = prefix;
++ ro->gateway = gateway;
++ ro->metric = metric;
++ ++l->n;
++}
++
++void
+ clear_route_list (struct route_list *rl)
+ {
+ const int capacity = rl->capacity;
+@@ -340,6 +439,15 @@
+ }
+
+ void
++clear_route_ipv6_list (struct route_ipv6_list *rl6)
++{
++ const int capacity = rl6->capacity;
++ const size_t rl6_size = array_mult_safe (sizeof(struct route_ipv6), capacity, sizeof(struct route_ipv6_list));
++ memset(rl6, 0, rl6_size);
++ rl6->capacity = capacity;
++}
++
++void
+ route_list_add_default_gateway (struct route_list *rl,
+ struct env_set *es,
+ const in_addr_t addr)
+@@ -469,6 +577,72 @@
+ return ret;
+ }
+
++bool
++init_route_ipv6_list (struct route_ipv6_list *rl6,
++ const struct route_ipv6_option_list *opt6,
++ const char *remote_endpoint,
++ int default_metric,
++ struct env_set *es)
++{
++ struct gc_arena gc = gc_new ();
++ bool ret = true;
++
++ clear_route_ipv6_list (rl6);
++
++ rl6->flags = opt6->flags;
++
++ if (default_metric)
++ {
++ rl6->default_metric = default_metric;
++ rl6->default_metric_defined = true;
++ }
++
++ /* "default_gateway" is stuff for "redirect-gateway", which we don't
++ * do for IPv6 yet -> TODO
++ */
++ {
++ dmsg (D_ROUTE, "ROUTE6: default_gateway=UNDEF");
++ }
++
++ if ( is_route_parm_defined( remote_endpoint ))
++ {
++ if ( inet_pton( AF_INET6, remote_endpoint,
++ &rl6->remote_endpoint_ipv6) == 1 )
++ {
++ rl6->remote_endpoint_defined = true;
++ }
++ else
++ {
++ msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve default gateway: %s", remote_endpoint);
++ ret = false;
++ }
++ }
++ else
++ rl6->remote_endpoint_defined = false;
++
++
++ if (!(opt6->n >= 0 && opt6->n <= rl6->capacity))
++ msg (M_FATAL, PACKAGE_NAME " ROUTE6: (init) number of route options (%d) is greater than route list capacity (%d)", opt6->n, rl6->capacity);
++
++ /* parse the routes from opt to rl6 */
++ {
++ int i, j = 0;
++ for (i = 0; i < opt6->n; ++i)
++ {
++ if (!init_route_ipv6 (&rl6->routes_ipv6[j],
++ &opt6->routes_ipv6[i],
++ rl6 ))
++ ret = false;
++ else
++ ++j;
++ }
++ rl6->n = j;
++ }
++
++ gc_free (&gc);
++ return ret;
++}
++
+ static void
+ add_route3 (in_addr_t network,
+ in_addr_t netmask,
+@@ -714,10 +888,13 @@
+ }
+
+ void
+-add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
++add_routes (struct route_list *rl, struct route_ipv6_list *rl6,
++ const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+ {
+- redirect_default_route_to_vpn (rl, tt, flags, es);
+- if (!rl->routes_added)
++ if (rl)
++ redirect_default_route_to_vpn (rl, tt, flags, es);
++
++ if (rl && !rl->routes_added)
+ {
+ int i;
+
+@@ -742,12 +919,27 @@
+ }
+ rl->routes_added = true;
+ }
++
++ if (rl6 && !rl6->routes_added)
++ {
++ int i;
++
++ for (i = 0; i < rl6->n; ++i)
++ {
++ struct route_ipv6 *r = &rl6->routes_ipv6[i];
++ if (flags & ROUTE_DELETE_FIRST)
++ delete_route_ipv6 (r, tt, flags, es);
++ add_route_ipv6 (r, tt, flags, es);
++ }
++ rl6->routes_added = true;
++ }
+ }
+
+ void
+-delete_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
++delete_routes (struct route_list *rl, struct route_ipv6_list *rl6,
++ const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+ {
+- if (rl->routes_added)
++ if (rl && rl->routes_added)
+ {
+ int i;
+ for (i = rl->n - 1; i >= 0; --i)
+@@ -757,9 +949,28 @@
+ }
+ rl->routes_added = false;
+ }
+- undo_redirect_default_route_to_vpn (rl, tt, flags, es);
+
+- clear_route_list (rl);
++ if ( rl )
++ {
++ undo_redirect_default_route_to_vpn (rl, tt, flags, es);
++ clear_route_list (rl);
++ }
++
++ if ( rl6 && rl6->routes_added )
++ {
++ int i;
++ for (i = rl6->n - 1; i >= 0; --i)
++ {
++ const struct route_ipv6 *r6 = &rl6->routes_ipv6[i];
++ delete_route_ipv6 (r6, tt, flags, es);
++ }
++ rl6->routes_added = false;
++ }
++
++ if ( rl6 )
++ {
++ clear_route_ipv6_list (rl6);
++ }
+ }
+
+ #ifdef ENABLE_DEBUG
+@@ -842,6 +1053,34 @@
+ setenv_route (es, &rl->routes[i], i + 1);
+ }
+
++static void
++setenv_route_ipv6 (struct env_set *es, const struct route_ipv6 *r6, int i)
++{
++ struct gc_arena gc = gc_new ();
++ if (r6->defined)
++ {
++ struct buffer name1 = alloc_buf_gc( 256, &gc );
++ struct buffer val = alloc_buf_gc( 256, &gc );
++ struct buffer name2 = alloc_buf_gc( 256, &gc );
++
++ buf_printf( &name1, "route_ipv6_network_%d", i );
++ buf_printf( &val, "%s/%d", print_in6_addr( r6->network, 0, &gc ),
++ r6->netbits );
++ setenv_str( es, BSTR(&name1), BSTR(&val) );
++
++ buf_printf( &name2, "route_ipv6_gateway_%d", i );
++ setenv_str( es, BSTR(&name2), print_in6_addr( r6->gateway, 0, &gc ));
++ }
++ gc_free (&gc);
++}
++void
++setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6)
++{
++ int i;
++ for (i = 0; i < rl6->n; ++i)
++ setenv_route_ipv6 (es, &rl6->routes_ipv6[i], i + 1);
++}
++
+ void
+ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+ {
+@@ -1035,6 +1274,176 @@
+ gc_free (&gc);
+ }
+
++void
++add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
++{
++ struct gc_arena gc;
++ struct argv argv;
++
++ const char *network;
++ const char *gateway;
++ bool status = false;
++ const char *device = tt->actual_name;
++ int byte, bits_to_clear;
++ struct in6_addr network_copy = r6->network;
++
++ if (!r6->defined)
++ return;
++
++ gc_init (&gc);
++ argv_init (&argv);
++
++ /* clear host bit parts of route
++ * (needed if routes are specified improperly, or if we need to
++ * explicitely setup the "connected" network routes on some OSes)
++ */
++ byte = 15;
++ bits_to_clear = 128 - r6->netbits;
++
++ while( byte >= 0 && bits_to_clear > 0 )
++ {
++ if ( bits_to_clear >= 8 )
++ { network_copy.s6_addr[byte--] = 0; bits_to_clear -= 8; }
++ else
++ { network_copy.s6_addr[byte--] &= (~0 << bits_to_clear); bits_to_clear = 0; }
++ }
++
++ network = print_in6_addr( network_copy, 0, &gc);
++ gateway = print_in6_addr( r6->gateway, 0, &gc);
++
++ if ( !tt->ipv6 )
++ {
++ msg( M_INFO, "add_route_ipv6(): not adding %s/%d, no IPv6 on if %s",
++ network, r6->netbits, device );
++ return;
++ }
++
++ msg( M_INFO, "add_route_ipv6(%s/%d -> %s metric %d) dev %s",
++ network, r6->netbits, gateway, r6->metric, device );
++
++ /*
++ * Filter out routes which are essentially no-ops
++ * (not currently done for IPv6)
++ */
++
++#if defined(TARGET_LINUX)
++#ifdef CONFIG_FEATURE_IPROUTE
++ argv_printf (&argv, "%s -6 route add %s/%d dev %s",
++ iproute_path,
++ network,
++ r6->netbits,
++ device);
++ if (r6->metric_defined)
++ argv_printf_cat (&argv, " metric %d", r6->metric);
++
++#else
++ argv_printf (&argv, "%s -A inet6 add %s/%d dev %s",
++ ROUTE_PATH,
++ network,
++ r6->netbits,
++ device);
++ if (r6->metric_defined)
++ argv_printf_cat (&argv, " metric %d", r6->metric);
++#endif /*CONFIG_FEATURE_IPROUTE*/
++ argv_msg (D_ROUTE, &argv);
++ status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 add command failed");
++
++#elif defined (WIN32)
++
++ /* netsh interface ipv6 add route 2001:db8::/32 MyTunDevice */
++ argv_printf (&argv, "%s%sc interface ipv6 add route %s/%d %s",
++ get_win_sys_path(),
++ NETSH_PATH_SUFFIX,
++ network,
++ r6->netbits,
++ device);
++
++ /* next-hop depends on TUN or TAP mode:
++ * - in TAP mode, we use the "real" next-hop
++ * - in TUN mode we use a special-case link-local address that the tapdrvr
++ * knows about and will answer ND (neighbor discovery) packets for
++ */
++ if ( tt->type == DEV_TYPE_TUN )
++ argv_printf_cat( &argv, " %s", "fe80::8" );
++ else
++ argv_printf_cat( &argv, " %s", gateway );
++
++#if 0
++ if (r->metric_defined)
++ argv_printf_cat (&argv, " METRIC %d", r->metric);
++#endif
++
++ argv_msg (D_ROUTE, &argv);
++
++ netcmd_semaphore_lock ();
++ status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add ipv6 command failed");
++ netcmd_semaphore_release ();
++
++#elif defined (TARGET_SOLARIS)
++
++ /* example: route add -inet6 2001:db8::/32 somegateway 0 */
++
++ /* for some weird reason, this does not work for me unless I set
++ * "metric 0" - otherwise, the routes will be nicely installed, but
++ * packets will just disappear somewhere. So we use "0" now...
++ */
++
++ argv_printf (&argv, "%s add -inet6 %s/%d %s 0",
++ ROUTE_PATH,
++ network,
++ r6->netbits,
++ gateway );
++
++ argv_msg (D_ROUTE, &argv);
++ status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add -inet6 command failed");
++
++#elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)
++
++ argv_printf (&argv, "%s add -inet6 %s/%d -iface %s",
++ ROUTE_PATH,
++ network,
++ r6->netbits,
++ device );
++
++ argv_msg (D_ROUTE, &argv);
++ status = openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route add -inet6 command failed");
++
++#elif defined(TARGET_DARWIN)
++
++ argv_printf (&argv, "%s add -inet6 %s -prefixlen %d -iface %s",
++ ROUTE_PATH,
++ network, r6->netbits, device );
++
++ argv_msg (D_ROUTE, &argv);
++ status = openvpn_execve_check (&argv, es, 0, "ERROR: MacOS X route add -inet6 command failed");
++
++#elif defined(TARGET_OPENBSD)
++
++ argv_printf (&argv, "%s add -inet6 %s -prefixlen %d %s",
++ ROUTE_PATH,
++ network, r6->netbits, gateway );
++
++ argv_msg (D_ROUTE, &argv);
++ status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD route add -inet6 command failed");
++
++#elif defined(TARGET_NETBSD)
++
++ argv_printf (&argv, "%s add -inet6 %s/%d %s",
++ ROUTE_PATH,
++ network, r6->netbits, gateway );
++
++ argv_msg (D_ROUTE, &argv);
++ status = openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD route add -inet6 command failed");
++
++#else
++ msg (M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system. Try putting your routes in a --route-up script");
++#endif
++
++ r6->defined = status;
++ argv_reset (&argv);
++ gc_free (&gc);
++}
++
+ static void
+ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+ {
+@@ -1171,6 +1580,142 @@
+ #endif
+
+ argv_reset (&argv);
++ gc_free (&gc);
++}
++
++static void
++delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
++{
++ struct gc_arena gc;
++ struct argv argv;
++ const char *network;
++ const char *gateway;
++ const char *device = tt->actual_name;
++
++ if (!r6->defined)
++ return;
++
++ gc_init (&gc);
++ argv_init (&argv);
++
++ network = print_in6_addr( r6->network, 0, &gc);
++ gateway = print_in6_addr( r6->gateway, 0, &gc);
++
++ if ( !tt->ipv6 )
++ {
++ msg( M_INFO, "delete_route_ipv6(): not deleting %s/%d, no IPv6 on if %s",
++ network, r6->netbits, device );
++ return;
++ }
++
++ msg( M_INFO, "delete_route_ipv6(%s/%d)", network, r6->netbits );
++
++#if defined(TARGET_LINUX)
++#ifdef CONFIG_FEATURE_IPROUTE
++ argv_printf (&argv, "%s -6 route del %s/%d dev %s",
++ iproute_path,
++ network,
++ r6->netbits,
++ device);
++#else
++ argv_printf (&argv, "%s -A inet6 del %s/%d dev %s",
++ ROUTE_PATH,
++ network,
++ r6->netbits,
++ device);
++#endif /*CONFIG_FEATURE_IPROUTE*/
++ argv_msg (D_ROUTE, &argv);
++ openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 del command failed");
++
++#elif defined (WIN32)
++
++ /* netsh interface ipv6 delete route 2001:db8::/32 MyTunDevice */
++ argv_printf (&argv, "%s%sc interface ipv6 delete route %s/%d %s",
++ get_win_sys_path(),
++ NETSH_PATH_SUFFIX,
++ network,
++ r6->netbits,
++ device);
++
++ /* next-hop depends on TUN or TAP mode:
++ * - in TAP mode, we use the "real" next-hop
++ * - in TUN mode we use a special-case link-local address that the tapdrvr
++ * knows about and will answer ND (neighbor discovery) packets for
++ * (and "route deletion without specifying next-hop" does not work...)
++ */
++ if ( tt->type == DEV_TYPE_TUN )
++ argv_printf_cat( &argv, " %s", "fe80::8" );
++ else
++ argv_printf_cat( &argv, " %s", gateway );
++
++#if 0
++ if (r->metric_defined)
++ argv_printf_cat (&argv, "METRIC %d", r->metric);
++#endif
++
++ argv_msg (D_ROUTE, &argv);
++
++ netcmd_semaphore_lock ();
++ openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add ipv6 command failed");
++ netcmd_semaphore_release ();
++
++#elif defined (TARGET_SOLARIS)
++
++ /* example: route delete -inet6 2001:db8::/32 somegateway */
++ /* GERT-TODO: this is untested, but should work */
++
++ argv_printf (&argv, "%s delete -inet6 %s/%d %s",
++ ROUTE_PATH,
++ network,
++ r6->netbits,
++ gateway );
++
++ argv_msg (D_ROUTE, &argv);
++ openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route delete -inet6 command failed");
++
++#elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)
++
++ argv_printf (&argv, "%s delete -inet6 %s/%d -iface %s",
++ ROUTE_PATH,
++ network,
++ r6->netbits,
++ device );
++
++ argv_msg (D_ROUTE, &argv);
++ openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route delete -inet6 command failed");
++
++#elif defined(TARGET_DARWIN)
++
++ argv_printf (&argv, "%s delete -inet6 %s -prefixlen %d -iface %s",
++ ROUTE_PATH,
++ network, r6->netbits, device );
++
++ argv_msg (D_ROUTE, &argv);
++ openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route delete -inet6 command failed");
++
++#elif defined(TARGET_OPENBSD)
++
++ argv_printf (&argv, "%s delete -inet6 %s -prefixlen %d %s",
++ ROUTE_PATH,
++ network, r6->netbits, gateway );
++
++ argv_msg (D_ROUTE, &argv);
++ openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD route delete -inet6 command failed");
++
++#elif defined(TARGET_NETBSD)
++
++ argv_printf (&argv, "%s delete -inet6 %s/%d %s",
++ ROUTE_PATH,
++ network, r6->netbits, gateway );
++
++ argv_msg (D_ROUTE, &argv);
++ openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD route delete -inet6 command failed");
++
++#else
++ msg (M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system. Try putting your routes in a --route-down script");
++#endif
++
++ argv_reset (&argv);
+ gc_free (&gc);
+ }
+
+Index: openvpn-2.2.1/route.h
+===================================================================
+--- openvpn-2.2.1.orig/route.h 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/route.h 2011-12-13 12:24:54.642739141 +0100
+@@ -92,6 +92,19 @@
+ struct route_option routes[EMPTY_ARRAY_SIZE];
+ };
+
++struct route_ipv6_option {
++ const char *prefix; /* e.g. "2001:db8:1::/64" */
++ const char *gateway; /* e.g. "2001:db8:0::2" */
++ const char *metric; /* e.g. "5" */
++};
++
++struct route_ipv6_option_list {
++ unsigned int flags;
++ int capacity;
++ int n;
++ struct route_ipv6_option routes_ipv6[EMPTY_ARRAY_SIZE];
++};
++
+ struct route {
+ bool defined;
+ const struct route_option *option;
+@@ -113,6 +126,31 @@
+ struct route routes[EMPTY_ARRAY_SIZE];
+ };
+
++struct route_ipv6 {
++ bool defined;
++ const struct route_ipv6_option *option;
++ struct in6_addr network;
++ unsigned int netbits;
++ struct in6_addr gateway;
++ bool metric_defined;
++ int metric;
++};
++
++struct route_ipv6_list {
++ bool routes_added;
++ unsigned int flags;
++ int default_metric;
++ bool default_metric_defined;
++ struct in6_addr remote_endpoint_ipv6;
++ bool remote_endpoint_defined;
++ bool did_redirect_default_gateway; /* TODO (?) */
++ bool did_local; /* TODO (?) */
++ int capacity;
++ int n;
++ struct route_ipv6 routes_ipv6[EMPTY_ARRAY_SIZE];
++};
++
++
+ #if P2MP
+ /* internal OpenVPN route */
+ struct iroute {
+@@ -120,15 +158,24 @@
+ int netbits;
+ struct iroute *next;
+ };
++
++struct iroute_ipv6 {
++ struct in6_addr network;
++ unsigned int netbits;
++ struct iroute_ipv6 *next;
++};
+ #endif
+
+ struct route_option_list *new_route_option_list (const int max_routes, struct gc_arena *a);
++struct route_ipv6_option_list *new_route_ipv6_option_list (const int max_routes, struct gc_arena *a);
+ struct route_option_list *clone_route_option_list (const struct route_option_list *src, struct gc_arena *a);
+ void copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src);
+
+ struct route_list *new_route_list (const int max_routes, struct gc_arena *a);
++struct route_ipv6_list *new_route_ipv6_list (const int max_routes, struct gc_arena *a);
+
+ void add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
++void add_route_ipv6 (struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
+
+ void add_route_to_option_list (struct route_option_list *l,
+ const char *network,
+@@ -136,6 +183,11 @@
+ const char *gateway,
+ const char *metric);
+
++void add_route_ipv6_to_option_list (struct route_ipv6_option_list *l,
++ const char *prefix,
++ const char *gateway,
++ const char *metric);
++
+ bool init_route_list (struct route_list *rl,
+ const struct route_option_list *opt,
+ const char *remote_endpoint,
+@@ -143,21 +195,30 @@
+ in_addr_t remote_host,
+ struct env_set *es);
+
++bool init_route_ipv6_list (struct route_ipv6_list *rl6,
++ const struct route_ipv6_option_list *opt6,
++ const char *remote_endpoint,
++ int default_metric,
++ struct env_set *es);
++
+ void route_list_add_default_gateway (struct route_list *rl,
+ struct env_set *es,
+ const in_addr_t addr);
+
+ void add_routes (struct route_list *rl,
++ struct route_ipv6_list *rl6,
+ const struct tuntap *tt,
+ unsigned int flags,
+ const struct env_set *es);
+
+ void delete_routes (struct route_list *rl,
++ struct route_ipv6_list *rl6,
+ const struct tuntap *tt,
+ unsigned int flags,
+ const struct env_set *es);
+
+ void setenv_routes (struct env_set *es, const struct route_list *rl);
++void setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6);
+
+ bool is_special_addr (const char *addr_str);
+
+Index: openvpn-2.2.1/socket.c
+===================================================================
+--- openvpn-2.2.1.orig/socket.c 2011-12-13 12:23:07.000000000 +0100
++++ openvpn-2.2.1/socket.c 2011-12-13 12:24:54.645739102 +0100
+@@ -543,6 +543,24 @@
+ }
+ }
+
++bool
++ipv6_addr_safe (const char *ipv6_text_addr)
++{
++ /* verify non-NULL */
++ if (!ipv6_text_addr)
++ return false;
++
++ /* verify length is within limits */
++ if (strlen (ipv6_text_addr) > INET6_ADDRSTRLEN )
++ return false;
++
++ /* verify that string will convert to IPv6 address */
++ {
++ struct in6_addr a6;
++ return inet_pton( AF_INET6, ipv6_text_addr, &a6 ) == 1;
++ }
++}
++
+ static bool
+ dns_addr_safe (const char *addr)
+ {
+@@ -2578,6 +2596,55 @@
+ return BSTR (&out);
+ }
+
++/*
++ * Convert an in6_addr in host byte order
++ * to an ascii representation of an IPv6 address
++ */
++const char *
++print_in6_addr (struct in6_addr a6, unsigned int flags, struct gc_arena *gc)
++{
++ struct buffer out = alloc_buf_gc (64, gc);
++ char tmp_out_buf[64]; /* inet_ntop wants pointer to buffer */
++
++ if ( memcmp(&a6, &in6addr_any, sizeof(a6)) != 0 ||
++ !(flags & IA_EMPTY_IF_UNDEF))
++ {
++ inet_ntop (AF_INET6, &a6, tmp_out_buf, sizeof(tmp_out_buf)-1);
++ buf_printf (&out, "%s", tmp_out_buf );
++ }
++ return BSTR (&out);
++}
++
++/* add some offset to an ipv6 address
++ * (add in steps of 32 bits, taking overflow into next round)
++ */
++#ifndef s6_addr32
++# ifdef TARGET_SOLARIS
++# define s6_addr32 _S6_un._S6_u32
++# else
++# define s6_addr32 __u6_addr.__u6_addr32
++# endif
++#endif
++#ifndef UINT32_MAX
++# define UINT32_MAX (4294967295U)
++#endif
++struct in6_addr add_in6_addr( struct in6_addr base, uint32_t add )
++{
++ int i;
++ uint32_t h;
++
++ for( i=3; i>=0 && add > 0 ; i-- )
++ {
++ h = ntohl( base.s6_addr32[i] );
++ base.s6_addr32[i] = htonl( (h+add) & UINT32_MAX );
++ /* 32-bit overrun?
++ * caveat: can't do "h+add > UINT32_MAX" with 32bit math!
++ */
++ add = ( h > UINT32_MAX - add )? 1: 0;
++ }
++ return base;
++}
++
+ /* set environmental variables for ip/port in *addr */
+ void
+ setenv_sockaddr (struct env_set *es, const char *name_prefix, const struct openvpn_sockaddr *addr, const bool flags)
+@@ -3081,6 +3148,58 @@
+
+ #ifdef WIN32
+
++/*
++ * inet_ntop() and inet_pton() wrap-implementations using
++ * WSAAddressToString() and WSAStringToAddress() functions
++ */
++const char *
++inet_ntop(int af, const void *src, char *dst, socklen_t size)
++{
++ struct sockaddr_storage ss;
++ unsigned long s = size;
++
++ CLEAR(ss);
++ ss.ss_family = af;
++
++ switch(af) {
++ case AF_INET:
++ ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src;
++ break;
++ case AF_INET6:
++ ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src;
++ break;
++ default:
++ ASSERT (0);
++ }
++ // cannot direclty use &size because of strict aliasing rules
++ return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)?
++ dst : NULL;
++}
++
++int
++inet_pton(int af, const char *src, void *dst)
++{
++ struct sockaddr_storage ss;
++ int size = sizeof(ss);
++ char src_copy[INET6_ADDRSTRLEN+1];
++
++ CLEAR(ss);
++ // stupid non-const API
++ strncpynt(src_copy, src, INET6_ADDRSTRLEN+1);
++
++ if (WSAStringToAddress(src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) {
++ switch(af) {
++ case AF_INET:
++ *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr;
++ return 1;
++ case AF_INET6:
++ *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr;
++ return 1;
++ }
++ }
++ return 0;
++}
++
+ int
+ socket_recv_queue (struct link_socket *sock, int maxsize)
+ {
+Index: openvpn-2.2.1/socket.h
+===================================================================
+--- openvpn-2.2.1.orig/socket.h 2011-12-13 12:23:07.000000000 +0100
++++ openvpn-2.2.1/socket.h 2011-12-13 12:24:54.646739089 +0100
+@@ -368,6 +368,8 @@
+ #define IA_EMPTY_IF_UNDEF (1<<0)
+ #define IA_NET_ORDER (1<<1)
+ const char *print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc);
++const char *print_in6_addr (struct in6_addr addr6, unsigned int flags, struct gc_arena *gc);
++struct in6_addr add_in6_addr( struct in6_addr base, uint32_t add );
+
+ #define SA_IP_PORT (1<<0)
+ #define SA_SET_IF_NONZERO (1<<1)
+@@ -427,6 +429,7 @@
+ bool ip_addr_dotted_quad_safe (const char *dotted_quad);
+ bool ip_or_dns_addr_safe (const char *addr, const bool allow_fqdn);
+ bool mac_addr_safe (const char *mac_addr);
++bool ipv6_addr_safe (const char *ipv6_text_addr);
+
+ socket_descriptor_t create_socket_tcp (void);
+
+Index: openvpn-2.2.1/tun.c
+===================================================================
+--- openvpn-2.2.1.orig/tun.c 2011-12-13 12:23:07.394079932 +0100
++++ openvpn-2.2.1/tun.c 2011-12-13 12:41:30.078294479 +0100
+@@ -56,13 +56,14 @@
+ const in_addr_t ip,
+ const in_addr_t netmask,
+ const unsigned int flags);
++static void netsh_command (const struct argv *a, int n);
+
+ static const char *netsh_get_id (const char *dev_node, struct gc_arena *gc);
+
+ #endif
+
+ #ifdef TARGET_SOLARIS
+-static void solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual);
++static void solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual, bool unplumb_inet6);
+ #include <stropts.h>
+ #endif
+
+@@ -129,30 +130,6 @@
+ return dev;
+ }
+
+-/*
+- * Called by the open_tun function of OSes to check if we
+- * explicitly support IPv6.
+- *
+- * In this context, explicit means that the OS expects us to
+- * do something special to the tun socket in order to support
+- * IPv6, i.e. it is not transparent.
+- *
+- * ipv6_explicitly_supported should be set to false if we don't
+- * have any explicit IPv6 code in the tun device handler.
+- *
+- * If ipv6_explicitly_supported is true, then we have explicit
+- * OS-specific tun dev code for handling IPv6. If so, tt->ipv6
+- * is set according to the --tun-ipv6 command line option.
+- */
+-static void
+-ipv6_support (bool ipv6, bool ipv6_explicitly_supported, struct tuntap* tt)
+-{
+- tt->ipv6 = false;
+- if (ipv6_explicitly_supported)
+- tt->ipv6 = ipv6;
+- else if (ipv6)
+- msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS");
+-}
+
+ /* --ifconfig-nowarn disables some options sanity checking */
+ static const char ifconfig_warn_how_to_silence[] = "(silence this warning with --ifconfig-nowarn)";
+@@ -423,6 +400,8 @@
+ int topology, /* one of the TOP_x values */
+ const char *ifconfig_local_parm, /* --ifconfig parm 1 */
+ const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */
++ const char *ifconfig_ipv6_local_parm, /* --ifconfig parm 1 IPv6 */
++ const char *ifconfig_ipv6_remote_parm, /* --ifconfig parm 2 IPv6 */
+ in_addr_t local_public,
+ in_addr_t remote_public,
+ const bool strict_warn,
+@@ -537,6 +516,40 @@
+
+ tt->did_ifconfig_setup = true;
+ }
++
++ if (ifconfig_ipv6_local_parm && ifconfig_ipv6_remote_parm)
++ {
++ const char *ifconfig_ipv6_local = NULL;
++ const char *ifconfig_ipv6_remote = NULL;
++
++ /*
++ * Convert arguments to binary IPv6 addresses.
++ */
++
++ if ( inet_pton( AF_INET6, ifconfig_ipv6_local_parm, &tt->local_ipv6 ) != 1 ||
++ inet_pton( AF_INET6, ifconfig_ipv6_remote_parm, &tt->remote_ipv6 ) != 1 )
++ {
++ msg( M_FATAL, "init_tun: problem converting IPv6 ifconfig addresses %s and %s to binary", ifconfig_ipv6_local_parm, ifconfig_ipv6_remote_parm );
++ }
++ tt->netbits_ipv6 = 64;
++
++ /*
++ * Set ifconfig parameters
++ */
++ ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc);
++ ifconfig_ipv6_remote = print_in6_addr (tt->remote_ipv6, 0, &gc);
++
++ /*
++ * Set environmental variables with ifconfig parameters.
++ */
++ if (es)
++ {
++ setenv_str (es, "ifconfig_ipv6_local", ifconfig_ipv6_local);
++ setenv_str (es, "ifconfig_ipv6_remote", ifconfig_ipv6_remote);
++ }
++ tt->did_ifconfig_ipv6_setup = true;
++ }
++
+ gc_free (&gc);
+ return tt;
+ }
+@@ -559,6 +572,28 @@
+ #endif
+ }
+
++#if defined(TARGET_WIN32) || \
++ defined(TARGET_DARWIN) || defined(TARGET_NETBSD) || defined(TARGET_OPENBSD)
++
++/* some of the platforms will auto-add a "network route" pointing
++ * to the interface on "ifconfig tunX 2001:db8::1/64", others need
++ * an extra call to "route add..."
++ * -> helper function to simplify code below
++ */
++void add_route_connected_v6_net(struct tuntap * tt,
++ const struct env_set *es)
++{
++ struct route_ipv6 r6;
++
++ r6.defined = true;
++ r6.network = tt->local_ipv6;
++ r6.netbits = tt->netbits_ipv6;
++ r6.gateway = tt->local_ipv6;
++ add_route_ipv6 (&r6, tt, 0, es);
++}
++#endif
++
++
+ /* execute the ifconfig command through the shell */
+ void
+ do_ifconfig (struct tuntap *tt,
+@@ -574,10 +609,16 @@
+ const char *ifconfig_local = NULL;
+ const char *ifconfig_remote_netmask = NULL;
+ const char *ifconfig_broadcast = NULL;
++ const char *ifconfig_ipv6_local = NULL;
++ const char *ifconfig_ipv6_remote = NULL;
++ bool do_ipv6 = false;
+ struct argv argv;
+
+ argv_init (&argv);
+
++ msg( M_INFO, "do_ifconfig, tt->ipv6=%d, tt->did_ifconfig_ipv6_setup=%d",
++ tt->ipv6, tt->did_ifconfig_ipv6_setup );
++
+ /*
+ * We only handle TUN/TAP devices here, not --dev null devices.
+ */
+@@ -589,6 +630,13 @@
+ ifconfig_local = print_in_addr_t (tt->local, 0, &gc);
+ ifconfig_remote_netmask = print_in_addr_t (tt->remote_netmask, 0, &gc);
+
++ if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup )
++ {
++ ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc);
++ ifconfig_ipv6_remote = print_in6_addr (tt->remote_ipv6, 0, &gc);
++ do_ipv6 = true;
++ }
++
+ /*
+ * If TAP-style device, generate broadcast address.
+ */
+@@ -647,7 +695,19 @@
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
+ }
+- tt->did_ifconfig = true;
++ if ( do_ipv6 )
++ {
++ argv_printf( &argv,
++ "%s -6 addr add %s/%d dev %s",
++ iproute_path,
++ ifconfig_ipv6_local,
++ tt->netbits_ipv6,
++ actual
++ );
++ argv_msg (M_INFO, &argv);
++ openvpn_execve_check (&argv, es, S_FATAL, "Linux ip -6 addr add failed");
++ }
++ tt->did_ifconfig = true;
+ #else
+ if (tun)
+ argv_printf (&argv,
+@@ -670,6 +730,18 @@
+ );
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "Linux ifconfig failed");
++ if ( do_ipv6 )
++ {
++ argv_printf (&argv,
++ "%s %s inet6 add %s/%d",
++ IFCONFIG_PATH,
++ actual,
++ ifconfig_ipv6_local,
++ tt->netbits_ipv6
++ );
++ argv_msg (M_INFO, &argv);
++ openvpn_execve_check (&argv, es, S_FATAL, "Linux ifconfig inet6 failed");
++ }
+ tt->did_ifconfig = true;
+
+ #endif /*CONFIG_FEATURE_IPROUTE*/
+@@ -693,7 +765,7 @@
+
+ argv_msg (M_INFO, &argv);
+ if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-1 failed"))
+- solaris_error_close (tt, es, actual);
++ solaris_error_close (tt, es, actual, false);
+
+ argv_printf (&argv,
+ "%s %s netmask 255.255.255.255",
+@@ -725,7 +797,53 @@
+
+ argv_msg (M_INFO, &argv);
+ if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-2 failed"))
+- solaris_error_close (tt, es, actual);
++ solaris_error_close (tt, es, actual, false);
++
++ if ( do_ipv6 )
++ {
++ argv_printf (&argv, "%s %s inet6 unplumb",
++ IFCONFIG_PATH, actual );
++ argv_msg (M_INFO, &argv);
++ openvpn_execve_check (&argv, es, 0, NULL);
++
++ if ( tt->type == DEV_TYPE_TUN )
++ {
++ argv_printf (&argv,
++ "%s %s inet6 plumb %s/%d %s up",
++ IFCONFIG_PATH,
++ actual,
++ ifconfig_ipv6_local,
++ tt->netbits_ipv6,
++ ifconfig_ipv6_remote
++ );
++ }
++ else /* tap mode */
++ {
++ /* base IPv6 tap interface needs to be brought up first
++ */
++ argv_printf (&argv, "%s %s inet6 plumb up",
++ IFCONFIG_PATH, actual );
++ argv_msg (M_INFO, &argv);
++ if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig IPv6 (prepare) failed"))
++ solaris_error_close (tt, es, actual, true);
++
++ /* we might need to do "ifconfig %s inet6 auto-dhcp drop"
++ * after the system has noticed the interface and fired up
++ * the DHCPv6 client - but this takes quite a while, and the
++ * server will ignore the DHCPv6 packets anyway. So we don't.
++ */
++
++ /* static IPv6 addresses need to go to a subinterface (tap0:1)
++ */
++ argv_printf (&argv,
++ "%s %s inet6 addif %s/%d up",
++ IFCONFIG_PATH, actual,
++ ifconfig_ipv6_local, tt->netbits_ipv6 );
++ }
++ argv_msg (M_INFO, &argv);
++ if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig IPv6 failed"))
++ solaris_error_close (tt, es, actual, true);
++ }
+
+ if (!tun && tt->topology == TOP_SUBNET)
+ {
+@@ -787,10 +905,42 @@
+ );
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "OpenBSD ifconfig failed");
++ if ( do_ipv6 )
++ {
++ argv_printf (&argv,
++ "%s %s inet6 %s/%d",
++ IFCONFIG_PATH,
++ actual,
++ ifconfig_ipv6_local,
++ tt->netbits_ipv6
++ );
++ argv_msg (M_INFO, &argv);
++ openvpn_execve_check (&argv, es, S_FATAL, "OpenBSD ifconfig inet6 failed");
++
++ /* and, hooray, we explicitely need to add a route... */
++ add_route_connected_v6_net(tt, es);
++ }
+ tt->did_ifconfig = true;
+
+ #elif defined(TARGET_NETBSD)
+
++/* whether or not NetBSD can do IPv6 can be seen by the availability of
++ * the TUNSIFHEAD ioctl() - see next TARGET_NETBSD block for more details
++ */
++#ifdef TUNSIFHEAD
++# define NETBSD_MULTI_AF
++#endif
++
++ /* as on OpenBSD and Darwin, destroy and re-create tun<x> interface
++ */
++ argv_printf (&argv, "%s %s destroy", IFCONFIG_PATH, actual );
++ argv_msg (M_INFO, &argv);
++ openvpn_execve_check (&argv, es, 0, "NetBSD ifconfig destroy failed");
++
++ argv_printf (&argv, "%s %s create", IFCONFIG_PATH, actual );
++ argv_msg (M_INFO, &argv);
++ openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig create failed");
++
+ if (tun)
+ argv_printf (&argv,
+ "%s %s %s %s mtu %d netmask 255.255.255.255 up",
+@@ -817,6 +967,27 @@
+ );
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig failed");
++
++ if ( do_ipv6 )
++ {
++#ifdef NETBSD_MULTI_AF
++ argv_printf (&argv,
++ "%s %s inet6 %s/%d",
++ IFCONFIG_PATH,
++ actual,
++ ifconfig_ipv6_local,
++ tt->netbits_ipv6
++ );
++ argv_msg (M_INFO, &argv);
++ openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig inet6 failed");
++
++ /* and, hooray, we explicitely need to add a route... */
++ add_route_connected_v6_net(tt, es);
++#else
++ msg( M_INFO, "no IPv6 support for tun interfaces on NetBSD before 4.0 (if your system is newer, recompile openvpn)" );
++ tt->ipv6 = false;
++#endif
++ }
+ tt->did_ifconfig = true;
+
+ #elif defined(TARGET_DARWIN)
+@@ -882,6 +1053,22 @@
+ add_route (&r, tt, 0, es);
+ }
+
++ if ( do_ipv6 )
++ {
++ argv_printf (&argv,
++ "%s %s inet6 %s/%d",
++ IFCONFIG_PATH,
++ actual,
++ ifconfig_ipv6_local,
++ tt->netbits_ipv6
++ );
++ argv_msg (M_INFO, &argv);
++ openvpn_execve_check (&argv, es, S_FATAL, "MacOS X ifconfig inet6 failed");
++
++ /* and, hooray, we explicitely need to add a route... */
++ add_route_connected_v6_net(tt, es);
++ }
++
+ #elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)
+
+ /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
+@@ -920,6 +1107,19 @@
+ add_route (&r, tt, 0, es);
+ }
+
++ if ( do_ipv6 )
++ {
++ argv_printf (&argv,
++ "%s %s inet6 %s/%d",
++ IFCONFIG_PATH,
++ actual,
++ ifconfig_ipv6_local,
++ tt->netbits_ipv6
++ );
++ argv_msg (M_INFO, &argv);
++ openvpn_execve_check (&argv, es, S_FATAL, "FreeBSD ifconfig inet6 failed");
++ }
++
+ #elif defined (WIN32)
+ {
+ /*
+@@ -959,6 +1159,34 @@
+ tt->did_ifconfig = true;
+ }
+
++ /* IPv6 always uses "netsh" interface */
++ if ( do_ipv6 )
++ {
++ char * saved_actual;
++
++ if (!strcmp (actual, "NULL"))
++ msg (M_FATAL, "Error: When using --tun-ipv6, if you have more than one TAP-Win32 adapter, you must also specify --dev-node");
++
++ /* example: netsh interface ipv6 add address MyTap 2001:608:8003::d */
++ argv_printf (&argv,
++ "%s%sc interface ipv6 add address %s %s",
++ get_win_sys_path(),
++ NETSH_PATH_SUFFIX,
++ actual,
++ ifconfig_ipv6_local );
++
++ netsh_command (&argv, 4);
++
++ /* explicit route needed */
++ /* on windows, OpenVPN does ifconfig first, open_tun later, so
++ * tt->actual_name might not yet be initialized, but routing code
++ * needs to know interface name - point to "actual", restore later
++ */
++ saved_actual = tt->actual_name;
++ tt->actual_name = (char*) actual;
++ add_route_connected_v6_net(tt, es);
++ tt->actual_name = saved_actual;
++ }
+ #else
+ msg (M_FATAL, "Sorry, but I don't know how to do 'ifconfig' commands on this operating system. You should ifconfig your TUN/TAP device manually or use an --up script.");
+ #endif
+@@ -991,14 +1219,16 @@
+ #ifndef WIN32
+ static void
+ open_tun_generic (const char *dev, const char *dev_type, const char *dev_node,
+- bool ipv6, bool ipv6_explicitly_supported, bool dynamic,
++ bool ipv6_explicitly_supported, bool dynamic,
+ struct tuntap *tt)
+ {
+ char tunname[256];
+ char dynamic_name[256];
+ bool dynamic_opened = false;
+
+- ipv6_support (ipv6, ipv6_explicitly_supported, tt);
++
++ if ( tt->ipv6 && ! ipv6_explicitly_supported )
++ msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS");
+
+ if (tt->type == DEV_TYPE_NULL)
+ {
+@@ -1094,16 +1324,16 @@
+ #if !PEDANTIC
+
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+ struct ifreq ifr;
+
+- /*
+- * Set tt->ipv6 to true if
+- * (a) we have the capability of supporting --tun-ipv6, and
+- * (b) --tun-ipv6 was specified.
++ /* warn if a very old linux version is used & --tun-ipv6 set
+ */
+- ipv6_support (ipv6, LINUX_IPV6, tt);
++#if LINUX_IPV6 == 0
++ if ( tt->ipv6 )
++ msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS");
++#endif
+
+ /*
+ * We handle --dev null specially, we do not open /dev/null for this.
+@@ -1212,7 +1442,7 @@
+ #else
+
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+ ASSERT (0);
+ }
+@@ -1222,9 +1452,9 @@
+ #else
+
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+- open_tun_generic (dev, dev_type, dev_node, ipv6, false, true, tt);
++ open_tun_generic (dev, dev_type, dev_node, false, true, tt);
+ }
+
+ #endif /* HAVE_LINUX_IF_TUN_H */
+@@ -1244,7 +1474,7 @@
+ #endif
+
+ void
+-tuncfg (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, int persist_mode, const char *username, const char *groupname, const struct tuntap_options *options)
++tuncfg (const char *dev, const char *dev_type, const char *dev_node, int persist_mode, const char *username, const char *groupname, const struct tuntap_options *options)
+ {
+ struct tuntap *tt;
+
+@@ -1252,7 +1482,7 @@
+ clear_tuntap (tt);
+ tt->type = dev_type_enum (dev, dev_type);
+ tt->options = *options;
+- open_tun (dev, dev_type, dev_node, ipv6, tt);
++ open_tun (dev, dev_type, dev_node, tt);
+ if (ioctl (tt->fd, TUNSETPERSIST, persist_mode) < 0)
+ msg (M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
+ if (username != NULL)
+@@ -1395,7 +1625,7 @@
+ #endif
+
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+ int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
+ struct lifreq ifr;
+@@ -1406,7 +1636,10 @@
+ bool is_tun;
+ struct strioctl strioc_if, strioc_ppa;
+
+- ipv6_support (ipv6, true, tt);
++ /* improved generic TUN/TAP driver from
++ * http://www.whiteboard.ne.jp/~admin2/tuntap/
++ * has IPv6 support
++ */
+ memset(&ifr, 0x0, sizeof(ifr));
+
+ if (tt->type == DEV_TYPE_NULL)
+@@ -1561,6 +1794,18 @@
+ {
+ if (tt)
+ {
++ /* IPv6 interfaces need to be 'manually' de-configured */
++ if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup )
++ {
++ struct argv argv;
++ argv_init (&argv);
++ argv_printf( &argv, "%s %s inet6 unplumb",
++ IFCONFIG_PATH, tt->actual_name );
++ argv_msg (M_INFO, &argv);
++ openvpn_execve_check (&argv, NULL, 0, "Solaris ifconfig inet6 unplumb failed");
++ argv_reset (&argv);
++ }
++
+ if (tt->ip_fd >= 0)
+ {
+ struct lifreq ifr;
+@@ -1613,11 +1858,20 @@
+ }
+
+ static void
+-solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual)
++solaris_error_close (struct tuntap *tt, const struct env_set *es,
++ const char *actual, bool unplumb_inet6 )
+ {
+ struct argv argv;
+ argv_init (&argv);
+
++ if (unplumb_inet6)
++ {
++ argv_printf( &argv, "%s %s inet6 unplumb",
++ IFCONFIG_PATH, actual );
++ argv_msg (M_INFO, &argv);
++ openvpn_execve_check (&argv, es, 0, "Solaris ifconfig inet6 unplumb failed");
++ }
++
+ argv_printf (&argv,
+ "%s %s unplumb",
+ IFCONFIG_PATH,
+@@ -1674,9 +1928,9 @@
+ */
+
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+- open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
++ open_tun_generic (dev, dev_type, dev_node, true, true, tt);
+
+ /* Enable multicast on the interface */
+ if (tt->fd >= 0)
+@@ -1699,12 +1953,31 @@
+ }
+ }
+
++/* the current way OpenVPN handles tun devices on OpenBSD leads to
++ * lingering tunX interfaces after close -> for a full cleanup, they
++ * need to be explicitely destroyed
++ */
++
+ void
+ close_tun (struct tuntap* tt)
+ {
+ if (tt)
+ {
++ struct gc_arena gc = gc_new ();
++ struct argv argv;
++
++ /* setup command, close tun dev (clears tt->actual_name!), run command
++ */
++
++ argv_init (&argv);
++ argv_printf (&argv, "%s %s destroy",
++ IFCONFIG_PATH, tt->actual_name);
++
+ close_tun_generic (tt);
++
++ argv_msg (M_INFO, &argv);
++ openvpn_execve_check (&argv, NULL, 0, "OpenBSD 'destroy tun interface' failed (non-critical)");
++
+ free (tt);
+ }
+ }
+@@ -1767,33 +2040,51 @@
+ #elif defined(TARGET_NETBSD)
+
+ /*
+- * NetBSD does not support IPv6 on tun out of the box,
+- * but there exists a patch. When this patch is applied,
+- * only two things are left to openvpn:
+- * 1. Activate multicasting (this has already been done
+- * before by the kernel, but we make sure that nobody
+- * has deactivated multicasting inbetween.
+- * 2. Deactivate "link layer mode" (otherwise NetBSD
+- * prepends the address family to the packet, and we
+- * would run into the same trouble as with OpenBSD.
++ * NetBSD before 4.0 does not support IPv6 on tun out of the box,
++ * but there exists a patch (sys/net/if_tun.c, 1.79->1.80, see PR 32944).
++ *
++ * NetBSD 4.0 and up do, but we need to put the tun interface into
++ * "multi_af" mode, which will prepend the address family to all packets
++ * (same as OpenBSD and FreeBSD). If this is not enabled, the kernel
++ * silently drops all IPv6 packets on output and gets confused on input.
++ *
++ * On earlier versions, multi_af is not available at all, so we have
++ * two different NetBSD code variants here :-(
++ *
+ */
+
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+- open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
++#ifdef NETBSD_MULTI_AF
++ open_tun_generic (dev, dev_type, dev_node, true, true, tt);
++#else
++ open_tun_generic (dev, dev_type, dev_node, false, true, tt);
++#endif
++
+ if (tt->fd >= 0)
+ {
+ int i = IFF_POINTOPOINT|IFF_MULTICAST;
+ ioctl (tt->fd, TUNSIFMODE, &i); /* multicast on */
+ i = 0;
+ ioctl (tt->fd, TUNSLMODE, &i); /* link layer mode off */
++
++#ifdef NETBSD_MULTI_AF
++ i = 1;
++ if (ioctl (tt->fd, TUNSIFHEAD, &i) < 0) /* multi-af mode on */
++ {
++ msg (M_WARN | M_ERRNO, "ioctl(TUNSIFHEAD): %s", strerror(errno));
++ }
++#endif
+ }
+ }
+
+ void
+ close_tun (struct tuntap *tt)
+ {
++ /* TODO: we really should cleanup non-persistant tunX with
++ * "ifconfig tunX destroy" here...
++ */
+ if (tt)
+ {
+ close_tun_generic (tt);
+@@ -1801,6 +2092,65 @@
+ }
+ }
+
++#ifdef NETBSD_MULTI_AF
++
++static inline int
++netbsd_modify_read_write_return (int len)
++{
++ if (len > 0)
++ return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0;
++ else
++ return len;
++}
++
++int
++write_tun (struct tuntap* tt, uint8_t *buf, int len)
++{
++ if (tt->type == DEV_TYPE_TUN)
++ {
++ u_int32_t type;
++ struct iovec iv[2];
++ struct openvpn_iphdr *iph;
++
++ iph = (struct openvpn_iphdr *) buf;
++
++ if (tt->ipv6 && OPENVPN_IPH_GET_VER(iph->version_len) == 6)
++ type = htonl (AF_INET6);
++ else
++ type = htonl (AF_INET);
++
++ iv[0].iov_base = (char *)&type;
++ iv[0].iov_len = sizeof (type);
++ iv[1].iov_base = buf;
++ iv[1].iov_len = len;
++
++ return netbsd_modify_read_write_return (writev (tt->fd, iv, 2));
++ }
++ else
++ return write (tt->fd, buf, len);
++}
++
++int
++read_tun (struct tuntap* tt, uint8_t *buf, int len)
++{
++ if (tt->type == DEV_TYPE_TUN)
++ {
++ u_int32_t type;
++ struct iovec iv[2];
++
++ iv[0].iov_base = (char *)&type;
++ iv[0].iov_len = sizeof (type);
++ iv[1].iov_base = buf;
++ iv[1].iov_len = len;
++
++ return netbsd_modify_read_write_return (readv (tt->fd, iv, 2));
++ }
++ else
++ return read (tt->fd, buf, len);
++}
++
++#else /* not NETBSD_MULTI_AF -> older code, IPv4 only */
++
+ int
+ write_tun (struct tuntap* tt, uint8_t *buf, int len)
+ {
+@@ -1812,6 +2162,7 @@
+ {
+ return read (tt->fd, buf, len);
+ }
++#endif /* NETBSD_MULTI_AF */
+
+ #elif defined(TARGET_FREEBSD)
+
+@@ -1825,9 +2176,9 @@
+ }
+
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+- open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
++ open_tun_generic (dev, dev_type, dev_node, true, true, tt);
+
+ if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
+ {
+@@ -1913,9 +2264,9 @@
+ }
+
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+- open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
++ open_tun_generic (dev, dev_type, dev_node, true, true, tt);
+
+ if (tt->fd >= 0)
+ {
+@@ -1984,6 +2335,61 @@
+ return read (tt->fd, buf, len);
+ }
+
++#elif defined(TARGET_DARWIN)
++
++/* Darwin (MacOS X) is mostly "just use the generic stuff", but there
++ * is always one caveat...:
++ *
++ * If IPv6 is configured, and the tun device is closed, the IPv6 address
++ * configured to the tun interface changes to a lingering /128 route
++ * pointing to lo0. Need to unconfigure... (observed on 10.5)
++ */
++
++void
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
++{
++ open_tun_generic (dev, dev_type, dev_node, false, true, tt);
++}
++
++void
++close_tun (struct tuntap* tt)
++{
++ if (tt)
++ {
++ struct gc_arena gc = gc_new ();
++ struct argv argv;
++ argv_init (&argv);
++
++ if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup )
++ {
++ const char * ifconfig_ipv6_local =
++ print_in6_addr (tt->local_ipv6, 0, &gc);
++
++ argv_printf (&argv, "%s delete -inet6 %s",
++ ROUTE_PATH, ifconfig_ipv6_local );
++ argv_msg (M_INFO, &argv);
++ openvpn_execve_check (&argv, NULL, 0, "MacOS X 'remove inet6 route' failed (non-critical)");
++ }
++
++ close_tun_generic (tt);
++ free (tt);
++ argv_reset (&argv);
++ gc_free (&gc);
++ }
++}
++
++int
++write_tun (struct tuntap* tt, uint8_t *buf, int len)
++{
++ return write (tt->fd, buf, len);
++}
++
++int
++read_tun (struct tuntap* tt, uint8_t *buf, int len)
++{
++ return read (tt->fd, buf, len);
++}
++
+ #elif defined(WIN32)
+
+ int
+@@ -3969,7 +4375,7 @@
+ }
+
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+ struct gc_arena gc = gc_new ();
+ char device_path[256];
+@@ -3980,7 +4386,7 @@
+
+ /*netcmd_semaphore_lock ();*/
+
+- ipv6_support (ipv6, false, tt);
++ msg( M_INFO, "open_tun, tt->ipv6=%d", tt->ipv6 );
+
+ if (tt->type == DEV_TYPE_NULL)
+ {
+@@ -4102,6 +4508,16 @@
+ msg (M_FATAL, "ERROR: This version of " PACKAGE_NAME " requires a TAP-Win32 driver that is at least version %d.%d -- If you recently upgraded your " PACKAGE_NAME " distribution, a reboot is probably required at this point to get Windows to see the new driver.",
+ TAP_WIN32_MIN_MAJOR,
+ TAP_WIN32_MIN_MINOR);
++
++ /* usage of numeric constants is ugly, but this is really tied to
++ * *this* version of the driver
++ */
++ if ( tt->ipv6 && tt->type == DEV_TYPE_TUN &&
++ info[0] == 9 && info[1] < 8)
++ {
++ msg( M_INFO, "WARNING: Tap-Win32 driver version %d.%d does not support IPv6 in TUN mode. IPv6 will be disabled. Upgrade to Tap-Win32 9.8 (2.2-beta3 release or later) or use TAP mode to get IPv6", (int) info[0], (int) info[1] );
++ tt->ipv6 = false;
++ }
+ }
+
+ /* get driver MTU */
+@@ -4426,6 +4842,12 @@
+
+ if (tt)
+ {
++ if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup )
++ {
++ /* netsh interface ipv6 delete address \"%s\" %s */
++ const char * ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc);
++ msg( M_WARN, "TODO: remove IPv6 address %s", ifconfig_ipv6_local );
++ }
+ #if 1
+ if (tt->ipapi_context_defined)
+ {
+@@ -4529,9 +4951,9 @@
+ #else /* generic */
+
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+- open_tun_generic (dev, dev_type, dev_node, ipv6, false, true, tt);
++ open_tun_generic (dev, dev_type, dev_node, false, true, tt);
+ }
+
+ void
+Index: openvpn-2.2.1/tun.h
+===================================================================
+--- openvpn-2.2.1.orig/tun.h 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/tun.h 2011-12-13 12:24:54.653739003 +0100
+@@ -130,6 +130,7 @@
+ int topology; /* one of the TOP_x values */
+
+ bool did_ifconfig_setup;
++ bool did_ifconfig_ipv6_setup;
+ bool did_ifconfig;
+
+ bool ipv6;
+@@ -146,6 +147,10 @@
+ in_addr_t remote_netmask;
+ in_addr_t broadcast;
+
++ struct in6_addr local_ipv6;
++ struct in6_addr remote_ipv6;
++ int netbits_ipv6;
++
+ #ifdef WIN32
+ HANDLE hand;
+ struct overlapped_io reads;
+@@ -197,7 +202,7 @@
+ void clear_tuntap (struct tuntap *tuntap);
+
+ void open_tun (const char *dev, const char *dev_type, const char *dev_node,
+- bool ipv6, struct tuntap *tt);
++ struct tuntap *tt);
+
+ void close_tun (struct tuntap *tt);
+
+@@ -206,7 +211,7 @@
+ int read_tun (struct tuntap* tt, uint8_t *buf, int len);
+
+ void tuncfg (const char *dev, const char *dev_type, const char *dev_node,
+- bool ipv6, int persist_mode, const char *username,
++ int persist_mode, const char *username,
+ const char *groupname, const struct tuntap_options *options);
+
+ const char *guess_tuntap_dev (const char *dev,
+@@ -219,6 +224,8 @@
+ int topology, /* one of the TOP_x values */
+ const char *ifconfig_local_parm, /* --ifconfig parm 1 */
+ const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */
++ const char *ifconfig_ipv6_local_parm, /* --ifconfig parm 1 / IPv6 */
++ const char *ifconfig_ipv6_remote_parm, /* --ifconfig parm 2 / IPv6 */
+ in_addr_t local_public,
+ in_addr_t remote_public,
+ const bool strict_warn,
+Index: openvpn-2.2.1/win32.c
+===================================================================
+--- openvpn-2.2.1.orig/win32.c 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/win32.c 2011-12-13 12:24:54.654738990 +0100
+@@ -874,16 +874,21 @@
+ static char *
+ env_block (const struct env_set *es)
+ {
++ char * force_path = "PATH=C:\\Windows\\System32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem";
++
+ if (es)
+ {
+ struct env_item *e;
+ char *ret;
+ char *p;
+ size_t nchars = 1;
++ bool path_seen = false;
+
+ for (e = es->list; e != NULL; e = e->next)
+ nchars += strlen (e->string) + 1;
+
++ nchars += strlen(force_path)+1;
++
+ ret = (char *) malloc (nchars);
+ check_malloc_return (ret);
+
+@@ -895,7 +900,18 @@
+ strcpy (p, e->string);
+ p += strlen (e->string) + 1;
+ }
++ if ( strncmp(e->string, "PATH=", 5 ) == 0 )
++ path_seen = true;
++ }
++
++ /* make sure PATH is set */
++ if ( !path_seen )
++ {
++ msg( M_INFO, "env_block: add %s", force_path );
++ strcpy( p, force_path );
++ p += strlen(force_path) + 1;
+ }
++
+ *p = '\0';
+ return ret;
+ }
+Index: openvpn-2.2.1/win32.h
+===================================================================
+--- openvpn-2.2.1.orig/win32.h 2011-12-13 12:23:07.000000000 +0100
++++ openvpn-2.2.1/win32.h 2011-12-13 12:24:54.654738990 +0100
+@@ -272,6 +272,8 @@
+
+ /* call self in a subprocess */
+ void fork_to_self (const char *cmdline);
++const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
++int inet_pton(int af, const char *src, void *st);
+
+ /* Find temporary directory */
+ const char *win_get_tempdir();
diff --git a/debian/patches/jjo-ipv6-support.patch b/debian/patches/jjo-ipv6-support.patch
new file mode 100644
index 0000000..c516763
--- /dev/null
+++ b/debian/patches/jjo-ipv6-support.patch
@@ -0,0 +1,4011 @@
+Description: OpenVPN over UDP6/TCP6 patch
+Author: JuanJo Ciarlante <jjo@google.com>
+URL: https://github.com/jjo/openvpn-ipv6/
+Index: openvpn-2.2.1/README.ipv6
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ openvpn-2.2.1/README.ipv6 2011-12-13 12:23:07.264081559 +0100
+@@ -0,0 +1,81 @@
++[ Last updated: 25-Mar-2011. ]
++
++OpenVPN-2.1 over UDP6/TCP6 README for ipv6-0.4.x patch releases:
++( --udp6 and --tcp6-{client,server} )
++
++* Availability
++ Source code under GPLv2 from http://github.com/jjo/openvpn-ipv6
++
++ Distro ready repos/packages:
++ o Debian sid official repo, by Alberto Gonzalez Iniesta,
++ starting from openvpn_2.1~rc20-2
++ o Gentoo official portage tree, by Marcel Pennewiss:
++ - https://bugs.gentoo.org/show_bug.cgi?id=287896
++ o Ubuntu package, by Bernhard Schmidt:
++ - https://launchpad.net/~berni/+archive/ipv6/+packages
++ o Freetz.org, milestone freetz-1.2
++ - http://trac.freetz.org/milestone/freetz-1.2
++
++* Status:
++ o OK:
++ - upd6,tcp6: GNU/Linux, win32, openbsd-4.7, freebsd-8.1
++ - udp4->upd6,tcp4->tcp6 (ipv4/6 mapped): GNU/Linux
++ (gives a warning on local!=remote proto matching)
++ o NOT:
++ - win32: tcp4->tcp6 (ipv4/6 mapped) fails w/connection refused
++ o NOT tested:
++ - mgmt console
++
++* Build setup:
++ ./configure --enable-ipv6 (by default)
++
++* Usage:
++ For IPv6 just specify "-p upd6" an proper IPv6 hostnames, adapting the example
++ from man page ...
++
++ On may:
++ openvpn --proto udp6 --remote <june_IPv6_addr> --dev tun1 \
++ --ifconfig 10.4.0.1 10.4.0.2 --verb 5 --secret key
++
++ On june:
++ openvpn --proto udp6 --remote <may_IPv6_addr> --dev tun1 \
++ --ifconfig 10.4.0.2 10.4.0.1 --verb 5 --secret key
++
++ Same for --proto tcp6-client, tcp6-server.
++
++* Main code changes summary:
++ - socket.h: New struct openvpn_sockaddr type that holds sockaddrs and pktinfo,
++ (here I omitted #ifdef USE_PF_xxxx, see socket.h )
++
++ struct openvpn_sockaddr {
++ union {
++ struct sockaddr sa;
++ struct sockaddr_in in;
++ struct sockaddr_in6 in6;
++ } addr;
++ };
++
++ struct link_socket_addr
++ {
++ struct openvpn_sockaddr local;
++ struct openvpn_sockaddr remote;
++ struct openvpn_sockaddr actual;
++ };
++
++ PRO: allows simple type overloading: local.addr.sa, local.addr.in, local.addr.in6 ... etc
++ (also local.pi.in and local.pi.in6)
++
++ - several function prototypes moved from sockaddr_in to openvpn_sockaddr
++ - several new sockaddr functions needed to "generalize" AF_xxxx operations:
++ addr_copy(), addr_zero(), ...etc
++ proto_is_udp(), proto_is_dgram(), proto_is_net()
++
++* TODO: See TODO.ipv6
++
++--
++JuanJo Ciarlante jjo () google () com ............................
++: :
++. Linux IP Aliasing author .
++. Modular algo (AES et all) support for FreeSWAN/OpenSWAN author .
++. OpenVPN over IPv6 support .
++:...... plus other scattered free software bits in the wild ...:
+Index: openvpn-2.2.1/TODO.ipv6
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ openvpn-2.2.1/TODO.ipv6 2011-12-13 12:23:07.267081520 +0100
+@@ -0,0 +1,30 @@
++[ Last updated: 11-Nov-2009. ]
++
++* All platforms:
++ o mgmt console: as currently passes straight in_addr_t bits around
++
++ o make possible to get AF from getaddrinfo() answer, ie allow openvpn to
++ use ipv4/6 if DNS returns A/AAAA without specifying protocol.
++ Hard: requires deep changes in initialization/calling logic
++
++ o use AI_PASSIVE
++
++ o the getaddr()/getaddr6() interface is not prepared for handling socktype
++ "tagging", currently I abuse the sockflags bits for getting the ai_socktype
++ downstream.
++
++ o implement comparison for mapped addesses: server in dual stack
++ listening IPv6 must permit incoming streams from allowed IPv4 peer,
++ currently you need to pass eg: --remote ffff::1.2.3.4
++
++ o do something with multi mode learn routes, for now just ignoring
++ ipv6 addresses seems the most sensible thing to do, because there's
++ no support for intra-tunnel ipv6 stuff.
++
++* win32:
++ o find out about mapped addresses, as I can't make it work
++ with bound at ::1 and connect to 127.0.0.1
++
++* N/A:
++ o this is ipv6 *endpoint* support, so don't expect "ifconfig6"-like
++ support in this patch
+Index: openvpn-2.2.1/acinclude.m4
+===================================================================
+--- openvpn-2.2.1.orig/acinclude.m4 2011-06-24 08:13:38.000000000 +0200
++++ openvpn-2.2.1/acinclude.m4 2011-12-13 12:23:07.290081232 +0100
+@@ -123,5 +123,9 @@
+ AC_DEFINE_UNQUOTED(socklen_t, $curl_cv_socklen_t_equiv,
+ [type to use in place of socklen_t if not defined])],
+ [#include <sys/types.h>
+-#include <sys/socket.h>])
++#ifdef WIN32
++#include <ws2tcpip.h>
++#else
++#include <sys/socket.h>
++#endif])
+ ])
+Index: openvpn-2.2.1/aclocal.m4
+===================================================================
+--- openvpn-2.2.1.orig/aclocal.m4 2011-07-01 11:26:36.000000000 +0200
++++ openvpn-2.2.1/aclocal.m4 2011-12-13 12:23:07.323080820 +0100
+@@ -13,8 +13,8 @@
+
+ m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+-m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.66],,
+-[m4_warning([this file was generated for autoconf 2.66.
++m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.65],,
++[m4_warning([this file was generated for autoconf 2.65.
+ You have another version of autoconf. It may work, but is not guaranteed to.
+ If you have problems, you may need to regenerate the build system entirely.
+ To do so, use the procedure documented by the package, typically `autoreconf'.])])
+Index: openvpn-2.2.1/buffer.c
+===================================================================
+--- openvpn-2.2.1.orig/buffer.c 2011-06-24 08:13:38.000000000 +0200
++++ openvpn-2.2.1/buffer.c 2011-12-13 12:23:07.326080784 +0100
+@@ -214,6 +214,23 @@
+ return ret;
+ }
+
++bool
++buf_puts(struct buffer *buf, const char *str)
++{
++ int ret = false;
++ 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);
++ ret = true;
++ }
++ return ret;
++}
++
++
+ /*
+ * This is necessary due to certain buggy implementations of snprintf,
+ * that don't guarantee null termination for size > 0.
+Index: openvpn-2.2.1/buffer.h
+===================================================================
+--- openvpn-2.2.1.orig/buffer.h 2011-06-24 08:13:38.000000000 +0200
++++ openvpn-2.2.1/buffer.h 2011-12-13 12:23:07.329080745 +0100
+@@ -277,6 +277,11 @@
+ ;
+
+ /*
++ * puts append to a buffer with overflow check
++ */
++bool buf_puts (struct buffer *buf, const char *str);
++
++/*
+ * Like snprintf but guarantees null termination for size > 0
+ */
+ int openvpn_snprintf(char *str, size_t size, const char *format, ...)
+Index: openvpn-2.2.1/config.h.in
+===================================================================
+--- openvpn-2.2.1.orig/config.h.in 2011-07-01 11:26:37.000000000 +0200
++++ openvpn-2.2.1/config.h.in 2011-12-13 12:23:07.332080708 +0100
+@@ -531,6 +531,9 @@
+ /* Use LZO compression library */
+ #undef USE_LZO
+
++/* struct sockaddr_in6 is needed for IPv6 peer support */
++#undef USE_PF_INET6
++
+ /* Enable PKCS11 capability */
+ #undef USE_PKCS11
+
+Index: openvpn-2.2.1/configure
+===================================================================
+--- openvpn-2.2.1.orig/configure 2011-07-01 11:26:37.000000000 +0200
++++ openvpn-2.2.1/configure 2011-12-13 12:23:07.347080520 +0100
+@@ -319,7 +319,7 @@
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+- } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
++ } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir"
+
+
+ } # as_fn_mkdir_p
+@@ -359,19 +359,19 @@
+ fi # as_fn_arith
+
+
+-# as_fn_error STATUS ERROR [LINENO LOG_FD]
+-# ----------------------------------------
++# as_fn_error ERROR [LINENO LOG_FD]
++# ---------------------------------
+ # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+ # provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+-# script with STATUS, using 1 if that was 0.
++# script with status $?, using 1 if that was 0.
+ as_fn_error ()
+ {
+- as_status=$1; test $as_status -eq 0 && as_status=1
+- if test "$4"; then
+- as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+- $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
++ as_status=$?; test $as_status -eq 0 && as_status=1
++ if test "$3"; then
++ as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
++ $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3
+ fi
+- $as_echo "$as_me: error: $2" >&2
++ $as_echo "$as_me: error: $1" >&2
+ as_fn_exit $as_status
+ } # as_fn_error
+
+@@ -533,7 +533,7 @@
+ exec 6>&1
+
+ # Name of the host.
+-# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
++# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+ # so uname gets run too.
+ ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+@@ -715,6 +715,7 @@
+ enable_http
+ enable_fragment
+ enable_multihome
++enable_ipv6
+ enable_port_share
+ enable_debug
+ enable_small
+@@ -858,7 +859,7 @@
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+- as_fn_error $? "invalid feature name: $ac_useropt"
++ as_fn_error "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+@@ -884,7 +885,7 @@
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+- as_fn_error $? "invalid feature name: $ac_useropt"
++ as_fn_error "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+@@ -1088,7 +1089,7 @@
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+- as_fn_error $? "invalid package name: $ac_useropt"
++ as_fn_error "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+@@ -1104,7 +1105,7 @@
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+- as_fn_error $? "invalid package name: $ac_useropt"
++ as_fn_error "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+@@ -1134,8 +1135,8 @@
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+- -*) as_fn_error $? "unrecognized option: \`$ac_option'
+-Try \`$0 --help' for more information"
++ -*) as_fn_error "unrecognized option: \`$ac_option'
++Try \`$0 --help' for more information."
+ ;;
+
+ *=*)
+@@ -1143,7 +1144,7 @@
+ # Reject names that are not valid shell variable names.
+ case $ac_envvar in #(
+ '' | [0-9]* | *[!_$as_cr_alnum]* )
+- as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
++ as_fn_error "invalid variable name: \`$ac_envvar'" ;;
+ esac
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+@@ -1161,13 +1162,13 @@
+
+ if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+- as_fn_error $? "missing argument to $ac_option"
++ as_fn_error "missing argument to $ac_option"
+ fi
+
+ if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+- fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
++ fatal) as_fn_error "unrecognized options: $ac_unrecognized_opts" ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+ fi
+@@ -1190,7 +1191,7 @@
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+- as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
++ as_fn_error "expected an absolute directory name for --$ac_var: $ac_val"
+ done
+
+ # There might be people who depend on the old broken behavior: `$host'
+@@ -1204,8 +1205,8 @@
+ if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+- $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host.
+- If a cross compiler is detected then cross compile mode will be used" >&2
++ $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
++ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+@@ -1220,9 +1221,9 @@
+ ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ ac_ls_di=`ls -di .` &&
+ ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+- as_fn_error $? "working directory cannot be determined"
++ as_fn_error "working directory cannot be determined"
+ test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+- as_fn_error $? "pwd does not report name of working directory"
++ as_fn_error "pwd does not report name of working directory"
+
+
+ # Find the source files, if location was not specified.
+@@ -1261,11 +1262,11 @@
+ fi
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+- as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
++ as_fn_error "cannot find sources ($ac_unique_file) in $srcdir"
+ fi
+ ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ ac_abs_confdir=`(
+- cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
++ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error "$ac_msg"
+ pwd)`
+ # When building in place, set srcdir=.
+ if test "$ac_abs_confdir" = "$ac_pwd"; then
+@@ -1305,7 +1306,7 @@
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+- -q, --quiet, --silent do not print \`checking ...' messages
++ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+@@ -1383,6 +1384,7 @@
+ --disable-http Disable HTTP proxy support
+ --disable-fragment Disable internal fragmentation support (--fragment)
+ --disable-multihome Disable multi-homed UDP server support (--multihome)
++ --disable-ipv6 Disable UDP/IPv6 support
+ --disable-port-share Disable TCP server port-share support (--port-share)
+ --disable-debug Disable debugging support (disable gremlin and verb 7+ messages)
+ --enable-small Enable smaller executable size (disable OCC, usage message, and verb 4 parm list)
+@@ -1588,10 +1590,10 @@
+ ac_fn_c_check_header_mongrel ()
+ {
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+- if eval "test \"\${$3+set}\"" = set; then :
++ if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+ $as_echo_n "checking for $2... " >&6; }
+-if eval "test \"\${$3+set}\"" = set; then :
++if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+ $as_echo_n "(cached) " >&6
+ fi
+ eval ac_res=\$$3
+@@ -1650,15 +1652,17 @@
+ $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+ $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+-( $as_echo "## -------------------------------------------------- ##
++( cat <<\_ASBOX
++## -------------------------------------------------- ##
+ ## Report this to openvpn-users@lists.sourceforge.net ##
+-## -------------------------------------------------- ##"
++## -------------------------------------------------- ##
++_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+ $as_echo_n "checking for $2... " >&6; }
+-if eval "test \"\${$3+set}\"" = set; then :
++if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+ $as_echo_n "(cached) " >&6
+ else
+ eval "$3=\$ac_header_compiler"
+@@ -1722,7 +1726,7 @@
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+ $as_echo_n "checking for $2... " >&6; }
+-if eval "test \"\${$3+set}\"" = set; then :
++if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+ $as_echo_n "(cached) " >&6
+ else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+@@ -1753,7 +1757,7 @@
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+ $as_echo_n "checking for $2... " >&6; }
+-if eval "test \"\${$3+set}\"" = set; then :
++if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+ $as_echo_n "(cached) " >&6
+ else
+ eval "$3=no"
+@@ -2030,7 +2034,7 @@
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+ $as_echo_n "checking for $2... " >&6; }
+-if eval "test \"\${$3+set}\"" = set; then :
++if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+ $as_echo_n "(cached) " >&6
+ else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+@@ -2203,9 +2207,11 @@
+ {
+ echo
+
+- $as_echo "## ---------------- ##
++ cat <<\_ASBOX
++## ---------------- ##
+ ## Cache variables. ##
+-## ---------------- ##"
++## ---------------- ##
++_ASBOX
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+ (
+@@ -2239,9 +2245,11 @@
+ )
+ echo
+
+- $as_echo "## ----------------- ##
++ cat <<\_ASBOX
++## ----------------- ##
+ ## Output variables. ##
+-## ----------------- ##"
++## ----------------- ##
++_ASBOX
+ echo
+ for ac_var in $ac_subst_vars
+ do
+@@ -2254,9 +2262,11 @@
+ echo
+
+ if test -n "$ac_subst_files"; then
+- $as_echo "## ------------------- ##
++ cat <<\_ASBOX
++## ------------------- ##
+ ## File substitutions. ##
+-## ------------------- ##"
++## ------------------- ##
++_ASBOX
+ echo
+ for ac_var in $ac_subst_files
+ do
+@@ -2270,9 +2280,11 @@
+ fi
+
+ if test -s confdefs.h; then
+- $as_echo "## ----------- ##
++ cat <<\_ASBOX
++## ----------- ##
+ ## confdefs.h. ##
+-## ----------- ##"
++## ----------- ##
++_ASBOX
+ echo
+ cat confdefs.h
+ echo
+@@ -2327,12 +2339,7 @@
+ ac_site_file1=NONE
+ ac_site_file2=NONE
+ if test -n "$CONFIG_SITE"; then
+- # We do not want a PATH search for config.site.
+- case $CONFIG_SITE in #((
+- -*) ac_site_file1=./$CONFIG_SITE;;
+- */*) ac_site_file1=$CONFIG_SITE;;
+- *) ac_site_file1=./$CONFIG_SITE;;
+- esac
++ ac_site_file1=$CONFIG_SITE
+ elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+@@ -2347,11 +2354,7 @@
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+ $as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+- . "$ac_site_file" \
+- || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+-as_fn_error $? "failed to load site script $ac_site_file
+-See \`config.log' for more details" "$LINENO" 5; }
++ . "$ac_site_file"
+ fi
+ done
+
+@@ -2427,7 +2430,7 @@
+ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+ $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+- as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
++ as_fn_error "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+ fi
+ ## -------------------- ##
+ ## Main body of script. ##
+@@ -2446,22 +2449,16 @@
+
+ ac_aux_dir=
+ for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+- if test -f "$ac_dir/install-sh"; then
+- ac_aux_dir=$ac_dir
+- ac_install_sh="$ac_aux_dir/install-sh -c"
+- break
+- elif test -f "$ac_dir/install.sh"; then
+- ac_aux_dir=$ac_dir
+- ac_install_sh="$ac_aux_dir/install.sh -c"
+- break
+- elif test -f "$ac_dir/shtool"; then
+- ac_aux_dir=$ac_dir
+- ac_install_sh="$ac_aux_dir/shtool install -c"
+- break
+- fi
++ for ac_t in install-sh install.sh shtool; do
++ if test -f "$ac_dir/$ac_t"; then
++ ac_aux_dir=$ac_dir
++ ac_install_sh="$ac_aux_dir/$ac_t -c"
++ break 2
++ fi
++ done
+ done
+ if test -z "$ac_aux_dir"; then
+- as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
++ as_fn_error "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+ fi
+
+ # These three variables are undocumented and unsupported,
+@@ -2475,7 +2472,7 @@
+
+ # Make sure we can run config.sub.
+ $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+- as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
++ as_fn_error "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+ $as_echo_n "checking build system type... " >&6; }
+@@ -2486,16 +2483,16 @@
+ test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+ test "x$ac_build_alias" = x &&
+- as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
++ as_fn_error "cannot guess build type; you must specify one" "$LINENO" 5
+ ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+- as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
++ as_fn_error "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+ $as_echo "$ac_cv_build" >&6; }
+ case $ac_cv_build in
+ *-*-*) ;;
+-*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
++*) as_fn_error "invalid value of canonical build" "$LINENO" 5;;
+ esac
+ build=$ac_cv_build
+ ac_save_IFS=$IFS; IFS='-'
+@@ -2520,7 +2517,7 @@
+ ac_cv_host=$ac_cv_build
+ else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+- as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
++ as_fn_error "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+ fi
+
+ fi
+@@ -2528,7 +2525,7 @@
+ $as_echo "$ac_cv_host" >&6; }
+ case $ac_cv_host in
+ *-*-*) ;;
+-*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
++*) as_fn_error "invalid value of canonical host" "$LINENO" 5;;
+ esac
+ host=$ac_cv_host
+ ac_save_IFS=$IFS; IFS='-'
+@@ -2650,11 +2647,11 @@
+ '
+ case `pwd` in
+ *[\\\"\#\$\&\'\`$am_lf]*)
+- as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
++ as_fn_error "unsafe absolute working directory name" "$LINENO" 5;;
+ esac
+ case $srcdir in
+ *[\\\"\#\$\&\'\`$am_lf\ \ ]*)
+- as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;;
++ as_fn_error "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;;
+ esac
+
+ # Do `set' in a subshell so we don't clobber the current shell's
+@@ -2676,7 +2673,7 @@
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+- as_fn_error $? "ls -t appears to fail. Make sure there is not a broken
++ as_fn_error "ls -t appears to fail. Make sure there is not a broken
+ alias in your environment" "$LINENO" 5
+ fi
+
+@@ -2686,7 +2683,7 @@
+ # Ok.
+ :
+ else
+- as_fn_error $? "newly created file is older than distributed files!
++ as_fn_error "newly created file is older than distributed files!
+ Check your system clock" "$LINENO" 5
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+@@ -2924,7 +2921,7 @@
+ $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+ set x ${MAKE-make}
+ ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+-if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\"" = set; then :
++if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then :
+ $as_echo_n "(cached) " >&6
+ else
+ cat >conftest.make <<\_ACEOF
+@@ -2932,7 +2929,7 @@
+ all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+ _ACEOF
+-# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
++# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+ case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+@@ -2966,7 +2963,7 @@
+ am__isrc=' -I$(srcdir)'
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+- as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
++ as_fn_error "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
+ fi
+ fi
+
+@@ -3184,6 +3181,15 @@
+ fi
+
+
++# Check whether --enable-ipv6 was given.
++if test "${enable_ipv6+set}" = set; then :
++ enableval=$enable_ipv6; PF_INET6="$enableval"
++else
++ PF_INET6="yes"
++
++fi
++
++
+ # Check whether --enable-port-share was given.
+ if test "${enable_port_share+set}" = set; then :
+ enableval=$enable_port_share; PORT_SHARE="$enableval"
+@@ -3954,8 +3960,8 @@
+
+ test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+-as_fn_error $? "no acceptable C compiler found in \$PATH
+-See \`config.log' for more details" "$LINENO" 5; }
++as_fn_error "no acceptable C compiler found in \$PATH
++See \`config.log' for more details." "$LINENO" 5; }
+
+ # Provide some information about the compiler.
+ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+@@ -4069,8 +4075,9 @@
+
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+-as_fn_error 77 "C compiler cannot create executables
+-See \`config.log' for more details" "$LINENO" 5; }
++{ as_fn_set_status 77
++as_fn_error "C compiler cannot create executables
++See \`config.log' for more details." "$LINENO" 5; }; }
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+ $as_echo "yes" >&6; }
+@@ -4112,8 +4119,8 @@
+ else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+-as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+-See \`config.log' for more details" "$LINENO" 5; }
++as_fn_error "cannot compute suffix of executables: cannot compile and link
++See \`config.log' for more details." "$LINENO" 5; }
+ fi
+ rm -f conftest conftest$ac_cv_exeext
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+@@ -4170,9 +4177,9 @@
+ else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+-as_fn_error $? "cannot run C compiled programs.
++as_fn_error "cannot run C compiled programs.
+ If you meant to cross compile, use \`--host'.
+-See \`config.log' for more details" "$LINENO" 5; }
++See \`config.log' for more details." "$LINENO" 5; }
+ fi
+ fi
+ fi
+@@ -4223,8 +4230,8 @@
+
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+-as_fn_error $? "cannot compute suffix of object files: cannot compile
+-See \`config.log' for more details" "$LINENO" 5; }
++as_fn_error "cannot compute suffix of object files: cannot compile
++See \`config.log' for more details." "$LINENO" 5; }
+ fi
+ rm -f conftest.$ac_cv_objext conftest.$ac_ext
+ fi
+@@ -4762,8 +4769,8 @@
+ else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+-as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+-See \`config.log' for more details" "$LINENO" 5; }
++as_fn_error "C preprocessor \"$CPP\" fails sanity check
++See \`config.log' for more details." "$LINENO" 5; }
+ fi
+
+ ac_ext=c
+@@ -4824,7 +4831,7 @@
+ done
+ IFS=$as_save_IFS
+ if test -z "$ac_cv_path_GREP"; then
+- as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
++ as_fn_error "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+ else
+ ac_cv_path_GREP=$GREP
+@@ -4890,7 +4897,7 @@
+ done
+ IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP"; then
+- as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
++ as_fn_error "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+ else
+ ac_cv_path_EGREP=$EGREP
+@@ -5064,7 +5071,8 @@
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+ "
+-if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
++eval as_val=\$$as_ac_Header
++ if test "x$as_val" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+ #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+ _ACEOF
+@@ -5181,7 +5189,7 @@
+ test -n "$MAN2HTML" && break
+ done
+
+- test -z "${MAN2HTML}" && as_fn_error $? "man2html is required for win32" "$LINENO" 5
++ test -z "${MAN2HTML}" && as_fn_error "man2html is required for win32" "$LINENO" 5
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+@@ -5518,7 +5526,11 @@
+
+
+ ac_fn_c_check_type "$LINENO" "socklen_t" "ac_cv_type_socklen_t" "#include <sys/types.h>
++#ifdef WIN32
++#include <ws2tcpip.h>
++#else
+ #include <sys/socket.h>
++#endif
+ "
+ if test "x$ac_cv_type_socklen_t" = x""yes; then :
+
+@@ -5570,7 +5582,7 @@
+ esac
+
+ if test "x$curl_cv_socklen_t_equiv" = x; then
+- as_fn_error $? "Cannot find a type to use in place of socklen_t" "$LINENO" 5
++ as_fn_error "Cannot find a type to use in place of socklen_t" "$LINENO" 5
+ fi
+
+ fi
+@@ -5711,7 +5723,7 @@
+
+ else
+
+- as_fn_error $? "C compiler is unable to creaty empty arrays" "$LINENO" 5
++ as_fn_error "C compiler is unable to creaty empty arrays" "$LINENO" 5
+
+ fi
+ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+@@ -5725,7 +5737,8 @@
+ do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+-if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
++eval as_val=\$$as_ac_Header
++ if test "x$as_val" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+ #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+ _ACEOF
+@@ -5781,7 +5794,8 @@
+ do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+-if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
++eval as_val=\$$as_ac_Header
++ if test "x$as_val" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+ #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+ _ACEOF
+@@ -6077,8 +6091,9 @@
+ if test "$ac_cv_type_unsigned_int" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+-as_fn_error 77 "cannot compute sizeof (unsigned int)
+-See \`config.log' for more details" "$LINENO" 5; }
++{ as_fn_set_status 77
++as_fn_error "cannot compute sizeof (unsigned int)
++See \`config.log' for more details." "$LINENO" 5; }; }
+ else
+ ac_cv_sizeof_unsigned_int=0
+ fi
+@@ -6110,8 +6125,9 @@
+ if test "$ac_cv_type_unsigned_long" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+-as_fn_error 77 "cannot compute sizeof (unsigned long)
+-See \`config.log' for more details" "$LINENO" 5; }
++{ as_fn_set_status 77
++as_fn_error "cannot compute sizeof (unsigned long)
++See \`config.log' for more details." "$LINENO" 5; }; }
+ else
+ ac_cv_sizeof_unsigned_long=0
+ fi
+@@ -6208,13 +6224,14 @@
+ do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+-if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
++eval as_val=\$$as_ac_var
++ if test "x$as_val" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+ #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+ _ACEOF
+
+ else
+- as_fn_error $? "Required library function not found" "$LINENO" 5
++ as_fn_error "Required library function not found" "$LINENO" 5
+ fi
+ done
+
+@@ -6222,7 +6239,8 @@
+ do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+-if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
++eval as_val=\$$as_ac_var
++ if test "x$as_val" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+ #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+ _ACEOF
+@@ -6513,7 +6531,8 @@
+ do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+-if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
++eval as_val=\$$as_ac_var
++ if test "x$as_val" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+ #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+ _ACEOF
+@@ -6727,13 +6746,14 @@
+ do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+-if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
++eval as_val=\$$as_ac_var
++ if test "x$as_val" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+ #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+ _ACEOF
+
+ else
+- as_fn_error $? "Required library function not found" "$LINENO" 5
++ as_fn_error "Required library function not found" "$LINENO" 5
+ fi
+ done
+
+@@ -6741,7 +6761,8 @@
+ do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+-if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
++eval as_val=\$$as_ac_var
++ if test "x$as_val" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+ #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+ _ACEOF
+@@ -6861,6 +6882,19 @@
+
+ LDFLAGS="$OLDLDFLAGS"
+
++if test "$PF_INET6" = "yes"; then
++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct sockaddr_in6 for IPv6 support..." >&5
++$as_echo "$as_me: checking for struct sockaddr_in6 for IPv6 support..." >&6;}
++ ac_fn_c_check_type "$LINENO" "struct sockaddr_in6" "ac_cv_type_struct_sockaddr_in6" "#include \"syshead.h\"
++"
++if test "x$ac_cv_type_struct_sockaddr_in6" = x""yes; then :
++
++$as_echo "#define USE_PF_INET6 1" >>confdefs.h
++
++fi
++
++fi
++
+
+ if test "$MEMCHECK" = "valgrind"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for valgrind tool and Header files..." >&5
+@@ -6874,7 +6908,7 @@
+ CFLAGS="-g -fno-inline"
+
+ else
+- as_fn_error $? "valgrind headers not found." "$LINENO" 5
++ as_fn_error "valgrind headers not found." "$LINENO" 5
+
+ fi
+
+@@ -6933,12 +6967,12 @@
+
+
+ else
+- as_fn_error $? "dmalloc library not found." "$LINENO" 5
++ as_fn_error "dmalloc library not found." "$LINENO" 5
+
+ fi
+
+ else
+- as_fn_error $? "dmalloc headers not found." "$LINENO" 5
++ as_fn_error "dmalloc headers not found." "$LINENO" 5
+
+ fi
+
+@@ -7093,7 +7127,7 @@
+ as_ac_Lib=`$as_echo "ac_cv_lib_$i''_lzo1x_1_15_compress" | $as_tr_sh`
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lzo1x_1_15_compress in -l$i" >&5
+ $as_echo_n "checking for lzo1x_1_15_compress in -l$i... " >&6; }
+-if eval "test \"\${$as_ac_Lib+set}\"" = set; then :
++if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then :
+ $as_echo_n "(cached) " >&6
+ else
+ ac_check_lib_save_LIBS=$LIBS
+@@ -7128,7 +7162,8 @@
+ eval ac_res=\$$as_ac_Lib
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+ $as_echo "$ac_res" >&6; }
+-if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
++eval as_val=\$$as_ac_Lib
++ if test "x$as_val" = x""yes; then :
+
+
+ LIBS="-l$i $LIBS"
+@@ -7148,14 +7183,14 @@
+
+ done
+ if test $havelzolib = 0 ; then
+- as_fn_error $? "LZO headers were found but LZO library was not found" "$LINENO" 5
++ as_fn_error "LZO headers were found but LZO library was not found" "$LINENO" 5
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: LZO headers were not found" >&5
+ $as_echo "LZO headers were not found" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: LZO library available from http://www.oberhumer.com/opensource/lzo/" >&5
+ $as_echo "LZO library available from http://www.oberhumer.com/opensource/lzo/" >&6; }
+- as_fn_error $? "Or try ./configure --disable-lzo" "$LINENO" 5
++ as_fn_error "Or try ./configure --disable-lzo" "$LINENO" 5
+ fi
+ fi
+
+@@ -7167,7 +7202,7 @@
+ if test "x$ac_cv_header_openssl_evp_h" = x""yes; then :
+
+ else
+- as_fn_error $? "OpenSSL Crypto headers not found." "$LINENO" 5
++ as_fn_error "OpenSSL Crypto headers not found." "$LINENO" 5
+ fi
+
+
+@@ -7176,7 +7211,7 @@
+ as_ac_Lib=`$as_echo "ac_cv_lib_$lib''_EVP_CIPHER_CTX_init" | $as_tr_sh`
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for EVP_CIPHER_CTX_init in -l$lib" >&5
+ $as_echo_n "checking for EVP_CIPHER_CTX_init in -l$lib... " >&6; }
+-if eval "test \"\${$as_ac_Lib+set}\"" = set; then :
++if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then :
+ $as_echo_n "(cached) " >&6
+ else
+ ac_check_lib_save_LIBS=$LIBS
+@@ -7211,7 +7246,8 @@
+ eval ac_res=\$$as_ac_Lib
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+ $as_echo "$ac_res" >&6; }
+-if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
++eval as_val=\$$as_ac_Lib
++ if test "x$as_val" = x""yes; then :
+
+ cryptofound=1
+
+@@ -7223,7 +7259,7 @@
+
+ done
+
+- test -n "$cryptofound" || as_fn_error $? "OpenSSL Crypto library not found." "$LINENO" 5
++ test -n "$cryptofound" || as_fn_error "OpenSSL Crypto library not found." "$LINENO" 5
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking that OpenSSL Library is at least version 0.9.6" >&5
+ $as_echo_n "checking that OpenSSL Library is at least version 0.9.6... " >&6; }
+@@ -7303,7 +7339,7 @@
+
+
+ else
+- as_fn_error $? "OpenSSL crypto Library is too old." "$LINENO" 5
++ as_fn_error "OpenSSL crypto Library is too old." "$LINENO" 5
+
+ fi
+ rm -f conftest*
+@@ -7317,7 +7353,7 @@
+ if test "x$ac_cv_header_openssl_ssl_h" = x""yes; then :
+
+ else
+- as_fn_error $? "OpenSSL SSL headers not found." "$LINENO" 5
++ as_fn_error "OpenSSL SSL headers not found." "$LINENO" 5
+
+ fi
+
+@@ -7327,7 +7363,7 @@
+ as_ac_Lib=`$as_echo "ac_cv_lib_$lib''_SSL_CTX_new" | $as_tr_sh`
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_CTX_new in -l$lib" >&5
+ $as_echo_n "checking for SSL_CTX_new in -l$lib... " >&6; }
+-if eval "test \"\${$as_ac_Lib+set}\"" = set; then :
++if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then :
+ $as_echo_n "(cached) " >&6
+ else
+ ac_check_lib_save_LIBS=$LIBS
+@@ -7362,7 +7398,8 @@
+ eval ac_res=\$$as_ac_Lib
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+ $as_echo "$ac_res" >&6; }
+-if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
++eval as_val=\$$as_ac_Lib
++ if test "x$as_val" = x""yes; then :
+
+ sslfound=1
+
+@@ -7374,7 +7411,7 @@
+
+ done
+
+- test -n "${sslfound}" || as_fn_error $? "OpenSSL SSL library not found." "$LINENO" 5
++ test -n "${sslfound}" || as_fn_error "OpenSSL SSL library not found." "$LINENO" 5
+
+ if test "$MEMCHECK" = "ssl"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Memory Debugging Capabilities in OpenSSL Library..." >&5
+@@ -7424,7 +7461,7 @@
+ $as_echo "NOTE: OpenSSL library must be compiled with CRYPTO_MDEBUG" >&6; }
+
+ else
+- as_fn_error $? "Memory Debugging function in OpenSSL library not found." "$LINENO" 5
++ as_fn_error "Memory Debugging function in OpenSSL library not found." "$LINENO" 5
+
+ fi
+
+@@ -7799,7 +7836,6 @@
+
+ ac_libobjs=
+ ac_ltlibobjs=
+-U=
+ for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+@@ -7823,15 +7859,15 @@
+ fi
+
+ if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+- as_fn_error $? "conditional \"AMDEP\" was never defined.
++ as_fn_error "conditional \"AMDEP\" was never defined.
+ Usually this means the macro was only invoked conditionally." "$LINENO" 5
+ fi
+ if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+- as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
++ as_fn_error "conditional \"am__fastdepCC\" was never defined.
+ Usually this means the macro was only invoked conditionally." "$LINENO" 5
+ fi
+ if test -z "${WIN32_TRUE}" && test -z "${WIN32_FALSE}"; then
+- as_fn_error $? "conditional \"WIN32\" was never defined.
++ as_fn_error "conditional \"WIN32\" was never defined.
+ Usually this means the macro was only invoked conditionally." "$LINENO" 5
+ fi
+
+@@ -7981,19 +8017,19 @@
+ (unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+-# as_fn_error STATUS ERROR [LINENO LOG_FD]
+-# ----------------------------------------
++# as_fn_error ERROR [LINENO LOG_FD]
++# ---------------------------------
+ # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+ # provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+-# script with STATUS, using 1 if that was 0.
++# script with status $?, using 1 if that was 0.
+ as_fn_error ()
+ {
+- as_status=$1; test $as_status -eq 0 && as_status=1
+- if test "$4"; then
+- as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+- $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
++ as_status=$?; test $as_status -eq 0 && as_status=1
++ if test "$3"; then
++ as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
++ $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3
+ fi
+- $as_echo "$as_me: error: $2" >&2
++ $as_echo "$as_me: error: $1" >&2
+ as_fn_exit $as_status
+ } # as_fn_error
+
+@@ -8189,7 +8225,7 @@
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+- } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
++ } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir"
+
+
+ } # as_fn_mkdir_p
+@@ -8368,7 +8404,7 @@
+ ac_need_defaults=false;;
+ --he | --h)
+ # Conflict between --help and --header
+- as_fn_error $? "ambiguous option: \`$1'
++ as_fn_error "ambiguous option: \`$1'
+ Try \`$0 --help' for more information.";;
+ --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+@@ -8377,7 +8413,7 @@
+ ac_cs_silent=: ;;
+
+ # This is an error.
+- -*) as_fn_error $? "unrecognized option: \`$1'
++ -*) as_fn_error "unrecognized option: \`$1'
+ Try \`$0 --help' for more information." ;;
+
+ *) as_fn_append ac_config_targets " $1"
+@@ -8441,7 +8477,7 @@
+ "install-win32/Makefile") CONFIG_FILES="$CONFIG_FILES install-win32/Makefile" ;;
+ "install-win32/settings") CONFIG_FILES="$CONFIG_FILES install-win32/settings" ;;
+
+- *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
++ *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ esac
+ done
+
+@@ -8479,7 +8515,7 @@
+ {
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+-} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
++} || as_fn_error "cannot create a temporary directory in ." "$LINENO" 5
+
+ # Set up the scripts for CONFIG_FILES section.
+ # No need to generate them if there are no CONFIG_FILES.
+@@ -8496,7 +8532,7 @@
+ fi
+ ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+ if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+- ac_cs_awk_cr='\\r'
++ ac_cs_awk_cr='\r'
+ else
+ ac_cs_awk_cr=$ac_cr
+ fi
+@@ -8510,18 +8546,18 @@
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+ } >conf$$subs.sh ||
+- as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+-ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
++ as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5
++ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'`
+ ac_delim='%!_!# '
+ for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+- as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
++ as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+- as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
++ as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+@@ -8610,28 +8646,20 @@
+ else
+ cat
+ fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \
+- || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
++ || as_fn_error "could not setup config files machinery" "$LINENO" 5
+ _ACEOF
+
+-# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+-# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
++# VPATH may cause trouble with some makes, so we remove $(srcdir),
++# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+ # trailing colons and then remove the whole line if VPATH becomes empty
+ # (actually we leave an empty line to preserve line numbers).
+ if test "x$srcdir" = x.; then
+- ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+-h
+-s///
+-s/^/:/
+-s/[ ]*$/:/
+-s/:\$(srcdir):/:/g
+-s/:\${srcdir}:/:/g
+-s/:@srcdir@:/:/g
+-s/^:*//
++ ac_vpsub='/^[ ]*VPATH[ ]*=/{
++s/:*\$(srcdir):*/:/
++s/:*\${srcdir}:*/:/
++s/:*@srcdir@:*/:/
++s/^\([^=]*=[ ]*\):*/\1/
+ s/:*$//
+-x
+-s/\(=[ ]*\).*/\1/
+-G
+-s/\n//
+ s/^[^=]*=[ ]*$//
+ }'
+ fi
+@@ -8659,7 +8687,7 @@
+ if test -z "$ac_t"; then
+ break
+ elif $ac_last_try; then
+- as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
++ as_fn_error "could not make $CONFIG_HEADERS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+@@ -8744,7 +8772,7 @@
+ _ACAWK
+ _ACEOF
+ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+- as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
++ as_fn_error "could not setup config headers machinery" "$LINENO" 5
+ fi # test -n "$CONFIG_HEADERS"
+
+
+@@ -8757,7 +8785,7 @@
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+- :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
++ :L* | :C*:*) as_fn_error "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+@@ -8785,7 +8813,7 @@
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+- as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
++ as_fn_error "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ as_fn_append ac_file_inputs " '$ac_f'"
+@@ -8812,7 +8840,7 @@
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$tmp/stdin" \
+- || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
++ || as_fn_error "could not create $ac_file" "$LINENO" 5 ;;
+ esac
+ ;;
+ esac
+@@ -8949,22 +8977,22 @@
+ $ac_datarootdir_hack
+ "
+ eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \
+- || as_fn_error $? "could not create $ac_file" "$LINENO" 5
++ || as_fn_error "could not create $ac_file" "$LINENO" 5
+
+ test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+-which seems to be undefined. Please make sure it is defined" >&5
++which seems to be undefined. Please make sure it is defined." >&5
+ $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+-which seems to be undefined. Please make sure it is defined" >&2;}
++which seems to be undefined. Please make sure it is defined." >&2;}
+
+ rm -f "$tmp/stdin"
+ case $ac_file in
+ -) cat "$tmp/out" && rm -f "$tmp/out";;
+ *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";;
+ esac \
+- || as_fn_error $? "could not create $ac_file" "$LINENO" 5
++ || as_fn_error "could not create $ac_file" "$LINENO" 5
+ ;;
+ :H)
+ #
+@@ -8975,19 +9003,19 @@
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs"
+ } >"$tmp/config.h" \
+- || as_fn_error $? "could not create $ac_file" "$LINENO" 5
++ || as_fn_error "could not create $ac_file" "$LINENO" 5
+ if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+ $as_echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ rm -f "$ac_file"
+ mv "$tmp/config.h" "$ac_file" \
+- || as_fn_error $? "could not create $ac_file" "$LINENO" 5
++ || as_fn_error "could not create $ac_file" "$LINENO" 5
+ fi
+ else
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \
+- || as_fn_error $? "could not create -" "$LINENO" 5
++ || as_fn_error "could not create -" "$LINENO" 5
+ fi
+ # Compute "$ac_file"'s index in $config_headers.
+ _am_arg="$ac_file"
+@@ -9138,7 +9166,7 @@
+ ac_clean_files=$ac_clean_files_save
+
+ test $ac_write_fail = 0 ||
+- as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
++ as_fn_error "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+ # configure is writing to config.log, and then calls config.status.
+@@ -9159,7 +9187,7 @@
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+- $ac_cs_success || as_fn_exit 1
++ $ac_cs_success || as_fn_exit $?
+ fi
+ if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+Index: openvpn-2.2.1/configure.ac
+===================================================================
+--- openvpn-2.2.1.orig/configure.ac 2011-06-24 08:13:38.000000000 +0200
++++ openvpn-2.2.1/configure.ac 2011-12-13 12:23:07.348080508 +0100
+@@ -146,6 +146,12 @@
+ [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"],
+@@ -566,6 +572,16 @@
+ AC_CHECK_FUNC(epoll_create, AC_DEFINE(HAVE_EPOLL_CREATE, 1, [epoll_create function is defined]))
+ 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
+Index: openvpn-2.2.1/init.c
+===================================================================
+--- openvpn-2.2.1.orig/init.c 2011-06-24 08:13:38.000000000 +0200
++++ openvpn-2.2.1/init.c 2011-12-13 12:23:07.351080471 +0100
+@@ -96,7 +96,7 @@
+ */
+ if (options->pull
+ && options->ping_rec_timeout_action == PING_UNDEF
+- && options->ce.proto == PROTO_UDPv4)
++ && proto_is_dgram(options->ce.proto))
+ {
+ options->ping_rec_timeout = PRE_PULL_INITIAL_PING_RESTART;
+ options->ping_rec_timeout_action = PING_RESTART;
+@@ -1150,7 +1150,12 @@
+ 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);
++ /* TODO(jjo): for ipv6 this will convert some 32bits in the ipv6 addr
++ * to a meaningless ipv4 address.
++ * In any case, is somewhat inconsistent to send local tunnel
++ * addr with remote _endpoint_ 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,
+@@ -1566,7 +1571,7 @@
+ #ifdef ENABLE_OCC
+ if (found & OPT_P_EXPLICIT_NOTIFY)
+ {
+- if (c->options.ce.proto != PROTO_UDPv4 && c->options.explicit_exit_notification)
++ if (!proto_is_udp(c->options.ce.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;
+@@ -1661,13 +1666,22 @@
+ switch (c->options.ce.proto)
+ {
+ case PROTO_UDPv4:
++#ifdef USE_PF_INET6
++ case PROTO_UDPv6:
++#endif
+ if (proxy)
+ sec = c->options.ce.connect_retry_seconds;
+ break;
+ case PROTO_TCPv4_SERVER:
++#ifdef USE_PF_INET6
++ case PROTO_TCPv6_SERVER:
++#endif
+ sec = 1;
+ break;
+ case PROTO_TCPv4_CLIENT:
++#ifdef USE_PF_INET6
++ case PROTO_TCPv6_CLIENT:
++#endif
+ sec = c->options.ce.connect_retry_seconds;
+ break;
+ }
+@@ -2807,7 +2821,7 @@
+ #ifdef WIN32
+ msg (M_INFO, "NOTE: --fast-io is disabled since we are running on Windows");
+ #else
+- if (c->options.ce.proto != PROTO_UDPv4)
++ if (!proto_is_udp(c->options.ce.proto))
+ msg (M_INFO, "NOTE: --fast-io is disabled since we are not using UDP");
+ else
+ {
+@@ -3083,7 +3097,11 @@
+ /* link_socket_mode allows CM_CHILD_TCP
+ instances to inherit acceptable fds
+ from a top-level parent */
+- if (c->options.ce.proto == PROTO_TCPv4_SERVER)
++ if (c->options.ce.proto == PROTO_TCPv4_SERVER
++#ifdef USE_PF_INET6
++ || c->options.ce.proto == PROTO_TCPv6_SERVER
++#endif
++ )
+ {
+ if (c->mode == CM_TOP)
+ link_socket_mode = LS_MODE_TCP_LISTEN;
+@@ -3358,17 +3376,8 @@
+ {
+ CLEAR (*dest);
+
+- switch (src->options.ce.proto)
+- {
+- case PROTO_UDPv4:
+- dest->mode = CM_CHILD_UDP;
+- break;
+- case PROTO_TCPv4_SERVER:
+- dest->mode = CM_CHILD_TCP;
+- break;
+- default:
+- ASSERT (0);
+- }
++ /* proto_is_dgram will ASSERT(0) if proto is invalid */
++ dest->mode = proto_is_dgram(src->options.ce.proto)? CM_CHILD_UDP : CM_CHILD_TCP;
+
+ dest->gc = gc_new ();
+
+@@ -3474,7 +3483,7 @@
+ dest->c2.es_owned = false;
+
+ dest->c2.event_set = NULL;
+- if (src->options.ce.proto == PROTO_UDPv4)
++ if (proto_is_dgram(src->options.ce.proto))
+ do_event_set_init (dest, false);
+ }
+
+Index: openvpn-2.2.1/manage.c
+===================================================================
+--- openvpn-2.2.1.orig/manage.c 2011-06-24 08:13:38.000000000 +0200
++++ openvpn-2.2.1/manage.c 2011-12-13 12:23:07.354080432 +0100
+@@ -1957,9 +1957,9 @@
+ /*
+ * 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
+@@ -1971,7 +1971,7 @@
+ }
+ 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);
+ }
+ }
+@@ -2427,7 +2427,7 @@
+ && 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);
+ }
+
+Index: openvpn-2.2.1/mroute.c
+===================================================================
+--- openvpn-2.2.1.orig/mroute.c 2011-06-24 08:13:38.000000000 +0200
++++ openvpn-2.2.1/mroute.c 2011-12-13 12:23:07.354080432 +0100
+@@ -226,25 +226,47 @@
+ 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;
+ }
+
+Index: openvpn-2.2.1/mtcp.c
+===================================================================
+--- openvpn-2.2.1.orig/mtcp.c 2011-06-24 08:13:38.000000000 +0200
++++ openvpn-2.2.1/mtcp.c 2011-12-13 12:23:07.358080384 +0100
+@@ -150,6 +150,11 @@
+ 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
++#ifdef USE_PF_INET6
++ || mi->context.c2.link_socket->info.lsa->actual.dest.addr.sa.sa_family == AF_INET6
++#endif
++ );
+ 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");
+Index: openvpn-2.2.1/multi.c
+===================================================================
+--- openvpn-2.2.1.orig/multi.c 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/multi.c 2011-12-13 12:23:07.359080371 +0100
+@@ -1058,8 +1058,8 @@
+ 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)
+@@ -2496,9 +2496,9 @@
+ 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);
+@@ -2675,16 +2675,24 @@
+ {
+ ASSERT (top->options.mode == MODE_SERVER);
+
+- switch (top->options.ce.proto) {
+- case PROTO_UDPv4:
+- tunnel_server_udp (top);
+- break;
+- case PROTO_TCPv4_SERVER:
+- tunnel_server_tcp (top);
+- break;
+- default:
+- ASSERT (0);
+- }
++#ifdef USE_PF_INET6
++ if (proto_is_dgram(top->options.ce.proto))
++ tunnel_server_udp(top);
++ else
++ tunnel_server_tcp(top);
++#else
++ switch (top->options.ce.proto)
++ {
++ case PROTO_UDPv4:
++ tunnel_server_udp (top);
++ break;
++ case PROTO_TCPv4_SERVER:
++ tunnel_server_tcp (top);
++ break;
++ default:
++ ASSERT (0);
++ }
++#endif
+ }
+
+ #else
+Index: openvpn-2.2.1/occ.c
+===================================================================
+--- openvpn-2.2.1.orig/occ.c 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/occ.c 2011-12-13 12:23:07.362080332 +0100
+@@ -369,7 +369,7 @@
+ c->c2.max_send_size_remote,
+ c->c2.max_recv_size_local);
+ if (!c->options.fragment
+- && c->options.ce.proto == PROTO_UDPv4
++ && (proto_is_dgram(c->options.ce.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))
+Index: openvpn-2.2.1/openvpn.8
+===================================================================
+--- openvpn-2.2.1.orig/openvpn.8 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/openvpn.8 2011-12-13 12:23:07.367080271 +0100
+@@ -5463,13 +5463,16 @@
+ script execution.
+ .\"*********************************************************
+ .TP
+-.B trusted_ip
++.B trusted_ip (or trusted_ip6)
+ Actual IP address of connecting client or peer which has been authenticated.
+ Set prior to execution of
+ .B \-\-ipchange, \-\-client-connect,
+ and
+ .B \-\-client-disconnect
+ scripts.
++If using ipv6 endpoints (udp6, tcp6),
++.B trusted_ip6
++will be set instead.
+ .\"*********************************************************
+ .TP
+ .B trusted_port
+@@ -5481,7 +5484,7 @@
+ scripts.
+ .\"*********************************************************
+ .TP
+-.B untrusted_ip
++.B untrusted_ip (or untrusted_ip6)
+ Actual IP address of connecting client or peer which has not been authenticated
+ yet. Sometimes used to
+ .B nmap
+@@ -5493,6 +5496,9 @@
+ and
+ .B \-\-auth-user-pass-verify
+ scripts.
++If using ipv6 endpoints (udp6, tcp6),
++.B untrusted_ip6
++will be set instead.
+ .\"*********************************************************
+ .TP
+ .B untrusted_port
+Index: openvpn-2.2.1/options.c
+===================================================================
+--- openvpn-2.2.1.orig/options.c 2011-12-13 12:22:25.000000000 +0100
++++ openvpn-2.2.1/options.c 2011-12-13 12:23:07.374080184 +0100
+@@ -79,6 +79,12 @@
+ #ifdef ENABLE_EUREPHIA
+ " [eurephia]"
+ #endif
++#if ENABLE_IP_PKTINFO
++ " [MH]"
++#endif
++#ifdef USE_PF_INET6
++ " [PF_INET6]"
++#endif
+ " built on " __DATE__
+ ;
+
+@@ -101,6 +107,9 @@
+ "--proto p : Use protocol p for communicating with peer.\n"
+ " p = udp (default), tcp-server, or tcp-client\n"
+ "--proto-force p : only consider protocol p in list of connection profiles.\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"
+@@ -1707,11 +1716,27 @@
+ * Sanity check on TCP mode options
+ */
+
+- if (ce->connect_retry_defined && ce->proto != PROTO_TCPv4_CLIENT)
+- msg (M_USAGE, "--connect-retry doesn't make sense unless also used with --proto tcp-client");
+-
+- if (ce->connect_timeout_defined && ce->proto != PROTO_TCPv4_CLIENT)
+- msg (M_USAGE, "--connect-timeout doesn't make sense unless also used with --proto tcp-client");
++ if (ce->connect_retry_defined && ce->proto != PROTO_TCPv4_CLIENT
++#ifdef USE_PF_INET6
++ && ce->proto != PROTO_TCPv6_CLIENT
++#endif
++ )
++ msg (M_USAGE, "--connect-retry doesn't make sense unless also used with --proto tcp-client"
++#ifdef USE_PF_INET6
++ " or tcp6-client"
++#endif
++ );
++
++ if (ce->connect_timeout_defined && ce->proto != PROTO_TCPv4_CLIENT
++#ifdef USE_PF_INET6
++ && ce->proto != PROTO_TCPv6_CLIENT
++#endif
++ )
++ msg (M_USAGE, "--connect-timeout doesn't make sense unless also used with --proto tcp-client"
++#ifdef USE_PF_INET6
++ " or tcp6-client"
++#endif
++ );
+
+ /*
+ * Sanity check on MTU parameters
+@@ -1720,7 +1745,7 @@
+ 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 (ce->proto != PROTO_UDPv4 && options->mtu_test)
++ if (!proto_is_udp(ce->proto) && options->mtu_test)
+ msg (M_USAGE, "--mtu-test only makes sense with --proto udp");
+ #endif
+
+@@ -1733,7 +1758,8 @@
+ * Sanity check on --local, --remote, and --ifconfig
+ */
+
+- if (string_defined_equal (ce->local, ce->remote)
++ if (proto_is_net(ce->proto)
++ && string_defined_equal (ce->local, ce->remote)
+ && ce->local_port == ce->remote_port)
+ msg (M_USAGE, "--remote and --local addresses are the same");
+
+@@ -1798,16 +1824,20 @@
+ */
+
+ #ifdef ENABLE_FRAGMENT
+- if (ce->proto != PROTO_UDPv4 && options->fragment)
++ if (!proto_is_udp(ce->proto) && options->fragment)
+ msg (M_USAGE, "--fragment can only be used with --proto udp");
+ #endif
+
+ #ifdef ENABLE_OCC
+- if (ce->proto != PROTO_UDPv4 && options->explicit_exit_notification)
++ if (!proto_is_udp(ce->proto) && options->explicit_exit_notification)
+ msg (M_USAGE, "--explicit-exit-notify can only be used with --proto udp");
+ #endif
+
+- if (!ce->remote && ce->proto == PROTO_TCPv4_CLIENT)
++ if (!ce->remote && (ce->proto == PROTO_TCPv4_CLIENT
++#ifdef USE_PF_INET6
++ || ce->proto == PROTO_TCPv6_CLIENT
++#endif
++ ))
+ msg (M_USAGE, "--remote MUST be used in TCP Client mode");
+
+ #ifdef ENABLE_HTTP_PROXY
+@@ -1825,7 +1855,12 @@
+ msg (M_USAGE, "--socks-proxy can not be used in TCP Server mode");
+ #endif
+
+- if (ce->proto == PROTO_TCPv4_SERVER && connection_list_defined (options))
++ if ((ce->proto == PROTO_TCPv4_SERVER
++#ifdef USE_PF_INET6
++ || ce->proto == PROTO_TCPv6_SERVER
++#endif
++ )
++ && connection_list_defined (options))
+ msg (M_USAGE, "TCP server mode allows at most one --remote address");
+
+ #if P2MP_SERVER
+@@ -1839,11 +1874,28 @@
+ 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 (!(ce->proto == PROTO_UDPv4 || ce->proto == PROTO_TCPv4_SERVER))
+- msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server");
++ if (!(proto_is_udp(ce->proto) || ce->proto == PROTO_TCPv4_SERVER
++#ifdef USE_PF_INET6
++ || ce->proto == PROTO_TCPv6_SERVER
++#endif
++ ))
++ msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server"
++#ifdef USE_PF_INET6
++ " or proto tcp6-server"
++#endif
++ );
+ #if PORT_SHARE
+- if ((options->port_share_host || options->port_share_port) && ce->proto != PROTO_TCPv4_SERVER)
+- msg (M_USAGE, "--port-share only works in TCP server mode (--proto tcp-server)");
++ if ((options->port_share_host || options->port_share_port) &&
++ (ce->proto != PROTO_TCPv4_SERVER
++#ifdef USE_PF_INET6
++ && ce->proto != PROTO_TCPv6_SERVER
++#endif
++ ))
++ msg (M_USAGE, "--port-share only works in TCP server mode (--proto tcp-server"
++#ifdef USE_PF_INET6
++ " or tcp6-server"
++#endif
++ ")");
+ #endif
+ if (!options->tls_server)
+ msg (M_USAGE, "--mode server requires --tls-server");
+@@ -1871,9 +1923,17 @@
+ 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 (!(ce->proto == PROTO_UDPv4 || ce->proto == PROTO_TCPv4_SERVER))
+- msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server");
+- if (ce->proto != PROTO_UDPv4 && (options->cf_max || options->cf_per))
++ if (!(proto_is_dgram(ce->proto) || ce->proto == PROTO_TCPv4_SERVER
++#ifdef USE_PF_INET6
++ || ce->proto == PROTO_TCPv6_SERVER
++#endif
++ ))
++ msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server"
++#ifdef USE_PF_INET6
++ " or --proto tcp6-server"
++#endif
++ );
++ if (!proto_is_udp(ce->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");
+@@ -1964,7 +2024,7 @@
+ /*
+ * Check consistency of replay options
+ */
+- if ((ce->proto != PROTO_UDPv4)
++ if ((!proto_is_udp(ce->proto))
+ && (options->replay_window != defaults.replay_window
+ || options->replay_time != defaults.replay_time))
+ msg (M_USAGE, "--replay-window only makes sense with --proto udp");
+@@ -2137,6 +2197,10 @@
+ {
+ if (ce->proto == PROTO_TCPv4)
+ ce->proto = PROTO_TCPv4_CLIENT;
++#ifdef USE_PF_INET6
++ else if (ce->proto == PROTO_TCPv6)
++ ce->proto = PROTO_TCPv6_CLIENT;
++#endif
+ }
+ #endif
+
+Index: openvpn-2.2.1/ps.c
+===================================================================
+--- openvpn-2.2.1.orig/ps.c 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/ps.c 2011-12-13 12:23:07.375080171 +0100
+@@ -320,9 +320,9 @@
+ 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
+Index: openvpn-2.2.1/route.c
+===================================================================
+--- openvpn-2.2.1.orig/route.c 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/route.c 2011-12-13 12:23:07.377080145 +0100
+@@ -581,13 +581,23 @@
+ if (!local)
+ {
+ /* route remote host to original default gateway */
+- add_route3 (rl->spec.remote_host,
+- ~0,
+- rl->spec.net_gateway,
+- tt,
+- flags,
+- es);
+- rl->did_local = true;
++#ifdef USE_PF_INET6
++ /* if remote_host is not ipv4 (ie: ipv6), just skip
++ * adding this special /32 route */
++ if (rl->spec.remote_host != IPV4_INVALID_ADDR) {
++#endif
++ add_route3 (rl->spec.remote_host,
++ ~0,
++ rl->spec.net_gateway,
++ tt,
++ flags,
++ es);
++ rl->did_local = true;
++#ifdef USE_PF_INET6
++ } else {
++ dmsg (D_ROUTE, "ROUTE remote_host protocol differs from tunneled");
++ }
++#endif
+ }
+
+ /* route DHCP/DNS server traffic through original default gateway */
+Index: openvpn-2.2.1/socket.c
+===================================================================
+--- openvpn-2.2.1.orig/socket.c 2011-12-13 12:22:23.000000000 +0100
++++ openvpn-2.2.1/socket.c 2011-12-13 12:23:07.381080096 +0100
+@@ -36,10 +36,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
+ };
+
+ /*
+@@ -276,6 +282,201 @@
+ return (flags & GETADDR_HOST_ORDER) ? ntohl (ia.s_addr) : ia.s_addr;
+ }
+
++#ifdef USE_PF_INET6
++/*
++ * Translate IPv6 addr or hostname into struct addrinfo
++ * If resolve error, try again for
++ * resolve_retry_seconds seconds.
++ */
++bool
++getaddr6 (unsigned int flags,
++ const char *hostname,
++ int resolve_retry_seconds,
++ volatile int *signal_received,
++ int *gai_err,
++ struct sockaddr_in6 *in6)
++{
++ bool success;
++ struct addrinfo hints, *ai;
++ int status;
++ int sigrec = 0;
++ int msglevel = (flags & GETADDR_FATAL) ? M_FATAL : D_RESOLVE_ERRORS;
++ struct gc_arena gc = gc_new ();
++
++ ASSERT(in6);
++
++ if (!hostname)
++ hostname = "::";
++
++ if (flags & GETADDR_RANDOMIZE)
++ hostname = hostname_randomize(hostname, &gc);
++
++ if (flags & GETADDR_MSG_VIRT_OUT)
++ msglevel |= M_MSG_VIRT_OUT;
++
++ CLEAR (ai);
++ success = false;
++
++ if ((flags & (GETADDR_FATAL_ON_SIGNAL|GETADDR_WARN_ON_SIGNAL))
++ && !signal_received)
++ signal_received = &sigrec;
++
++ /* try numeric ipv6 addr first */
++ CLEAR(hints);
++ hints.ai_family = AF_INET6;
++ hints.ai_flags = AI_NUMERICHOST;
++ if ((status = getaddrinfo(hostname, NULL, &hints, &ai))==0)
++ {
++ *in6 = *((struct sockaddr_in6 *)(ai->ai_addr));
++ freeaddrinfo(ai);
++ ai = NULL;
++ }
++ if (gai_err)
++ *gai_err = status;
++
++
++ if (status != 0) /* parse as IPv6 address failed? */
++ {
++ const int fail_wait_interval = 5; /* seconds */
++ int resolve_retries = (flags & GETADDR_TRY_ONCE) ? 1 : (resolve_retry_seconds / fail_wait_interval);
++ const char *fmt;
++ int level = 0;
++ int err;
++
++ ai = NULL;
++
++ fmt = "RESOLVE: Cannot resolve host address: %s: %s";
++ if ((flags & GETADDR_MENTION_RESOLVE_RETRY)
++ && !resolve_retry_seconds)
++ fmt = "RESOLVE: Cannot resolve host address: %s: %s (I would have retried this name query if you had specified the --resolv-retry option.)";
++
++ if (!(flags & GETADDR_RESOLVE) || status == EAI_FAIL)
++ {
++ msg (msglevel, "RESOLVE: Cannot parse IPv6 address: %s", hostname);
++ goto done;
++ }
++
++#ifdef ENABLE_MANAGEMENT
++ if (flags & GETADDR_UPDATE_MANAGEMENT_STATE)
++ {
++ if (management)
++ management_set_state (management,
++ OPENVPN_STATE_RESOLVE,
++ NULL,
++ (in_addr_t)0,
++ (in_addr_t)0);
++ }
++#endif
++
++ /*
++ * Resolve hostname
++ */
++ while (true)
++ {
++ /* try hostname lookup */
++ hints.ai_flags = 0;
++ hints.ai_socktype = dnsflags_to_socktype(flags);
++ dmsg (D_SOCKET_DEBUG, "GETADDR6 flags=0x%04x ai_family=%d ai_socktype=%d",
++ flags, hints.ai_family, hints.ai_socktype);
++ err = getaddrinfo(hostname, NULL, &hints, &ai);
++
++ if (gai_err)
++ *gai_err = err;
++
++ if (signal_received)
++ {
++ get_signal (signal_received);
++ if (*signal_received) /* were we interrupted by a signal? */
++ {
++ if (0 == err) {
++ ASSERT(ai);
++ freeaddrinfo(ai);
++ ai = NULL;
++ }
++ if (*signal_received == SIGUSR1) /* ignore SIGUSR1 */
++ {
++ msg (level, "RESOLVE: Ignored SIGUSR1 signal received during DNS resolution attempt");
++ *signal_received = 0;
++ }
++ else
++ goto done;
++ }
++ }
++
++ /* success? */
++ if (0 == err)
++ break;
++
++ /* resolve lookup failed, should we
++ continue or fail? */
++
++ level = msglevel;
++ if (resolve_retries > 0)
++ level = D_RESOLVE_ERRORS;
++
++ msg (level,
++ fmt,
++ hostname,
++ gai_strerror(err));
++
++ if (--resolve_retries <= 0)
++ goto done;
++
++ openvpn_sleep (fail_wait_interval);
++ }
++
++ ASSERT(ai);
++
++ if (!ai->ai_next)
++ *in6 = *((struct sockaddr_in6*)(ai->ai_addr));
++ else
++ /* more than one address returned */
++ {
++ struct addrinfo *ai_cursor;
++ int n = 0;
++ /* count address list */
++ for (ai_cursor = ai; ai_cursor; ai_cursor = ai_cursor->ai_next) n++;
++ ASSERT (n >= 2);
++
++ msg (D_RESOLVE_ERRORS, "RESOLVE: NOTE: %s resolves to %d ipv6 addresses, choosing one by random",
++ hostname,
++ n);
++
++ /* choose address randomly, for basic load-balancing capability */
++ n--;
++ n %= get_random();
++ for (ai_cursor = ai; n; ai_cursor = ai_cursor->ai_next) n--;
++ *in6 = *((struct sockaddr_in6*)(ai_cursor->ai_addr));
++ }
++
++ freeaddrinfo(ai);
++ ai = NULL;
++
++ /* hostname resolve succeeded */
++ success = true;
++ }
++ else
++ {
++ /* IP address parse succeeded */
++ success = true;
++ }
++
++ done:
++ if (signal_received && *signal_received)
++ {
++ int level = 0;
++ if (flags & GETADDR_FATAL_ON_SIGNAL)
++ level = M_FATAL;
++ else if (flags & GETADDR_WARN_ON_SIGNAL)
++ level = M_WARN;
++ msg (level, "RESOLVE: signal received during DNS resolution attempt");
++ }
++
++ gc_free (&gc);
++ return success;
++}
++#endif /* USE_PF_INET6 */
++
+ /*
+ * We do our own inet_aton because the glibc function
+ * isn't very good about error checking.
+@@ -410,20 +611,53 @@
+ bool *changed,
+ const unsigned int sockflags)
+ {
+- if (host && addr)
++ switch(addr->addr.sa.sa_family)
+ {
+- const in_addr_t new_addr = getaddr (
+- sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sockflags),
+- host,
+- 1,
+- NULL,
+- NULL);
+- if (new_addr && addr->sa.sin_addr.s_addr != new_addr)
++ case AF_INET:
++ if (host && addr)
+ {
+- addr->sa.sin_addr.s_addr = new_addr;
+- *changed = true;
++ const in_addr_t new_addr = getaddr (
++ sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sockflags),
++ host,
++ 1,
++ NULL,
++ NULL);
++ if (new_addr && addr->addr.in4.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:
++ if (host && addr)
++ {
++ struct sockaddr_in6 sin6;
++ CLEAR(sin6);
++ int success = getaddr6 (
++ sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sockflags),
++ host,
++ 1,
++ NULL,
++ NULL,
++ &sin6);
++ if ( success )
++ {
++ if (!IN6_ARE_ADDR_EQUAL(&sin6.sin6_addr, &addr->addr.in6.sin6_addr))
++ {
++ int port = addr->addr.in6.sin6_port;
++ /* ipv6 requires also eg. sin6_scope_id => easier to fully copy and override port */
++ addr->addr.in6 = sin6;
++ addr->addr.in6.sin6_port = port;
++ }
++ }
++ }
++ break;
++#endif
++ default:
++ ASSERT(0);
++ }
+ }
+
+ static int
+@@ -610,12 +844,62 @@
+ else if (flags & SF_USE_IP_PKTINFO)
+ {
+ int pad = 1;
+- setsockopt (sd, SOL_IP, IP_PKTINFO, (void*)&pad, sizeof(pad));
++#ifdef IP_PKTINFO
++ if (setsockopt (sd, SOL_IP, IP_PKTINFO,
++ (void*)&pad, sizeof(pad)) < 0)
++ msg(M_SOCKERR, "UDP: failed setsockopt for IP_PKTINFO");
++#elif defined(IP_RECVDSTADDR)
++ if (setsockopt (sd, IPPROTO_IP, IP_RECVDSTADDR,
++ (void*)&pad, sizeof(pad)) < 0)
++ msg(M_SOCKERR, "UDP: failed setsockopt for IP_RECVDSTADDR");
++#else
++#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
++#endif
++ }
++#endif
++ 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;
++ if (setsockopt (sd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
++ (void*)&pad, sizeof(pad)) < 0)
++ msg(M_SOCKERR, "UDP: failed setsockopt for IPV6_RECVPKTINFO");
+ }
+ #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)
+ {
+@@ -623,6 +907,7 @@
+ if (sock->info.proto == PROTO_UDPv4)
+ {
+ sock->sd = create_socket_udp (sock->sockflags);
++ sock->sockflags |= SF_GETADDRINFO_DGRAM;
+
+ #ifdef ENABLE_SOCKS
+ if (sock->socks_proxy)
+@@ -634,6 +919,18 @@
+ {
+ 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);
++ sock->sockflags |= SF_GETADDRINFO_DGRAM;
++ }
++#endif
+ else
+ {
+ ASSERT (0);
+@@ -671,7 +968,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);
+@@ -679,7 +981,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");
+@@ -692,7 +994,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 */
+@@ -708,7 +1010,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);
+@@ -809,7 +1112,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",
+@@ -830,7 +1133,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)
+@@ -888,7 +1191,7 @@
+ }
+ }
+ #else
+- 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 ();
+ #endif
+@@ -966,7 +1269,20 @@
+ if (*signal_received)
+ goto done;
+
+- *sd = create_socket_tcp ();
++#ifdef USE_PF_INET6
++ switch(local->addr.sa.sa_family)
++ {
++ case PF_INET6:
++ *sd = create_socket_tcp6 ();
++ break;
++ case PF_INET:
++#endif
++ *sd = create_socket_tcp ();
++#ifdef USE_PF_INET6
++ break;
++ }
++#endif
++
+ if (bind_local)
+ socket_bind (*sd, local, "TCP Client");
+ update_remote (remote_dynamic, remote, remote_changed, sockflags);
+@@ -1031,15 +1347,54 @@
+ /* 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,
++#ifdef USE_PF_INET6
++ /* may return AF_{INET|INET6} guessed from local_host */
++ switch(addr_guess_family(sock->info.proto, sock->local_host))
++ {
++ case AF_INET:
++#endif
++ 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);
++#ifdef USE_PF_INET6
++ break;
++ case AF_INET6:
++ {
++ int success;
++ int err;
++ CLEAR(sock->info.lsa->local.addr.in6);
++ if (sock->local_host)
++ {
++ success = getaddr6(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);
++ &err,
++ &sock->info.lsa->local.addr.in6);
++ }
++ else
++ {
++ sock->info.lsa->local.addr.in6.sin6_family = AF_INET6;
++ sock->info.lsa->local.addr.in6.sin6_addr = in6addr_any;
++ success = true;
++ }
++ if (!success)
++ {
++ msg (M_FATAL, "getaddr6() 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 /* USE_PF_INET6 */
+ }
+
+ /* bind to local address/port */
+@@ -1062,14 +1417,32 @@
+ volatile int *signal_received)
+ {
+ struct gc_arena gc = gc_new ();
++#ifdef USE_PF_INET6
++ int af;
++#endif
+
+ if (!sock->did_resolve_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;
++#ifdef USE_PF_INET6
++ af = addr_guess_family(sock->info.proto, sock->remote_host);
++ switch(af)
++ {
++ case AF_INET:
++#endif
++ sock->info.lsa->remote.addr.in4.sin_family = AF_INET;
++ sock->info.lsa->remote.addr.in4.sin_addr.s_addr = 0;
++#ifdef USE_PF_INET6
++ break;
++ case AF_INET6:
++ CLEAR(sock->info.lsa->remote.addr.in6);
++ sock->info.lsa->remote.addr.in6.sin6_family = AF_INET6;
++ sock->info.lsa->remote.addr.in6.sin6_addr = in6addr_any;
++ break;
++ }
++#endif
+
+ if (sock->remote_host)
+ {
+@@ -1112,13 +1485,31 @@
+ ASSERT (0);
+ }
+
+- sock->info.lsa->remote.sa.sin_addr.s_addr = getaddr (
+- flags,
+- sock->remote_host,
+- retry,
+- &status,
+- signal_received);
+-
++#ifdef USE_PF_INET6
++ switch(af)
++ {
++ case AF_INET:
++#endif
++ sock->info.lsa->remote.addr.in4.sin_addr.s_addr = getaddr (
++ flags,
++ sock->remote_host,
++ retry,
++ &status,
++ signal_received);
++#ifdef USE_PF_INET6
++ break;
++ case AF_INET6:
++ status = getaddr6 (
++ flags,
++ sock->remote_host,
++ retry,
++ signal_received,
++ NULL,
++ &sock->info.lsa->remote.addr.in6);
++ break;
++ }
++#endif
++
+ dmsg (D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d",
+ flags,
+ phase,
+@@ -1138,8 +1529,19 @@
+ goto done;
+ }
+ }
+-
+- sock->info.lsa->remote.sa.sin_port = htons (sock->remote_port);
++#ifdef USE_PF_INET6
++ switch(af)
++ {
++ case AF_INET:
++#endif
++ sock->info.lsa->remote.addr.in4.sin_port = htons (sock->remote_port);
++#ifdef USE_PF_INET6
++ break;
++ case AF_INET6:
++ sock->info.lsa->remote.addr.in6.sin6_port = htons (sock->remote_port);
++ break;
++ }
++#endif
+ }
+
+ /* should we re-use previous active remote address? */
+@@ -1256,7 +1658,11 @@
+ if (mode == LS_MODE_TCP_ACCEPT_FROM)
+ {
+ ASSERT (accept_from);
+- ASSERT (sock->info.proto == PROTO_TCPv4_SERVER);
++ ASSERT (sock->info.proto == PROTO_TCPv4_SERVER
++#ifdef USE_PF_INET6
++ || sock->info.proto == PROTO_TCPv6_SERVER
++#endif
++ );
+ ASSERT (!sock->inetd);
+ sock->sd = accept_from->sd;
+ }
+@@ -1313,7 +1719,11 @@
+ /* were we started by inetd or xinetd? */
+ if (sock->inetd)
+ {
+- ASSERT (sock->info.proto != PROTO_TCPv4_CLIENT);
++ ASSERT (sock->info.proto != PROTO_TCPv4_CLIENT
++#ifdef USE_PF_INET6
++ && sock->info.proto != PROTO_TCPv6_CLIENT
++#endif
++ );
+ ASSERT (socket_defined (inetd_socket_descriptor));
+ sock->sd = inetd_socket_descriptor;
+ }
+@@ -1366,7 +1776,34 @@
+ /* were we started by inetd or xinetd? */
+ if (sock->inetd)
+ {
+- 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
++ ) {
++ /* AF_INET as default (and fallback) for inetd */
++ sock->info.lsa->actual.dest.addr.sa.sa_family = AF_INET;
++#ifdef USE_PF_INET6
++#ifdef HAVE_GETSOCKNAME
++ {
++ /* inetd: hint family type for dest = local's */
++ struct openvpn_sockaddr local_addr;
++ socklen_t addrlen = sizeof(local_addr);
++ if (getsockname (sock->sd, (struct sockaddr *)&local_addr, &addrlen) == 0) {
++ sock->info.lsa->actual.dest.addr.sa.sa_family = local_addr.addr.sa.sa_family;
++ dmsg (D_SOCKET_DEBUG, "inetd(%s): using sa_family=%d from getsockname(%d)",
++ proto2ascii(sock->info.proto, false), local_addr.addr.sa.sa_family,
++ sock->sd);
++ } else
++ msg (M_WARN, "inetd(%s): getsockname(%d) failed, using AF_INET",
++ proto2ascii(sock->info.proto, false), sock->sd);
++ }
++#else
++ msg (M_WARN, "inetd(%s): this OS does not provide the getsockname() "
++ "function, using AF_INET",
++ proto2ascii(sock->info.proto, false));
++#endif
++#endif
+ sock->sd =
+ socket_listen_accept (sock->sd,
+ &sock->info.lsa->actual,
+@@ -1376,6 +1813,7 @@
+ false,
+ sock->inetd == INETD_NOWAIT,
+ signal_received);
++ }
+ ASSERT (!remote_changed);
+ if (*signal_received)
+ goto done;
+@@ -1388,7 +1826,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)
+ {
+@@ -1423,7 +1865,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
+@@ -1510,8 +1956,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);
+
+@@ -1526,7 +1972,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);
+ }
+ }
+
+@@ -1708,13 +2154,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);
+ }
+
+@@ -1729,10 +2182,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
++ *
++ */
++#ifdef USE_PF_INET6
++ if (lsa->actual.dest.addr.sa.sa_family != AF_INET)
++ return IPV4_INVALID_ADDR;
++#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;
+ }
+@@ -1959,26 +2427,61 @@
+ const unsigned int flags,
+ struct gc_arena *gc)
+ {
+- if (addr)
++ struct buffer out = alloc_buf_gc (128, gc);
++ bool addr_is_defined;
++ addr_is_defined = addr_defined (addr);
++ if (!addr_is_defined) {
++ return "[undef]";
++ }
++#ifdef USE_PF_INET6
++ switch(addr->addr.sa.sa_family)
+ {
+- struct buffer out = alloc_buf_gc (64, gc);
+- const int port = ntohs (addr->sa.sin_port);
++ case AF_INET:
++#endif
++ {
++ const int port= ntohs (addr->addr.in4.sin_port);
++ buf_puts (&out, "[AF_INET]");
+
+- if (!(flags & PS_DONT_SHOW_ADDR))
+- buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]"));
++ if (!(flags & PS_DONT_SHOW_ADDR))
++ buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->addr.in4.sin_addr) : "[undef]"));
+
+- if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED)))
+- && port)
++ if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED)))
++ && port)
++ {
++ if (separator)
++ buf_printf (&out, "%s", separator);
++
++ buf_printf (&out, "%d", port);
++ }
++ }
++#ifdef USE_PF_INET6
++ break;
++ case AF_INET6:
+ {
+- if (separator)
+- buf_printf (&out, "%s", separator);
++ const int port= ntohs (addr->addr.in6.sin6_port);
++ char buf[INET6_ADDRSTRLEN] = "";
++ 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);
++ buf_printf (&out, "%d", port);
++ }
+ }
+- return BSTR (&out);
++ break;
++ default:
++ ASSERT(0);
+ }
+- else
+- return "[NULL]";
++#endif
++ return BSTR (&out);
+ }
+
+ const char *
+@@ -1987,6 +2490,10 @@
+ return print_link_socket_actual_ex (act, ":", PS_SHOW_PORT|PS_SHOW_PKTINFO, gc);
+ }
+
++#ifndef IF_NAMESIZE
++#define IF_NAMESIZE 16
++#endif
++
+ const char *
+ print_link_socket_actual_ex (const struct link_socket_actual *act,
+ const char *separator,
+@@ -1995,15 +2502,54 @@
+ {
+ if (act)
+ {
++ char ifname[IF_NAMESIZE] = "[undef]";
+ 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))
+ {
+- struct openvpn_sockaddr sa;
+- CLEAR (sa);
+- sa.sa.sin_addr = act->pi.ipi_spec_dst;
+- buf_printf (&out, " (via %s)", print_sockaddr_ex (&sa, separator, 0, gc));
++#ifdef USE_PF_INET6
++ switch(act->dest.addr.sa.sa_family)
++ {
++ case AF_INET:
++#endif
++ {
++ struct openvpn_sockaddr sa;
++ CLEAR (sa);
++ sa.addr.in4.sin_family = AF_INET;
++#ifdef IP_PKTINFO
++ sa.addr.in4.sin_addr = act->pi.in4.ipi_spec_dst;
++ if_indextoname(act->pi.in4.ipi_ifindex, ifname);
++#elif defined(IP_RECVDSTADDR)
++ sa.addr.in4.sin_addr = act->pi.in4;
++ ifname[0]=0;
++#else
++#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
++#endif
++ buf_printf (&out, " (via %s%%%s)",
++ print_sockaddr_ex (&sa, separator, 0, gc),
++ ifname);
++ }
++#ifdef USE_PF_INET6
++ break;
++ case AF_INET6:
++ {
++ struct sockaddr_in6 sin6;
++ char buf[INET6_ADDRSTRLEN] = "[undef]";
++ CLEAR(sin6);
++ sin6.sin6_family = AF_INET6;
++ sin6.sin6_addr = act->pi.in6.ipi6_addr;
++ if_indextoname(act->pi.in6.ipi6_ifindex, ifname);
++ if (getnameinfo((struct sockaddr *)&sin6, sizeof (struct sockaddr_in6),
++ buf, sizeof (buf), NULL, 0, NI_NUMERICHOST) == 0)
++ buf_printf (&out, " (via %s%%%s)", buf, ifname);
++ else
++ buf_printf (&out, " (via [getnameinfo() err]%%%s)", ifname);
++ }
++ break;
++ }
++#endif /* USE_PF_INET6 */
++
+ }
+ #endif
+ return BSTR (&out);
+@@ -2038,18 +2584,40 @@
+ {
+ char name_buf[256];
+
+- 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);
++#ifdef USE_PF_INET6
++ char buf[128];
++ switch(addr->addr.sa.sa_family)
++ {
++ case AF_INET:
++#endif
++ 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);
+
+- setenv_str (es, name_buf, inet_ntoa (addr->sa.sin_addr));
++ setenv_str (es, name_buf, inet_ntoa (addr->addr.in4.sin_addr));
+
+- 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));
++ 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));
++ }
++#ifdef USE_PF_INET6
++ break;
++ 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->addr.in6.sin6_port)
++ {
++ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix);
++ setenv_int (es, name_buf, ntohs (addr->addr.in6.sin6_port));
++ }
++ break;
+ }
++#endif
+ }
+
+ void
+@@ -2059,7 +2627,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);
+ }
+ }
+@@ -2080,16 +2649,63 @@
+ struct proto_names {
+ const char *short_form;
+ const char *display_form;
++ bool is_dgram;
++ bool is_net;
++ unsigned short 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;
++}
++
++unsigned short
++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)
+ {
+@@ -2129,6 +2745,45 @@
+ return BSTR (&out);
+ }
+
++int
++addr_guess_family(int proto, const char *name)
++{
++#ifdef USE_PF_INET6
++ unsigned short ret;
++#endif
++ if (proto)
++ {
++ return proto_sa_family(proto); /* already stamped */
++ }
++#ifdef USE_PF_INET6
++ else
++ {
++ struct addrinfo hints , *ai;
++ int err;
++ CLEAR(hints);
++ hints.ai_flags = AI_NUMERICHOST;
++ err = getaddrinfo(name, NULL, &hints, &ai);
++ if ( 0 == err )
++ {
++ 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";
++ }
++ return "AF_UNSPEC";
++}
++
+ /*
+ * Given a local proto, return local proto
+ * if !remote, or compatible remote proto
+@@ -2143,10 +2798,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;
+ }
+@@ -2205,10 +2865,29 @@
+ #if ENABLE_IP_PKTINFO
+
+ #pragma pack(1) /* needed to keep structure size consistent for 32 vs. 64-bit architectures */
+-struct openvpn_pktinfo
++struct openvpn_in4_pktinfo
++{
++ struct cmsghdr cmsghdr;
++#ifdef HAVE_IN_PKTINFO
++ struct in_pktinfo pi4;
++#endif
++#ifdef IP_RECVDSTADDR
++ struct in_addr pi4;
++#endif
++};
++#ifdef USE_PF_INET6
++struct openvpn_in6_pktinfo
+ {
+ struct cmsghdr cmsghdr;
+- struct in_pktinfo in_pktinfo;
++ struct in6_pktinfo pi6;
++};
++#endif
++
++union openvpn_pktinfo {
++ struct openvpn_in4_pktinfo msgpi4;
++#ifdef USE_PF_INET6
++ struct openvpn_in6_pktinfo msgpi6;
++#endif
+ };
+ #pragma pack()
+
+@@ -2219,18 +2898,18 @@
+ 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);
++ mesg.msg_controllen = sizeof opi;
+ buf->len = recvmsg (sock->sd, &mesg, 0);
+ if (buf->len >= 0)
+ {
+@@ -2239,14 +2918,39 @@
+ cmsg = CMSG_FIRSTHDR (&mesg);
+ if (cmsg != NULL
+ && CMSG_NXTHDR (&mesg, cmsg) == NULL
++#ifdef IP_PKTINFO
+ && cmsg->cmsg_level == SOL_IP
+ && cmsg->cmsg_type == IP_PKTINFO
+- && cmsg->cmsg_len >= sizeof (opi))
++#elif defined(IP_RECVDSTADDR)
++ && cmsg->cmsg_level == IPPROTO_IP
++ && cmsg->cmsg_type == IP_RECVDSTADDR
++#else
++#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
++#endif
++ && cmsg->cmsg_len >= sizeof (struct openvpn_in4_pktinfo))
+ {
++#ifdef IP_PKTINFO
+ 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;
++#elif defined(IP_RECVDSTADDR)
++ from->pi.in4 = *(struct in_addr*) CMSG_DATA (cmsg);
++#else
++#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
++#endif
+ }
++#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;
+ }
+@@ -2258,18 +2962,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;
+ }
+
+@@ -2306,26 +3012,64 @@
+ 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 msgpi4;
++ mesg.msg_name = &to->dest.addr.sa;
++ mesg.msg_namelen = sizeof (struct sockaddr_in);
++ mesg.msg_control = &msgpi4;
++ mesg.msg_controllen = sizeof msgpi4;
++ mesg.msg_flags = 0;
++ cmsg = CMSG_FIRSTHDR (&mesg);
++ cmsg->cmsg_len = sizeof (struct openvpn_in4_pktinfo);
++#ifdef HAVE_IN_PKTINFO
++ cmsg->cmsg_level = SOL_IP;
++ cmsg->cmsg_type = IP_PKTINFO;
++ {
++ struct in_pktinfo *pkti;
++ 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;
++ }
++#elif defined(IP_RECVDSTADDR)
++ cmsg->cmsg_level = IPPROTO_IP;
++ cmsg->cmsg_type = IP_RECVDSTADDR;
++ *(struct in_addr *) CMSG_DATA (cmsg) = to->pi.in4;
++#else
++#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
++#endif
++ break;
++ }
++#ifdef USE_PF_INET6
++ case AF_INET6:
++ {
++ struct openvpn_in6_pktinfo msgpi6;
++ struct in6_pktinfo *pkti6;
++ mesg.msg_name = &to->dest.addr.sa;
++ mesg.msg_namelen = sizeof (struct sockaddr_in6);
++ mesg.msg_control = &msgpi6;
++ mesg.msg_controllen = sizeof msgpi6;
++ mesg.msg_flags = 0;
++ cmsg = CMSG_FIRSTHDR (&mesg);
++ cmsg->cmsg_len = sizeof (struct openvpn_in6_pktinfo);
++ 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);
+ }
+
+@@ -2346,11 +3090,11 @@
+ int status;
+
+ /* reset buf to its initial state */
+- if (sock->info.proto == PROTO_UDPv4)
++ if (proto_is_udp(sock->info.proto))
+ {
+ sock->reads.buf = sock->reads.buf_init;
+ }
+- else if (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_TCPv4_SERVER)
++ else if (proto_is_tcp(sock->info.proto))
+ {
+ stream_buf_get_next (&sock->stream_buf, &sock->reads.buf);
+ }
+@@ -2370,10 +3114,15 @@
+ ASSERT (ResetEvent (sock->reads.overlapped.hEvent));
+ sock->reads.flags = 0;
+
+- if (sock->info.proto == PROTO_UDPv4)
++ if (proto_is_udp(sock->info.proto))
+ {
+ sock->reads.addr_defined = true;
+- sock->reads.addrlen = sizeof (sock->reads.addr);
++#ifdef USE_PF_INET6
++ if (sock->info.proto == PROTO_UDPv6)
++ sock->reads.addrlen = sizeof (sock->reads.addr6);
++ else
++#endif
++ sock->reads.addrlen = sizeof (sock->reads.addr);
+ status = WSARecvFrom(
+ sock->sd,
+ wsabuf,
+@@ -2385,7 +3134,7 @@
+ &sock->reads.overlapped,
+ NULL);
+ }
+- else if (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_TCPv4_SERVER)
++ else if (proto_is_tcp(sock->info.proto))
+ {
+ sock->reads.addr_defined = false;
+ status = WSARecv(
+@@ -2405,8 +3154,14 @@
+
+ if (!status) /* operation completed immediately? */
+ {
++#ifdef USE_PF_INET6
++ int addrlen = af_addr_size(sock->info.lsa->local.addr.sa.sa_family);
++ if (sock->reads.addr_defined && sock->reads.addrlen != addrlen)
++ bad_address_length (sock->reads.addrlen, addrlen);
++#else
+ if (sock->reads.addr_defined && sock->reads.addrlen != sizeof (sock->reads.addr))
+ bad_address_length (sock->reads.addrlen, sizeof (sock->reads.addr));
++#endif
+
+ sock->reads.iostate = IOSTATE_IMMEDIATE_RETURN;
+
+@@ -2465,12 +3220,22 @@
+ ASSERT (ResetEvent (sock->writes.overlapped.hEvent));
+ sock->writes.flags = 0;
+
+- if (sock->info.proto == PROTO_UDPv4)
++ if (proto_is_udp(sock->info.proto))
+ {
+ /* set destination address for UDP writes */
+ sock->writes.addr_defined = true;
+- sock->writes.addr = to->dest.sa;
+- sock->writes.addrlen = sizeof (sock->writes.addr);
++#ifdef USE_PF_INET6
++ if (sock->info.proto == PROTO_UDPv6)
++ {
++ sock->writes.addr6 = to->dest.addr.in6;
++ sock->writes.addrlen = sizeof (sock->writes.addr6);
++ }
++ else
++#endif
++ {
++ sock->writes.addr = to->dest.addr.in4;
++ sock->writes.addrlen = sizeof (sock->writes.addr);
++ }
+
+ status = WSASendTo(
+ sock->sd,
+@@ -2483,7 +3248,7 @@
+ &sock->writes.overlapped,
+ NULL);
+ }
+- else if (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_TCPv4_SERVER)
++ else if (proto_is_tcp(sock->info.proto))
+ {
+ /* destination address for TCP writes was established on connection initiation */
+ sock->writes.addr_defined = false;
+@@ -2622,13 +3387,44 @@
+ if (from)
+ {
+ if (ret >= 0 && io->addr_defined)
++#ifdef USE_PF_INET6
++ {
++ /* TODO(jjo): streamline this mess */
++ /* in this func we dont have relevant info about the PF_ of this
++ * endpoint, as link_socket_actual will be zero for the 1st received packet
++ *
++ * Test for inets PF_ possible sizes
++ */
++ switch (io->addrlen)
++ {
++ case sizeof(struct sockaddr_in):
++ case sizeof(struct sockaddr_in6):
++ /* TODO(jjo): for some reason (?) I'm getting 24,28 for AF_INET6 */
++ case sizeof(struct sockaddr_in6)-4:
++ break;
++ default:
++ bad_address_length (io->addrlen, af_addr_size(io->addr.sin_family));
++ }
++
++ switch (io->addr.sin_family)
++ {
++ case AF_INET:
++ from->dest.addr.in4 = io->addr;
++ break;
++ case AF_INET6:
++ from->dest.addr.in6 = io->addr6;
++ break;
++ }
++ }
++#else
+ {
+ if (io->addrlen != sizeof (io->addr))
+ bad_address_length (io->addrlen, sizeof (io->addr));
+- from->dest.sa = io->addr;
++ from->dest.addr.in4 = io->addr;
+ }
++#endif
+ else
+- CLEAR (from->dest.sa);
++ CLEAR (from->dest.addr);
+ }
+
+ if (buf)
+Index: openvpn-2.2.1/socket.h
+===================================================================
+--- openvpn-2.2.1.orig/socket.h 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/socket.h 2011-12-13 12:23:07.382080084 +0100
+@@ -70,7 +70,13 @@
+ 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 */
+@@ -79,7 +85,17 @@
+ /*int dummy;*/ /* add offset to force a bug if dest not explicitly dereferenced */
+ struct openvpn_sockaddr dest;
+ #if ENABLE_IP_PKTINFO
+- struct in_pktinfo pi;
++ union {
++#ifdef HAVE_IN_PKTINFO
++ struct in_pktinfo in4;
++#endif
++#ifdef IP_RECVDSTADDR
++ struct in_addr in4;
++#endif
++#ifdef USE_PF_INET6
++ struct in6_pktinfo in6;
++#endif
++ } pi;
+ #endif
+ };
+
+@@ -199,6 +215,7 @@
+ # define SF_TCP_NODELAY (1<<1)
+ # define SF_PORT_SHARE (1<<2)
+ # define SF_HOST_RANDOMIZE (1<<3)
++# define SF_GETADDRINFO_DGRAM (1<<4)
+ unsigned int sockflags;
+
+ /* for stream sockets */
+@@ -371,6 +388,12 @@
+
+ void bad_address_length (int actual, int expected);
+
++#ifdef USE_PF_INET6
++/* IPV4_INVALID_ADDR: returned by link_socket_current_remote()
++ * to ease redirect-gateway logic for ipv4 tunnels on ipv6 endpoints
++ */
++#define IPV4_INVALID_ADDR 0xffffffff
++#endif
+ in_addr_t link_socket_current_remote (const struct link_socket_info *info);
+
+ void link_socket_connection_initiated (const struct buffer *buf,
+@@ -410,6 +433,14 @@
+ 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);
++
+
+ #if UNIX_SOCK_SUPPORT
+
+@@ -455,6 +486,11 @@
+ #define GETADDR_UPDATE_MANAGEMENT_STATE (1<<8)
+ #define GETADDR_RANDOMIZE (1<<9)
+
++/* [ab]use flags bits to get socktype info downstream */
++/* TODO(jjo): resolve tradeoff between hackiness|args-overhead */
++#define GETADDR_DGRAM (1<<10)
++#define dnsflags_to_socktype(flags) ((flags & GETADDR_DGRAM) ? SOCK_DGRAM : SOCK_STREAM)
++
+ in_addr_t getaddr (unsigned int flags,
+ const char *hostname,
+ int resolve_retry_seconds,
+@@ -472,23 +508,38 @@
+ * 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
++/*
++ * 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,
++#ifdef USE_PF_INET6
++ PROTO_UDPv6,
++ PROTO_TCPv6_SERVER,
++ PROTO_TCPv6_CLIENT,
++ PROTO_TCPv6,
++#endif
++ 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[];
+
+@@ -518,7 +569,7 @@
+ 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
+@@ -533,7 +584,36 @@
+ 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) {
++#ifdef HAVE_IN_PKTINFO
++ case AF_INET: return lsa->pi.in4.ipi_spec_dst.s_addr != 0;
++#endif
++#ifdef IP_RECVDSTADDR
++ case AF_INET: return lsa->pi.in4.s_addr != 0;
++#endif
++#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
++ return false;
+ }
+
+ static inline bool
+@@ -545,20 +625,50 @@
+ 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
+@@ -571,6 +681,74 @@
+ : 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(unsigned short 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", af);
++ 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)
+ {
+@@ -627,14 +805,18 @@
+ {
+ 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;
+ }
+@@ -740,7 +922,7 @@
+ 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;
+
+@@ -751,10 +933,10 @@
+ #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
+@@ -809,13 +991,14 @@
+ 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
+@@ -846,11 +1029,11 @@
+ 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);
+ }
+Index: openvpn-2.2.1/socks.c
+===================================================================
+--- openvpn-2.2.1.orig/socks.c 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/socks.c 2011-12-13 12:23:07.386080032 +0100
+@@ -299,9 +299,9 @@
+
+ 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)
+@@ -388,8 +388,8 @@
+ /* 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));
+ }
+
+
+@@ -507,8 +507,8 @@
+ 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;
+
+@@ -540,8 +540,8 @@
+ 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;
+ }
+Index: openvpn-2.2.1/syshead.h
+===================================================================
+--- openvpn-2.2.1.orig/syshead.h 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/syshead.h 2011-12-13 12:23:07.389079996 +0100
+@@ -28,6 +28,10 @@
+ /*
+ * Only include if not during configure
+ */
++#ifdef WIN32
++/* USE_PF_INET6: win32 ipv6 exists only after 0x0501 (XP) */
++#define WINVER 0x0501
++#endif
+ #ifndef PACKAGE_NAME
+ #include "config.h"
+ #endif
+@@ -339,6 +343,9 @@
+ #ifdef WIN32
+ #include <iphlpapi.h>
+ #include <wininet.h>
++/* The following two headers are needed of USE_PF_INET6 */
++#include <winsock2.h>
++#include <ws2tcpip.h>
+ #endif
+
+ #ifdef HAVE_SYS_MMAN_H
+@@ -383,9 +390,10 @@
+ #endif
+
+ /*
+- * Does this platform support linux-style IP_PKTINFO?
++ * Does this platform support linux-style IP_PKTINFO
++ * or bsd-style IP_RECVDSTADDR ?
+ */
+-#if defined(ENABLE_MULTIHOME) && defined(HAVE_IN_PKTINFO) && defined(IP_PKTINFO) && defined(HAVE_MSGHDR) && defined(HAVE_CMSGHDR) && defined(HAVE_IOVEC) && defined(CMSG_FIRSTHDR) && defined(CMSG_NXTHDR) && defined(HAVE_RECVMSG) && defined(HAVE_SENDMSG)
++#if defined(ENABLE_MULTIHOME) && ((defined(HAVE_IN_PKTINFO)&&defined(IP_PKTINFO)) || defined(IP_RECVDSTADDR)) && defined(HAVE_MSGHDR) && defined(HAVE_CMSGHDR) && defined(HAVE_IOVEC) && defined(CMSG_FIRSTHDR) && defined(CMSG_NXTHDR) && defined(HAVE_RECVMSG) && defined(HAVE_SENDMSG)
+ #define ENABLE_IP_PKTINFO 1
+ #else
+ #define ENABLE_IP_PKTINFO 0
+Index: openvpn-2.2.1/tun.c
+===================================================================
+--- openvpn-2.2.1.orig/tun.c 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/tun.c 2011-12-13 12:23:07.394079932 +0100
+@@ -1688,7 +1688,9 @@
+ strerror(errno));
+ }
+
++#ifdef IFF_MULTICAST /* openbsd 4.x doesn't have this */
+ info.flags |= IFF_MULTICAST;
++#endif
+
+ if (ioctl (tt->fd, TUNSIFINFO, &info) < 0) {
+ msg (M_WARN | M_ERRNO, "Can't set interface info: %s",
+Index: openvpn-2.2.1/win32.h
+===================================================================
+--- openvpn-2.2.1.orig/win32.h 2011-06-24 08:13:39.000000000 +0200
++++ openvpn-2.2.1/win32.h 2011-12-13 12:23:07.396079908 +0100
+@@ -195,7 +195,10 @@
+ DWORD flags;
+ int status;
+ bool addr_defined;
+- struct sockaddr_in addr;
++ union {
++ struct sockaddr_in addr;
++ struct sockaddr_in6 addr6;
++ };
+ int addrlen;
+ struct buffer buf_init;
+ struct buffer buf;
diff --git a/debian/patches/kfreebsd_support.patch b/debian/patches/kfreebsd_support.patch
new file mode 100644
index 0000000..38237f7
--- /dev/null
+++ b/debian/patches/kfreebsd_support.patch
@@ -0,0 +1,29 @@
+Description: Improve kFreeBSD support
+Author: Gonéri Le Bouder <goneri@rulezlan.org>
+Bug-Debian: http://bugs.debian.org/626062
+Index: openvpn-2.2.1/route.c
+===================================================================
+--- openvpn-2.2.1.orig/route.c 2011-12-13 12:24:54.641739154 +0100
++++ openvpn-2.2.1/route.c 2011-12-13 12:41:46.990083056 +0100
+@@ -1192,7 +1192,7 @@
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add command failed");
+
+-#elif defined(TARGET_FREEBSD)
++#elif defined(TARGET_FREEBSD)||defined(__FreeBSD_kernel__)
+
+ argv_printf (&argv, "%s add",
+ ROUTE_PATH);
+Index: openvpn-2.2.1/tun.c
+===================================================================
+--- openvpn-2.2.1.orig/tun.c 2011-12-13 12:41:30.078294479 +0100
++++ openvpn-2.2.1/tun.c 2011-12-13 12:41:46.993083019 +0100
+@@ -1069,7 +1069,7 @@
+ add_route_connected_v6_net(tt, es);
+ }
+
+-#elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)
++#elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)||defined(__FreeBSD_kernel__)
+
+ /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
+ if (tun)
diff --git a/debian/patches/manpage_dash_escaping.patch b/debian/patches/manpage_dash_escaping.patch
new file mode 100644
index 0000000..23fdb03
--- /dev/null
+++ b/debian/patches/manpage_dash_escaping.patch
@@ -0,0 +1,20 @@
+Index: openvpn-2.2.0/openvpn.8
+===================================================================
+--- openvpn-2.2.0.orig/openvpn.8 2011-04-21 21:13:51.000000000 +0200
++++ openvpn-2.2.0/openvpn.8 2011-05-10 16:19:19.548001393 +0200
+@@ -21,13 +21,13 @@
+ .\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ .\"
+ .\" Manual page for openvpn
+-.\
++.\"
+ .\" SH section heading
+ .\" SS subsection heading
+ .\" LP paragraph
+ .\" IP indented paragraph
+ .\" TP hanging label
+-.\
++.\"
+ .\" .nf -- no formatting
+ .\" .fi -- resume formatting
+ .\" .ft 3 -- boldface
diff --git a/debian/patches/openvpn-pkcs11warn.patch b/debian/patches/openvpn-pkcs11warn.patch
new file mode 100644
index 0000000..e7900a4
--- /dev/null
+++ b/debian/patches/openvpn-pkcs11warn.patch
@@ -0,0 +1,28 @@
+Description: Warn users about deprecated pkcs11 options
+Author: Florian Kulzer <florian.kulzer+debian@icfo.es>
+Bug-Debian: http://bugs.debian.org/475353
+Index: openvpn-2.2.1/options.c
+===================================================================
+--- openvpn-2.2.1.orig/options.c 2011-12-13 12:41:58.510939026 +0100
++++ openvpn-2.2.1/options.c 2011-12-13 12:42:07.882821863 +0100
+@@ -5993,6 +5993,20 @@
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->pkcs11_id_management = true;
+ }
++ else if (streq (p[0], "pkcs11-id-type") ||
++ streq (p[0], "pkcs11-sign-mode") ||
++ streq (p[0], "pkcs11-slot") ||
++ streq (p[0], "pkcs11-slot-type") ||
++ streq (p[0], "show-pkcs11-objects") ||
++ streq (p[0], "show-pkcs11-slots"))
++ {
++ if (file)
++ msg (msglevel, "You are using an obsolete parameter in %s:%d: %s (%s).\nPlease see /usr/share/doc/openvpn/NEWS.Debian.gz for details.",
++ file, line, p[0], PACKAGE_VERSION);
++ else
++ msg (msglevel, "You are using an obsolete parameter: --%s (%s).\nPlease see /usr/share/doc/openvpn/NEWS.Debian.gz for details.",
++ p[0], PACKAGE_VERSION);
++ }
+ #endif
+ #ifdef TUNSETPERSIST
+ else if (streq (p[0], "rmtun"))
diff --git a/debian/patches/remote_env.patch b/debian/patches/remote_env.patch
new file mode 100644
index 0000000..ae26415
--- /dev/null
+++ b/debian/patches/remote_env.patch
@@ -0,0 +1,15 @@
+Index: openvpn-2.1_rc15/options.c
+===================================================================
+--- openvpn-2.1_rc15.orig/options.c 2009-04-30 12:58:46.952616319 +0200
++++ openvpn-2.1_rc15/options.c 2009-04-30 12:58:50.352666598 +0200
+@@ -769,8 +769,8 @@
+ setenv_str_i (es, "proto", proto2ascii (e->proto, false), i);
+ setenv_str_i (es, "local", e->local, i);
+ setenv_int_i (es, "local_port", e->local_port, i);
+- setenv_str_i (es, "remote", e->local, i);
+- setenv_int_i (es, "remote_port", e->local_port, i);
++ setenv_str_i (es, "remote", e->remote, i);
++ setenv_int_i (es, "remote_port", e->remote_port, i);
+
+ #ifdef ENABLE_HTTP_PROXY
+ if (e->http_proxy_options)
diff --git a/debian/patches/route_default_nil.patch b/debian/patches/route_default_nil.patch
new file mode 100644
index 0000000..6d6705e
--- /dev/null
+++ b/debian/patches/route_default_nil.patch
@@ -0,0 +1,15 @@
+Description: Fix small wording in man page.
+Author: Alberto Gonzalez Iniesta <agi@inittab.org>
+Index: openvpn-2.2.1/openvpn.8
+===================================================================
+--- openvpn-2.2.1.orig/openvpn.8 2011-12-13 12:42:10.802785357 +0100
++++ openvpn-2.2.1/openvpn.8 2011-12-13 12:42:12.899759141 +0100
+@@ -954,7 +954,7 @@
+ otherwise 0.
+
+ The default can be specified by leaving an option blank or setting
+-it to "default".
++it to "nil".
+
+ The
+ .B network
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..ca7faac
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,8 @@
+auth-pam_libpam_so_filename.patch
+close_socket_before_scripts.patch
+debian_nogroup_for_sample_files.patch
+openvpn-pkcs11warn.patch
+jjo-ipv6-support.patch
+route_default_nil.patch
+ipv6-payload.patch
+kfreebsd_support.patch