diff options
Diffstat (limited to 'debian/patches')
21 files changed, 8818 insertions, 0 deletions
diff --git a/debian/patches/accommodate_typo.patch b/debian/patches/accommodate_typo.patch new file mode 100644 index 0000000..ac6be64 --- /dev/null +++ b/debian/patches/accommodate_typo.patch @@ -0,0 +1,15 @@ +Description: Simple spelling fix +Author: Alberto Gonzalez Iniesta <agi@inittab.org> +Index: openvpn/src/openvpn/occ.c +=================================================================== +--- openvpn.orig/src/openvpn/occ.c 2014-05-14 12:58:59.805184504 +0200 ++++ openvpn/src/openvpn/occ.c 2014-05-14 12:58:59.805184504 +0200 +@@ -379,7 +379,7 @@ + && 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)) +- msg (M_INFO, "NOTE: This connection is unable to accomodate a UDP packet size of %d. Consider using --fragment or --mssfix options as a workaround.", ++ msg (M_INFO, "NOTE: This connection is unable to accommodate a UDP packet size of %d. Consider using --fragment or --mssfix options as a workaround.", + c->c2.max_send_size_local); + } + event_timeout_clear (&c->c2.occ_mtu_load_test_interval); 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..7882ec5 --- /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/src/plugins/auth-pam/auth-pam.c +=================================================================== +--- openvpn.orig/src/plugins/auth-pam/auth-pam.c 2012-11-05 16:29:30.000000000 +0100 ++++ openvpn/src/plugins/auth-pam/auth-pam.c 2012-11-05 16:37:20.471136293 +0100 +@@ -696,7 +696,7 @@ + struct user_pass up; + int command; + #ifdef USE_PAM_DLOPEN +- static const char pam_so[] = "libpam.so"; ++ static const char pam_so[] = "libpam.so.0"; + #endif + + /* diff --git a/debian/patches/better_systemd_detection.patch b/debian/patches/better_systemd_detection.patch new file mode 100644 index 0000000..0270075 --- /dev/null +++ b/debian/patches/better_systemd_detection.patch @@ -0,0 +1,25 @@ +Description: Improved systemd detection for password prompt +Author: Martin Pitt <martinpitt@gnome.org> +Bug-Debian: http://bugs.debian.org/747265 +Bug: https://community.openvpn.net/openvpn/ticket/274 +Origin: upstream, https://community.openvpn.net/openvpn/attachment/ticket/274/0001-Update-systemd-check-to-current-upstream-version.patch +Index: openvpn/src/openvpn/console.c +=================================================================== +--- openvpn.orig/src/openvpn/console.c 2012-11-05 16:29:30.000000000 +0100 ++++ openvpn/src/openvpn/console.c 2014-05-14 13:00:24.253185771 +0200 +@@ -145,13 +145,8 @@ + { + struct stat a, b; + +- /* We simply test whether the systemd cgroup hierarchy is +- * mounted */ +- +- return (lstat("/sys/fs/cgroup", &a) == 0) +- && (lstat("/sys/fs/cgroup/systemd", &b) == 0) +- && (a.st_dev != b.st_dev); +- ++ /* mirror what upstream sd_booted() does */ ++ return (access("/run/systemd/system", F_OK) >= 0); + } + + static bool diff --git a/debian/patches/client_connect_tmp_files.patch b/debian/patches/client_connect_tmp_files.patch new file mode 100644 index 0000000..6770287 --- /dev/null +++ b/debian/patches/client_connect_tmp_files.patch @@ -0,0 +1,39 @@ +Index: openvpn/src/openvpn/multi.c +=================================================================== +--- openvpn.orig/src/openvpn/multi.c 2014-10-13 18:23:19.340637895 +0200 ++++ openvpn/src/openvpn/multi.c 2014-10-13 18:23:19.336637895 +0200 +@@ -1452,10 +1452,6 @@ + option_types_found, + mi->context.c2.es); + +- if (!platform_unlink (dc_file)) +- msg (D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s", +- dc_file); +- + /* + * If the --client-connect script generates a config file + * with an --ifconfig-push directive, it will override any +@@ -1698,6 +1694,11 @@ + multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found); + ++cc_succeeded_count; + } ++ ++ if (!platform_unlink (dc_file)) ++ msg (D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s", ++ dc_file); ++ + script_depr_failed: + argv_reset (&argv); + } +@@ -1751,6 +1752,11 @@ + } + else + cc_succeeded = false; ++ ++ if (!platform_unlink (dc_file)) ++ msg (D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s", ++ dc_file); ++ + script_failed: + argv_reset (&argv); + } 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..571b86d --- /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/src/openvpn/socket.c +=================================================================== +--- openvpn.orig/src/openvpn/socket.c 2014-05-14 12:58:44.345184272 +0200 ++++ openvpn/src/openvpn/socket.c 2014-05-14 12:58:44.341184271 +0200 +@@ -1493,6 +1493,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 */ +@@ -1723,10 +1727,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..0517e2f --- /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/sample/sample-config-files/server.conf +=================================================================== +--- openvpn.orig/sample/sample-config-files/server.conf 2012-11-05 16:29:30.264804667 +0100 ++++ openvpn/sample/sample-config-files/server.conf 2012-11-05 16:43:32.080979003 +0100 +@@ -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/sample/sample-config-files/tls-home.conf +=================================================================== +--- openvpn.orig/sample/sample-config-files/tls-home.conf 2012-11-05 16:29:30.264804667 +0100 ++++ openvpn/sample/sample-config-files/tls-home.conf 2012-11-05 16:43:32.080979003 +0100 +@@ -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/sample/sample-config-files/static-home.conf +=================================================================== +--- openvpn.orig/sample/sample-config-files/static-home.conf 2012-11-05 16:29:30.264804667 +0100 ++++ openvpn/sample/sample-config-files/static-home.conf 2012-11-05 16:43:32.080979003 +0100 +@@ -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/sample/sample-config-files/static-office.conf +=================================================================== +--- openvpn.orig/sample/sample-config-files/static-office.conf 2012-11-05 16:29:30.264804667 +0100 ++++ openvpn/sample/sample-config-files/static-office.conf 2012-11-05 16:43:32.084979030 +0100 +@@ -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/sample/sample-config-files/client.conf +=================================================================== +--- openvpn.orig/sample/sample-config-files/client.conf 2012-11-05 16:29:30.264804667 +0100 ++++ openvpn/sample/sample-config-files/client.conf 2012-11-05 16:43:32.084979030 +0100 +@@ -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/sample/sample-config-files/tls-office.conf +=================================================================== +--- openvpn.orig/sample/sample-config-files/tls-office.conf 2012-11-05 16:29:30.264804667 +0100 ++++ openvpn/sample/sample-config-files/tls-office.conf 2012-11-05 16:43:32.084979030 +0100 +@@ -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..03b5dd7 --- /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/src/openvpn/route.c +=================================================================== +--- openvpn.orig/src/openvpn/route.c 2014-05-14 12:58:57.865184474 +0200 ++++ openvpn/src/openvpn/route.c 2014-05-14 12:58:57.861184474 +0200 +@@ -1416,7 +1416,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/src/openvpn/tun.c +=================================================================== +--- openvpn.orig/src/openvpn/tun.c 2014-05-14 12:58:57.865184474 +0200 ++++ openvpn/src/openvpn/tun.c 2014-05-14 12:58:57.861184474 +0200 +@@ -1095,7 +1095,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/manpage_fixes.patch b/debian/patches/manpage_fixes.patch new file mode 100644 index 0000000..2e0e48b --- /dev/null +++ b/debian/patches/manpage_fixes.patch @@ -0,0 +1,130 @@ +Description: Man page fixes +Author: Alberto Gonzalez Iniesta <agi@inittab.org> +Index: openvpn/doc/openvpn.8 +=================================================================== +--- openvpn.orig/doc/openvpn.8 2014-09-02 12:22:58.938244125 +0200 ++++ openvpn/doc/openvpn.8 2014-09-02 12:22:58.934244125 +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 +@@ -2573,7 +2573,7 @@ + .B \-\-management-signal + Send SIGUSR1 signal to OpenVPN if management session disconnects. + This is useful when you wish to disconnect an OpenVPN session on +-user logoff. For --management-client this option is not needed since ++user logoff. For \-\-management-client this option is not needed since + a disconnect will always generate a SIGTERM. + .\"********************************************************* + .TP +@@ -3949,7 +3949,7 @@ + This option is only relevant in UDP mode, i.e. + when either + .B \-\-proto udp +-is specifed, or no ++is specified, or no + .B \-\-proto + option is specified. + +@@ -5087,7 +5087,7 @@ + + This option have changed behaviour in OpenVPN 2.3. Earlier you had to + define +-.B --win-sys env ++.B \-\-win-sys env + to use the SystemRoot environment variable, otherwise it defaulted to C:\\WINDOWS. + It is not needed to use the + .B env +@@ -5130,7 +5130,7 @@ + .B \-\-dev tun + mode, OpenVPN will cause the DHCP server to masquerade as if it were + coming from the remote endpoint. The optional offset parameter is +-an integer which is > -256 and < 256 and which defaults to 0. ++an integer which is > \-256 and < 256 and which defaults to 0. + If offset is positive, the DHCP server will masquerade as the IP + address at network address + offset. + If offset is negative, the DHCP server will masquerade as the IP +@@ -5426,26 +5426,26 @@ + .B \-\-topology + , which has no effect on IPv6). + .TP +-.B --ifconfig-ipv6 ipv6addr/bits ipv6remote ++.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 ++.B \-\-route-ipv6 + if no gateway is specified. + .TP +-.B --route-ipv6 ipv6addr/bits [gateway] [metric] ++.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 ++.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 ++.B \-\-ifconfig-ipv6, \-\-ifconfig-ipv6-pool, \-\-tun-ipv6 + and +-.B --push tun-ipv6 +-Is only accepted if ``--mode server'' or ``--server'' is set. ++.B \-\-push tun-ipv6 ++Is only accepted if ``\-\-mode server'' or ``\-\-server'' is set. + .TP +-.B --ifconfig-ipv6-pool ipv6addr/bits ++.B \-\-ifconfig-ipv6-pool ipv6addr/bits + Specify an IPv6 address pool for dynamic assignment to clients. The + pool starts at + .B ipv6addr +@@ -5454,20 +5454,20 @@ + setting controls the size of the pool. Due to implementation details, + the pool size must be between /64 and /112. + .TP +-.B --ifconfig-ipv6-push ipv6addr/bits ipv6remote ++.B \-\-ifconfig-ipv6-push ipv6addr/bits ipv6remote + for ccd/ per-client static IPv6 interface configuration, see +-.B --client-config-dir ++.B \-\-client-config-dir + and +-.B --ifconfig-push ++.B \-\-ifconfig-push + for more details. + .TP +-.B --iroute-ipv6 ipv6addr/bits ++.B \-\-iroute-ipv6 ipv6addr/bits + for ccd/ per-client static IPv6 route configuration, see +-.B --iroute ++.B \-\-iroute + for more details how to setup and use this, and how +-.B --iroute ++.B \-\-iroute + and +-.B --route ++.B \-\-route + interact. + + .\"********************************************************* +@@ -5953,7 +5953,7 @@ + .TP + .B peer_cert + Temporary file name containing the client certificate upon +-connection. Useful in conjunction with --tls-verify ++connection. Useful in conjunction with \-\-tls-verify + .\"********************************************************* + .TP + .B script_context diff --git a/debian/patches/openvpn-pkcs11warn.patch b/debian/patches/openvpn-pkcs11warn.patch new file mode 100644 index 0000000..e899dcb --- /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/src/openvpn/options.c +=================================================================== +--- openvpn.orig/src/openvpn/options.c 2014-09-02 12:22:48.334244586 +0200 ++++ openvpn/src/openvpn/options.c 2014-09-02 12:22:48.334244586 +0200 +@@ -6244,6 +6244,20 @@ + { + VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS); + } ++ 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 + #if PASSTOS_CAPABILITY + else if (streq (p[0], "passtos")) 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..e8572b5 --- /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/doc/openvpn.8 +=================================================================== +--- openvpn.orig/doc/openvpn.8 2014-05-14 12:58:55.637184441 +0200 ++++ openvpn/doc/openvpn.8 2014-05-14 12:58:55.633184441 +0200 +@@ -966,7 +966,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..a6f52d3 --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,10 @@ +auth-pam_libpam_so_filename.patch +close_socket_before_scripts.patch +debian_nogroup_for_sample_files.patch +openvpn-pkcs11warn.patch +route_default_nil.patch +kfreebsd_support.patch +accommodate_typo.patch +manpage_fixes.patch +better_systemd_detection.patch +client_connect_tmp_files.patch diff --git a/debian/patches/use-dpkg-buildflags.patch b/debian/patches/use-dpkg-buildflags.patch new file mode 100644 index 0000000..e1a7e46 --- /dev/null +++ b/debian/patches/use-dpkg-buildflags.patch @@ -0,0 +1,39 @@ +Description: Use build flags from environment for plugins (dpkg-buildflags). + Necessary for hardening flags. +Author: Simon Ruderich <simon@ruderich.org> +Last-Update: 2012-03-16 + +--- openvpn-2.2.1.orig/src/plugins/auth-pam/Makefile ++++ openvpn-2.2.1/src/plugins/auth-pam/Makefile +@@ -18,13 +18,13 @@ INCLUDE=-I../.. + CC_FLAGS=-O2 -Wall -DDLOPEN_PAM=$(DLOPEN_PAM) + + openvpn-auth-pam.so : auth-pam.o pamdl.o +- gcc ${CC_FLAGS} -fPIC -shared -Wl,-soname,openvpn-auth-pam.so -o openvpn-auth-pam.so auth-pam.o pamdl.o -lc $(LIBPAM) ++ gcc ${CFLAGS} ${CC_FLAGS} ${LDFLAGS} -fPIC -shared -Wl,-soname,openvpn-auth-pam.so -o openvpn-auth-pam.so auth-pam.o pamdl.o -lc $(LIBPAM) + + auth-pam.o : auth-pam.c pamdl.h +- gcc ${CC_FLAGS} -fPIC -c ${INCLUDE} auth-pam.c ++ gcc ${CFLAGS} ${CC_FLAGS} ${CPPFLAGS} -fPIC -c ${INCLUDE} auth-pam.c + + pamdl.o : pamdl.c pamdl.h +- gcc ${CC_FLAGS} -fPIC -c ${INCLUDE} pamdl.c ++ gcc ${CFLAGS} ${CC_FLAGS} ${CPPFLAGS} -fPIC -c ${INCLUDE} pamdl.c + + clean : + rm -f *.o *.so +--- openvpn-2.2.1.orig/src/plugins/down-root/Makefile ++++ openvpn-2.2.1/src/plugins/down-root/Makefile +@@ -8,10 +8,10 @@ INCLUDE=-I../.. + CC_FLAGS=-O2 -Wall + + down-root.so : down-root.o +- gcc ${CC_FLAGS} -fPIC -shared -Wl,-soname,openvpn-down-root.so -o openvpn-down-root.so down-root.o -lc ++ gcc ${CFLAGS} ${CC_FLAGS} ${LDFLAGS} -fPIC -shared -Wl,-soname,openvpn-down-root.so -o openvpn-down-root.so down-root.o -lc + + down-root.o : down-root.c +- gcc ${CC_FLAGS} -fPIC -c ${INCLUDE} down-root.c ++ gcc ${CFLAGS} ${CC_FLAGS} ${CPPFLAGS} -fPIC -c ${INCLUDE} down-root.c + + clean : + rm -f *.o *.so |