From 3cc0c95853fcb021a12c63581c971c65718cc685 Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Thu, 22 Mar 2012 10:59:59 +0000 Subject: main/openvpn: upgrade to 2.2.2 rebase the ipv6 patch --- main/openvpn/APKBUILD | 10 +- main/openvpn/openvpn-2.2.0-ipv6-20110522-1.patch | 4179 ---------------------- main/openvpn/openvpn-2.2.2-ipv6.patch | 3517 ++++++++++++++++++ 3 files changed, 3522 insertions(+), 4184 deletions(-) delete mode 100644 main/openvpn/openvpn-2.2.0-ipv6-20110522-1.patch create mode 100644 main/openvpn/openvpn-2.2.2-ipv6.patch (limited to 'main/openvpn') diff --git a/main/openvpn/APKBUILD b/main/openvpn/APKBUILD index 7f96559a13..929cd73db5 100644 --- a/main/openvpn/APKBUILD +++ b/main/openvpn/APKBUILD @@ -1,7 +1,7 @@ # Maintainer: Natanael Copa pkgname=openvpn -pkgver=2.2.0 -pkgrel=2 +pkgver=2.2.2 +pkgrel=0 pkgdesc="A robust, and highly configurable VPN (Virtual Private Network)" url="http://openvpn.sourceforge.net/" arch="all" @@ -15,7 +15,7 @@ source="http://swupdate.openvpn.net/community/releases/$pkgname-$pkgver.tar.gz openvpn.confd openvpn.up openvpn.down - openvpn-2.2.0-ipv6-20110522-1.patch + openvpn-2.2.2-ipv6.patch " _builddir="$srcdir"/$pkgname-$pkgver @@ -88,9 +88,9 @@ doc() { default_doc } -md5sums="4f440603eac45fec7be218b87d570834 openvpn-2.2.0.tar.gz +md5sums="c5181e27b7945fa6276d21873329c5c7 openvpn-2.2.2.tar.gz ec99092827faa7226e9f548c2cd1d20c openvpn.initd 9eca88cac6294027ec1bb7be74185c3a openvpn.confd dc72fecd1a1bcef937603057cd6574b1 openvpn.up dc3ff0bae442b9aedd947b8ffda1687a openvpn.down -25172fa251672edc3f7a277b5d7f3f72 openvpn-2.2.0-ipv6-20110522-1.patch" +51b1ddade743505b84d27db9ebfd6c0a openvpn-2.2.2-ipv6.patch" diff --git a/main/openvpn/openvpn-2.2.0-ipv6-20110522-1.patch b/main/openvpn/openvpn-2.2.0-ipv6-20110522-1.patch deleted file mode 100644 index 85819de42f..0000000000 --- a/main/openvpn/openvpn-2.2.0-ipv6-20110522-1.patch +++ /dev/null @@ -1,4179 +0,0 @@ -diff --git openvpn-2.2.0/ChangeLog.IPv6 openvpn-2.2-ipv6-20110522-1/ChangeLog.IPv6 -new file mode 100644 -index 0000000..283fe6e ---- /dev/null -+++ openvpn-2.2-ipv6-20110522-1/ChangeLog.IPv6 -@@ -0,0 +1,440 @@ -+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/: client-tap, v4+v6, ping6 on connected addr -+ -+ * TEST FAIL: Linux/: 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 (: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 -+ -+Thu Apr 28 19:10:01 CEST 2011 -+ -+ * rebase to "origin/release/2.2" branch (at v2.2.0 tag) -+ -+Thu May 19 20:51:12 CEST 2011 -+ -+ * include Windows "netsh add" -> "netsh set ... store=active" patch from -+ Seth Mos, to fix restart problems on Windows due to persistant addresses -+ -+ * TEST SUCCESS: Windows XP SP3: client-tun/net30, v4+v6 -+ -+Sat May 21 17:03:20 CEST 2011 -+ -+ * tun.c: Solaris cleanup (use CLEAR() to zero-out "ifr") -+ -+ * tun.c: Windows cleanup: remove route and IPv6 address on disconnect -+ -+ * route.c, route.h: remove "static" from delete_route_ipv6(), needed -+ for ipv6-route cleanup on disconnect -+ -+ * TEST SUCCESS: Windows XP SP3: client-tun/net30, v4+v6 -+ -+ * TEST SUCCESS: Windows 7 Home Premium: client-tun/net30, v4+v6 -+ -+So 22. Mai 14:46:12 CEST 2011 -+ -+ * Tony Lim: removing routes fails on windows if certain bits are set -+ in the "host part" (others are silently ignored) --> -+ -+ * route.c: create print_in6_addr_netbits_only() helper, call from -+ add_route_ipv6() and delete_route_ipv6() to get only network part -+ of route-to-be-modified -+ -+ * route.c: set 'store=active' on adding routes on WIN32 as well (Tony Lim) -+ -+ * options.c: bump IPv6 release to 20110522-1 -+ -+ * TEST SUCCESS: Linux/iproute2: client-tun/net30+subnet, v4+v6 -+ -+ * TEST SUCCESS: Windows XP SP3: client-tun/net30, v4+v6 -+ -+ * TEST SUCCESS: Windows 7 Home Premium: client-tun/net30, v4+v6 -+ -+ * TEST SUCCESS: OpenBSD 4.7: client-tun/net30, v4+v6 -+ TEST FAIL: OpenBSD 4.7: client-tun/subnet, v4 -+ (seems to be due to "topology subnet has just not been implemented yet") -diff --git openvpn-2.2.0/README.IPv6 openvpn-2.2-ipv6-20110522-1/README.IPv6 -new file mode 100644 -index 0000000..ca578f2 ---- /dev/null -+++ openvpn-2.2-ipv6-20110522-1/README.IPv6 -@@ -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 -diff --git openvpn-2.2.0/TODO.IPv6 openvpn-2.2-ipv6-20110522-1/TODO.IPv6 -new file mode 100644 -index 0000000..167ca51 ---- /dev/null -+++ openvpn-2.2-ipv6-20110522-1/TODO.IPv6 -@@ -0,0 +1,153 @@ -+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) -+ * fixed for Win32, 22.5.2011 -+ -+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 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) -+ -+24.) implement link-local IPv6 addresses -+ (OSPFv3 over TUN/multipoint does not work right now) -diff --git openvpn-2.2.0/forward.c openvpn-2.2-ipv6-20110522-1/forward.c -index 87d05cc..1f3d435 100644 ---- openvpn-2.2.0/forward.c -+++ openvpn-2.2-ipv6-20110522-1/forward.c -@@ -262,7 +262,8 @@ send_control_channel_string (struct context *c, const char *str, int msglevel) - 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); -diff --git openvpn-2.2.0/helper.c openvpn-2.2-ipv6-20110522-1/helper.c -index a9d7fd9..266b246 100644 ---- openvpn-2.2.0/helper.c -+++ openvpn-2.2-ipv6-20110522-1/helper.c -@@ -142,6 +142,55 @@ helper_client_server (struct options *o) - - #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: -diff --git openvpn-2.2.0/init.c openvpn-2.2-ipv6-20110522-1/init.c -index d47a4ef..7fc8eb7 100644 ---- openvpn-2.2.0/init.c -+++ openvpn-2.2-ipv6-20110522-1/init.c -@@ -843,7 +843,7 @@ do_persist_tuntap (const struct options *options) - 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 @@ do_alloc_route_list (struct context *c) - { - 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 @@ do_init_route_list (const struct options *options, - } - } - -+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. - */ -@@ -1172,12 +1213,13 @@ initialization_sequence_completed (struct context *c, const unsigned int flags) - 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)) - { -@@ -1234,11 +1276,16 @@ do_init_tun (struct context *c) - 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); -@@ -1270,6 +1317,8 @@ do_open_tun (struct context *c) - /* 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 -@@ -1286,7 +1335,7 @@ do_open_tun (struct context *c) - - /* 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) -@@ -1315,7 +1364,8 @@ do_open_tun (struct context *c) - - /* 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? -@@ -1389,8 +1439,9 @@ do_close_tun (struct context *c, bool force) - #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) -diff --git openvpn-2.2.0/init.h openvpn-2.2-ipv6-20110522-1/init.h -index cf5ca8a..5a1d1dc 100644 ---- openvpn-2.2.0/init.h -+++ openvpn-2.2-ipv6-20110522-1/init.h -@@ -63,6 +63,7 @@ void init_instance (struct context *c, const struct env_set *env, const unsigned - - 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); -diff --git openvpn-2.2.0/misc.c openvpn-2.2-ipv6-20110522-1/misc.c -index 4067d85..9d351f4 100644 ---- openvpn-2.2.0/misc.c -+++ openvpn-2.2-ipv6-20110522-1/misc.c -@@ -1001,7 +1001,9 @@ setenv_str_ex (struct env_set *es, - { - 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); -diff --git openvpn-2.2.0/mroute.c openvpn-2.2-ipv6-20110522-1/mroute.c -index 3debd80..3182f65 100644 ---- openvpn-2.2.0/mroute.c -+++ openvpn-2.2-ipv6-20110522-1/mroute.c -@@ -88,12 +88,33 @@ mroute_get_in_addr_t (struct mroute_addr *ma, const in_addr_t src, unsigned int - } - } - -+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 @@ mroute_extract_addr_ipv4 (struct mroute_addr *src, - } - 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; -@@ -252,14 +292,36 @@ bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr, - * 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); - } - - /* -@@ -337,17 +399,24 @@ mroute_addr_print_ex (const struct mroute_addr *ma, - } - 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 -@@ -418,6 +487,40 @@ mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir) - } - } - -+/* 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) - { -diff --git openvpn-2.2.0/mroute.h openvpn-2.2-ipv6-20110522-1/mroute.h -index 7265001..b72b5ff 100644 ---- openvpn-2.2.0/mroute.h -+++ openvpn-2.2-ipv6-20110522-1/mroute.h -@@ -85,7 +85,7 @@ struct mroute_addr { - /* - * 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 @@ struct mroute_helper *mroute_helper_init (int ageable_ttl_secs); - 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 -diff --git openvpn-2.2.0/multi.c openvpn-2.2-ipv6-20110522-1/multi.c -index 22c0a3f..f703b8d 100644 ---- openvpn-2.2.0/multi.c -+++ openvpn-2.2-ipv6-20110522-1/multi.c -@@ -316,25 +316,18 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa - */ - 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 @@ multi_del_iroutes (struct multi_context *m, - 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 @@ multi_learn_in_addr_t (struct multi_context *m, - } - } - -+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 @@ multi_add_iroutes (struct multi_context *m, - { - 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_add_iroutes (struct multi_context *m, - - 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 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi) - 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 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi) - 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 @@ multi_set_virtual_addr_env (struct multi_context *m, struct multi_instance *mi) - 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 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi - 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); -diff --git openvpn-2.2.0/openvpn.8 openvpn-2.2-ipv6-20110522-1/openvpn.8 -index 7d213f9..11fd5ad 100644 ---- openvpn-2.2.0/openvpn.8 -+++ openvpn-2.2-ipv6-20110522-1/openvpn.8 -@@ -789,6 +789,8 @@ or - .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 -@@ -4936,6 +4938,57 @@ if certificates are stored as private objects. - .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. -diff --git openvpn-2.2.0/openvpn.h openvpn-2.2-ipv6-20110522-1/openvpn.h -index 641bf93..e5e6e58 100644 ---- openvpn-2.2.0/openvpn.h -+++ openvpn-2.2-ipv6-20110522-1/openvpn.h -@@ -165,6 +165,9 @@ struct context_1 - /* 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 @@ struct context_2 - 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 -diff --git openvpn-2.2.0/options.c openvpn-2.2-ipv6-20110522-1/options.c -index 7708995..bf59e00 100644 ---- openvpn-2.2.0/options.c -+++ openvpn-2.2-ipv6-20110522-1/options.c -@@ -79,6 +79,7 @@ const char title_string[] = - #ifdef ENABLE_EUREPHIA - " [eurephia]" - #endif -+ " [IPv6 payload 20110522-1 (2.2.0)]" - " built on " __DATE__ - ; - -@@ -171,6 +172,8 @@ static const char usage_message[] = - " 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" -@@ -181,6 +184,10 @@ static const char usage_message[] = - " 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" -@@ -369,6 +376,7 @@ static const char usage_message[] = - "\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" -@@ -382,10 +390,16 @@ static const char usage_message[] = - "--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" -@@ -870,6 +884,78 @@ get_ip_addr (const char *ip_string, int msglevel, bool *error) - 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) - { -@@ -988,6 +1074,8 @@ show_p2mp_parms (const struct options *o) - #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)); -@@ -1008,6 +1096,9 @@ show_p2mp_parms (const struct options *o) - 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); -@@ -1021,6 +1112,9 @@ show_p2mp_parms (const struct options *o) - 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); -@@ -1075,6 +1169,25 @@ option_iroute (struct options *o, - 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 */ - -@@ -1112,6 +1225,13 @@ rol_check_alloc (struct options *options) - 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) -@@ -1202,6 +1322,9 @@ show_settings (const struct options *o) - 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); -@@ -1862,8 +1985,10 @@ options_postprocess_verify_ce (const struct options *options, const struct conne - if (options->connection_list) - msg (M_USAGE, " 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) -@@ -1888,6 +2013,11 @@ options_postprocess_verify_ce (const struct options *options, const struct conne - 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) -@@ -1919,6 +2049,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne - */ - 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"); -@@ -2460,6 +2592,8 @@ options_string (const struct options *o, - 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, -@@ -3785,6 +3919,30 @@ add_option (struct options *options, - 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); -@@ -4585,6 +4743,26 @@ add_option (struct options *options, - } - 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; -@@ -4796,6 +4974,33 @@ add_option (struct options *options, - } - } - } -+ 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; -@@ -4880,6 +5085,28 @@ add_option (struct options *options, - 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; -@@ -5075,6 +5302,11 @@ add_option (struct options *options, - } - 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; -@@ -5113,6 +5345,43 @@ add_option (struct options *options, - 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); -diff --git openvpn-2.2.0/options.h openvpn-2.2-ipv6-20110522-1/options.h -index 7f4c0cd..dd04ee8 100644 ---- openvpn-2.2.0/options.h -+++ openvpn-2.2-ipv6-20110522-1/options.h -@@ -205,6 +205,9 @@ struct options - 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 @@ struct options - 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 */ -@@ -355,12 +359,17 @@ struct options - struct plugin_option_list *plugin_list; - #endif - -+ const char *tmp_dir; -+ - #if P2MP - - #if P2MP_SERVER - 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) -@@ -382,24 +391,33 @@ struct options - 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; - const char *client_disconnect_script; - const char *learn_address_script; -- const char *tmp_dir; - const char *client_config_dir; - bool ccd_exclusive; - bool disable; - 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; -@@ -722,6 +740,10 @@ void options_string_import (struct options *options, - 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 - */ -diff --git openvpn-2.2.0/pool.c openvpn-2.2-ipv6-20110522-1/pool.c -index 84333df..60dc520 100644 ---- openvpn-2.2.0/pool.c -+++ openvpn-2.2-ipv6-20110522-1/pool.c -@@ -132,7 +132,10 @@ ifconfig_pool_verify_range (const int msglevel, const in_addr_t start, const in_ - } - - 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 @@ ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplica - 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_free (struct ifconfig_pool *pool) - } - - 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 @@ ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t * - 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 @@ ifconfig_pool_handle_to_ip_base (const struct ifconfig_pool* pool, ifconfig_pool - 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 @@ ifconfig_pool_list (const struct ifconfig_pool* pool, struct status_output *out) - 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 @@ ifconfig_pool_read (struct ifconfig_pool_persist *persist, struct ifconfig_pool - 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 @@ ifconfig_pool_read (struct ifconfig_pool_persist *persist, struct ifconfig_pool - 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 @@ ifconfig_pool_test (in_addr_t start, in_addr_t end) - #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 @@ ifconfig_pool_test (in_addr_t start, in_addr_t end) - #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", -diff --git openvpn-2.2.0/pool.h openvpn-2.2-ipv6-20110522-1/pool.h -index 81264a9..fc9d6ab 100644 ---- openvpn-2.2.0/pool.h -+++ openvpn-2.2-ipv6-20110522-1/pool.h -@@ -52,6 +52,9 @@ struct ifconfig_pool - 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 @@ struct ifconfig_pool_persist - - 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); - -diff --git openvpn-2.2.0/proto.h openvpn-2.2-ipv6-20110522-1/proto.h -index 55f0832..b8e8997 100644 ---- openvpn-2.2.0/proto.h -+++ openvpn-2.2-ipv6-20110522-1/proto.h -@@ -108,6 +108,21 @@ struct openvpn_iphdr { - }; - - /* -+ * 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 { -diff --git openvpn-2.2.0/push.c openvpn-2.2-ipv6-20110522-1/push.c -index 08c7f99..1fd8bea 100644 ---- openvpn-2.2.0/push.c -+++ openvpn-2.2-ipv6-20110522-1/push.c -@@ -189,8 +189,26 @@ send_push_reply (struct context *c) - 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) -diff --git openvpn-2.2.0/route.c openvpn-2.2-ipv6-20110522-1/route.c -index b5092fe..7c81f75 100644 ---- openvpn-2.2.0/route.c -+++ openvpn-2.2-ipv6-20110522-1/route.c -@@ -35,6 +35,7 @@ - #include "socket.h" - #include "manage.h" - #include "win32.h" -+#include "options.h" - - #include "memdbg.h" - -@@ -68,6 +69,15 @@ new_route_option_list (const int max_routes, struct gc_arena *a) - 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 +105,15 @@ new_route_list (const int max_routes, struct gc_arena *a) - 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 +330,68 @@ init_route (struct route *r, - 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 +412,23 @@ add_route_to_option_list (struct route_option_list *l, - } - - 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 +438,15 @@ clear_route_list (struct route_list *rl) - } - - 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 +576,72 @@ init_route_list (struct route_list *rl, - 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, -@@ -704,10 +877,13 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap * - } - - 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; - -@@ -732,12 +908,27 @@ add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, - } - 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) -@@ -747,9 +938,28 @@ delete_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flag - } - 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 -@@ -832,6 +1042,34 @@ setenv_routes (struct env_set *es, const struct route_list *rl) - 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) - { -@@ -1025,6 +1263,187 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s - gc_free (&gc); - } - -+ -+static const char * -+print_in6_addr_netbits_only( struct in6_addr network_copy, int netbits, -+ struct gc_arena * gc) -+{ -+ /* clear host bit parts of route -+ * (needed if routes are specified improperly, or if we need to -+ * explicitely setup/clear the "connected" network routes on some OSes) -+ */ -+ int byte = 15; -+ int bits_to_clear = 128 - 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; } -+ } -+ -+ return print_in6_addr( network_copy, 0, 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; -+ -+ if (!r6->defined) -+ return; -+ -+ gc_init (&gc); -+ argv_init (&argv); -+ -+ network = print_in6_addr_netbits_only( r6->network, r6->netbits, &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 -+ -+ /* in some versions of Windows, routes are persistent across reboots by -+ * default, unless "store=active" is set (pointed out by Tony Lim, thanks) -+ */ -+ argv_printf_cat( &argv, " store=active" ); -+ -+ 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) - { -@@ -1164,6 +1583,142 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags - gc_free (&gc); - } - -+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_netbits_only( r6->network, r6->netbits, &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); -+} -+ - /* - * The --redirect-gateway option requires OS-specific code below - * to get the current default gateway. -diff --git openvpn-2.2.0/route.h openvpn-2.2-ipv6-20110522-1/route.h -index c5cbb7c..6a7704f 100644 ---- openvpn-2.2.0/route.h -+++ openvpn-2.2-ipv6-20110522-1/route.h -@@ -92,6 +92,19 @@ struct route_option_list { - 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_list { - 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,25 @@ struct iroute { - 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 delete_route_ipv6 (const 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 +184,11 @@ void add_route_to_option_list (struct route_option_list *l, - 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 +196,30 @@ bool init_route_list (struct route_list *rl, - 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); - -diff --git openvpn-2.2.0/socket.c openvpn-2.2-ipv6-20110522-1/socket.c -index 4720398..c04edc9 100644 ---- openvpn-2.2.0/socket.c -+++ openvpn-2.2-ipv6-20110522-1/socket.c -@@ -342,6 +342,24 @@ ip_addr_dotted_quad_safe (const char *dotted_quad) - } - } - -+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) - { -@@ -2032,6 +2050,55 @@ print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc) - 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) -@@ -2337,6 +2404,58 @@ link_socket_write_udp_posix_sendmsg (struct link_socket *sock, - - #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) - { -diff --git openvpn-2.2.0/socket.h openvpn-2.2-ipv6-20110522-1/socket.h -index eef98d1..17943e7 100644 ---- openvpn-2.2.0/socket.h -+++ openvpn-2.2-ipv6-20110522-1/socket.h -@@ -351,6 +351,8 @@ const char *print_link_socket_actual (const struct link_socket_actual *act, - #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) -@@ -404,6 +406,7 @@ int openvpn_inet_aton (const char *dotted_quad, struct in_addr *addr); - 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); - -diff --git openvpn-2.2.0/syshead.h openvpn-2.2-ipv6-20110522-1/syshead.h -index 63b82ba..a01c2c4 100644 ---- openvpn-2.2.0/syshead.h -+++ openvpn-2.2-ipv6-20110522-1/syshead.h -@@ -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 - #include -+/* The following two headers are needed of USE_PF_INET6 */ -+#include -+#include - #endif - - #ifdef HAVE_SYS_MMAN_H -diff --git openvpn-2.2.0/tun.c openvpn-2.2-ipv6-20110522-1/tun.c -index 59e87dc..cea1784 100644 ---- openvpn-2.2.0/tun.c -+++ openvpn-2.2-ipv6-20110522-1/tun.c -@@ -56,13 +56,14 @@ static void netsh_ifconfig (const struct tuntap_options *to, - 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 - #endif - -@@ -129,30 +130,6 @@ guess_tuntap_dev (const char *dev, - 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 @@ init_tun (const char *dev, /* --dev option */ - 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 @@ init_tun (const char *dev, /* --dev option */ - - 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,40 @@ init_tun_post (struct tuntap *tt, - #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); -+} -+ -+void delete_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; -+ delete_route_ipv6 (&r6, tt, 0, es); -+} -+#endif -+ -+ - /* execute the ifconfig command through the shell */ - void - do_ifconfig (struct tuntap *tt, -@@ -574,10 +621,16 @@ do_ifconfig (struct tuntap *tt, - 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 +642,13 @@ do_ifconfig (struct tuntap *tt, - 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 +707,19 @@ do_ifconfig (struct tuntap *tt, - 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 +742,18 @@ do_ifconfig (struct tuntap *tt, - ); - 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 +777,7 @@ do_ifconfig (struct tuntap *tt, - - 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 +809,53 @@ do_ifconfig (struct tuntap *tt, - - 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 +917,42 @@ do_ifconfig (struct tuntap *tt, - ); - 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 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 +979,27 @@ do_ifconfig (struct tuntap *tt, - ); - 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 +1065,22 @@ do_ifconfig (struct tuntap *tt, - 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 +1119,19 @@ do_ifconfig (struct tuntap *tt, - 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 +1171,34 @@ do_ifconfig (struct tuntap *tt, - 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 set address MyTap 2001:608:8003::d store=active */ -+ argv_printf (&argv, -+ "%s%sc interface ipv6 set address %s %s store=active", -+ 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 +1231,16 @@ open_null (struct tuntap *tt) - #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 +1336,16 @@ close_tun_generic (struct tuntap *tt) - #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. -@@ -1215,13 +1457,13 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 - close (tt->fd); - tt->fd = -1; - } -- open_tun_generic (dev, dev_type, dev_node, ipv6, false, true, tt); -+ open_tun_generic (dev, dev_type, dev_node, false, true, tt); - } - - #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); - } -@@ -1231,9 +1473,9 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 - #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 */ -@@ -1253,7 +1495,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 - #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; - -@@ -1261,7 +1503,7 @@ tuncfg (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, - 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) -@@ -1404,7 +1646,7 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) - #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; -@@ -1415,8 +1657,11 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 - bool is_tun; - struct strioctl strioc_if, strioc_ppa; - -- ipv6_support (ipv6, true, tt); -- memset(&ifr, 0x0, sizeof(ifr)); -+ /* improved generic TUN/TAP driver from -+ * http://www.whiteboard.ne.jp/~admin2/tuntap/ -+ * has IPv6 support -+ */ -+ CLEAR(ifr); - - if (tt->type == DEV_TYPE_NULL) - { -@@ -1570,6 +1815,18 @@ solaris_close_tun (struct tuntap *tt) - { - 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; -@@ -1622,11 +1879,20 @@ close_tun (struct tuntap *tt) - } - - 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, -@@ -1683,9 +1949,9 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) - */ - - 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) -@@ -1706,12 +1972,31 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 - } - } - -+/* 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); - } - } -@@ -1774,33 +2059,51 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) - #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); -@@ -1808,6 +2111,65 @@ close_tun (struct tuntap *tt) - } - } - -+#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) - { -@@ -1819,6 +2181,7 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) - { - return read (tt->fd, buf, len); - } -+#endif /* NETBSD_MULTI_AF */ - - #elif defined(TARGET_FREEBSD) - -@@ -1832,9 +2195,9 @@ freebsd_modify_read_write_return (int len) - } - - 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) - { -@@ -1920,9 +2283,9 @@ dragonfly_modify_read_write_return (int len) - } - - 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) - { -@@ -1991,6 +2354,61 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) - 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 -@@ -3976,7 +4394,7 @@ fork_register_dns_action (struct tuntap *tt) - } - - 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]; -@@ -3987,7 +4405,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 - - /*netcmd_semaphore_lock ();*/ - -- ipv6_support (ipv6, false, tt); -+ msg( M_INFO, "open_tun, tt->ipv6=%d", tt->ipv6 ); - - if (tt->type == DEV_TYPE_NULL) - { -@@ -4109,6 +4527,16 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 - 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 */ -@@ -4433,6 +4861,26 @@ close_tun (struct tuntap *tt) - - if (tt) - { -+ if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup ) -+ { -+ struct argv argv; -+ argv_init (&argv); -+ -+ /* remove route pointing to interface */ -+ delete_route_connected_v6_net(tt, NULL); -+ -+ /* netsh interface ipv6 delete address \"%s\" %s */ -+ const char * ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc); -+ argv_printf (&argv, -+ "%s%sc interface ipv6 delete address %s %s", -+ get_win_sys_path(), -+ NETSH_PATH_SUFFIX, -+ tt->actual_name, -+ ifconfig_ipv6_local ); -+ -+ netsh_command (&argv, 1); -+ argv_reset (&argv); -+ } - #if 1 - if (tt->ipapi_context_defined) - { -@@ -4536,9 +4984,9 @@ ipset2ascii_all (struct gc_arena *gc) - #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 -diff --git openvpn-2.2.0/tun.h openvpn-2.2-ipv6-20110522-1/tun.h -index 011ab54..f28b8d8 100644 ---- openvpn-2.2.0/tun.h -+++ openvpn-2.2-ipv6-20110522-1/tun.h -@@ -130,6 +130,7 @@ struct tuntap - 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 @@ struct tuntap - 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 @@ tuntap_defined (const struct tuntap *tt) - 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 write_tun (struct tuntap* tt, uint8_t *buf, int len); - 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 @@ struct tuntap *init_tun (const char *dev, /* --dev option */ - 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, -diff --git openvpn-2.2.0/win32.c openvpn-2.2-ipv6-20110522-1/win32.c -index 2b7bf7b..cf6cc2d 100644 ---- openvpn-2.2.0/win32.c -+++ openvpn-2.2-ipv6-20110522-1/win32.c -@@ -874,16 +874,21 @@ win_safe_filename (const char *fn) - 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 @@ env_block (const struct env_set *es) - 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; - } -diff --git openvpn-2.2.0/win32.h openvpn-2.2-ipv6-20110522-1/win32.h -index b6a162e..829933f 100644 ---- openvpn-2.2.0/win32.h -+++ openvpn-2.2-ipv6-20110522-1/win32.h -@@ -269,6 +269,8 @@ char *get_win_sys_path (void); - - /* 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/main/openvpn/openvpn-2.2.2-ipv6.patch b/main/openvpn/openvpn-2.2.2-ipv6.patch new file mode 100644 index 0000000000..f8b8015ea6 --- /dev/null +++ b/main/openvpn/openvpn-2.2.2-ipv6.patch @@ -0,0 +1,3517 @@ +diff --git a/forward.c b/forward.c +index 87d05cc..1f3d435 100644 +--- a/forward.c ++++ b/forward.c +@@ -262,7 +262,8 @@ send_control_channel_string (struct context *c, const char *str, int msglevel) + 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); +diff --git a/helper.c b/helper.c +index a9d7fd9..266b246 100644 +--- a/helper.c ++++ b/helper.c +@@ -142,6 +142,55 @@ helper_client_server (struct options *o) + + #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: +diff --git a/init.c b/init.c +index d47a4ef..7fc8eb7 100644 +--- a/init.c ++++ b/init.c +@@ -843,7 +843,7 @@ do_persist_tuntap (const struct options *options) + 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 @@ do_alloc_route_list (struct context *c) + { + 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 @@ do_init_route_list (const struct options *options, + } + } + ++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. + */ +@@ -1172,12 +1213,13 @@ initialization_sequence_completed (struct context *c, const unsigned int flags) + 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)) + { +@@ -1234,11 +1276,16 @@ do_init_tun (struct context *c) + 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); +@@ -1270,6 +1317,8 @@ do_open_tun (struct context *c) + /* 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 +@@ -1286,7 +1335,7 @@ do_open_tun (struct context *c) + + /* 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) +@@ -1315,7 +1364,8 @@ do_open_tun (struct context *c) + + /* 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? +@@ -1389,8 +1439,9 @@ do_close_tun (struct context *c, bool force) + #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) +diff --git a/init.h b/init.h +index cf5ca8a..5a1d1dc 100644 +--- a/init.h ++++ b/init.h +@@ -63,6 +63,7 @@ void init_instance (struct context *c, const struct env_set *env, const unsigned + + 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); +diff --git a/misc.c b/misc.c +index 4067d85..9d351f4 100644 +--- a/misc.c ++++ b/misc.c +@@ -1001,7 +1001,9 @@ setenv_str_ex (struct env_set *es, + { + 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); +diff --git a/mroute.c b/mroute.c +index 1b3488f..6cfec27 100644 +--- a/mroute.c ++++ b/mroute.c +@@ -88,12 +88,33 @@ mroute_get_in_addr_t (struct mroute_addr *ma, const in_addr_t src, unsigned int + } + } + ++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 +@@ -157,13 +178,29 @@ mroute_extract_addr_ipv4 (struct mroute_addr *src, + } + break; + case 6: +- { +- if( !ipv6warned ) { +- msg (M_WARN, "IPv6 in tun mode is not supported in OpenVPN 2.2"); +- ipv6warned = true; +- } +- 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; +@@ -257,14 +294,36 @@ bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr, + * 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); + } + + /* +@@ -342,17 +401,24 @@ mroute_addr_print_ex (const struct mroute_addr *ma, + } + 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 +@@ -423,6 +489,40 @@ mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir) + } + } + ++/* 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) + { +diff --git a/mroute.h b/mroute.h +index 7265001..b72b5ff 100644 +--- a/mroute.h ++++ b/mroute.h +@@ -85,7 +85,7 @@ struct mroute_addr { + /* + * 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 @@ struct mroute_helper *mroute_helper_init (int ageable_ttl_secs); + 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 +diff --git a/multi.c b/multi.c +index 22c0a3f..f703b8d 100644 +--- a/multi.c ++++ b/multi.c +@@ -316,25 +316,18 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa + */ + 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 @@ multi_del_iroutes (struct multi_context *m, + 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 @@ multi_learn_in_addr_t (struct multi_context *m, + } + } + ++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 @@ multi_add_iroutes (struct multi_context *m, + { + 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_add_iroutes (struct multi_context *m, + + 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 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi) + 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 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi) + 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 @@ multi_set_virtual_addr_env (struct multi_context *m, struct multi_instance *mi) + 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 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi + 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); +diff --git a/openvpn.8 b/openvpn.8 +index 67a9779..5322618 100644 +--- a/openvpn.8 ++++ b/openvpn.8 +@@ -794,6 +794,8 @@ or + .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 @@ if certificates are stored as private objects. + .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. +diff --git a/openvpn.h b/openvpn.h +index 641bf93..e5e6e58 100644 +--- a/openvpn.h ++++ b/openvpn.h +@@ -165,6 +165,9 @@ struct context_1 + /* 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 @@ struct context_2 + 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 +diff --git a/options.c b/options.c +index 7a5e35d..8fdd8a5 100644 +--- a/options.c ++++ b/options.c +@@ -79,6 +79,7 @@ const char title_string[] = + #ifdef ENABLE_EUREPHIA + " [eurephia]" + #endif ++ " [IPv6 payload 20110522-1 (2.2.0)]" + " built on " __DATE__ + ; + +@@ -172,6 +173,8 @@ static const char usage_message[] = + " 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" +@@ -182,6 +185,10 @@ static const char usage_message[] = + " 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" +@@ -370,6 +377,7 @@ static const char usage_message[] = + "\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" +@@ -383,10 +391,16 @@ static const char usage_message[] = + "--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" +@@ -871,6 +885,78 @@ get_ip_addr (const char *ip_string, int msglevel, bool *error) + 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) + { +@@ -989,6 +1075,8 @@ show_p2mp_parms (const struct options *o) + #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)); +@@ -1009,6 +1097,9 @@ show_p2mp_parms (const struct options *o) + 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); +@@ -1022,6 +1113,9 @@ show_p2mp_parms (const struct options *o) + 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); +@@ -1076,6 +1170,25 @@ option_iroute (struct options *o, + 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 */ + +@@ -1113,6 +1226,13 @@ rol_check_alloc (struct options *options) + 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) +@@ -1203,6 +1323,9 @@ show_settings (const struct options *o) + 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); +@@ -1863,8 +1986,10 @@ options_postprocess_verify_ce (const struct options *options, const struct conne + if (options->connection_list) + msg (M_USAGE, " 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) +@@ -1889,6 +2014,11 @@ options_postprocess_verify_ce (const struct options *options, const struct conne + 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) +@@ -1920,6 +2050,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne + */ + 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"); +@@ -2461,6 +2593,8 @@ options_string (const struct options *o, + 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, +@@ -3786,6 +3920,30 @@ add_option (struct options *options, + 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); +@@ -4586,6 +4744,26 @@ add_option (struct options *options, + } + 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; +@@ -4797,6 +4975,33 @@ add_option (struct options *options, + } + } + } ++ 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; +@@ -4881,6 +5086,28 @@ add_option (struct options *options, + 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; +@@ -5076,6 +5303,11 @@ add_option (struct options *options, + } + 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; +@@ -5114,6 +5346,43 @@ add_option (struct options *options, + 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); +diff --git a/options.h b/options.h +index dd49355..3b01597 100644 +--- a/options.h ++++ b/options.h +@@ -205,6 +205,9 @@ struct options + 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 @@ struct options + 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 @@ struct options + 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 @@ struct options + 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 @@ struct options + 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 @@ void options_string_import (struct options *options, + 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 + */ +diff --git a/pool.c b/pool.c +index 84333df..60dc520 100644 +--- a/pool.c ++++ b/pool.c +@@ -132,7 +132,10 @@ ifconfig_pool_verify_range (const int msglevel, const in_addr_t start, const in_ + } + + 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 @@ ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplica + 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_free (struct ifconfig_pool *pool) + } + + 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 @@ ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t * + 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 @@ ifconfig_pool_handle_to_ip_base (const struct ifconfig_pool* pool, ifconfig_pool + 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 @@ ifconfig_pool_list (const struct ifconfig_pool* pool, struct status_output *out) + 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 @@ ifconfig_pool_read (struct ifconfig_pool_persist *persist, struct ifconfig_pool + 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 @@ ifconfig_pool_read (struct ifconfig_pool_persist *persist, struct ifconfig_pool + 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 @@ ifconfig_pool_test (in_addr_t start, in_addr_t end) + #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 @@ ifconfig_pool_test (in_addr_t start, in_addr_t end) + #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", +diff --git a/pool.h b/pool.h +index 81264a9..fc9d6ab 100644 +--- a/pool.h ++++ b/pool.h +@@ -52,6 +52,9 @@ struct ifconfig_pool + 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 @@ struct ifconfig_pool_persist + + 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); + +diff --git a/proto.h b/proto.h +index 55f0832..b8e8997 100644 +--- a/proto.h ++++ b/proto.h +@@ -108,6 +108,21 @@ struct openvpn_iphdr { + }; + + /* ++ * 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 { +diff --git a/push.c b/push.c +index 08c7f99..1fd8bea 100644 +--- a/push.c ++++ b/push.c +@@ -189,8 +189,26 @@ send_push_reply (struct context *c) + 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) +diff --git a/route.c b/route.c +index b5092fe..7c81f75 100644 +--- a/route.c ++++ b/route.c +@@ -35,6 +35,7 @@ + #include "socket.h" + #include "manage.h" + #include "win32.h" ++#include "options.h" + + #include "memdbg.h" + +@@ -68,6 +69,15 @@ new_route_option_list (const int max_routes, struct gc_arena *a) + 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 +105,15 @@ new_route_list (const int max_routes, struct gc_arena *a) + 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 +330,68 @@ init_route (struct route *r, + 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 +412,23 @@ add_route_to_option_list (struct route_option_list *l, + } + + 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 +438,15 @@ clear_route_list (struct route_list *rl) + } + + 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 +576,72 @@ init_route_list (struct route_list *rl, + 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, +@@ -704,10 +877,13 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap * + } + + 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; + +@@ -732,12 +908,27 @@ add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, + } + 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) +@@ -747,9 +938,28 @@ delete_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flag + } + 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 +@@ -832,6 +1042,34 @@ setenv_routes (struct env_set *es, const struct route_list *rl) + 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) + { +@@ -1025,6 +1263,187 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s + gc_free (&gc); + } + ++ ++static const char * ++print_in6_addr_netbits_only( struct in6_addr network_copy, int netbits, ++ struct gc_arena * gc) ++{ ++ /* clear host bit parts of route ++ * (needed if routes are specified improperly, or if we need to ++ * explicitely setup/clear the "connected" network routes on some OSes) ++ */ ++ int byte = 15; ++ int bits_to_clear = 128 - 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; } ++ } ++ ++ return print_in6_addr( network_copy, 0, 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; ++ ++ if (!r6->defined) ++ return; ++ ++ gc_init (&gc); ++ argv_init (&argv); ++ ++ network = print_in6_addr_netbits_only( r6->network, r6->netbits, &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 ++ ++ /* in some versions of Windows, routes are persistent across reboots by ++ * default, unless "store=active" is set (pointed out by Tony Lim, thanks) ++ */ ++ argv_printf_cat( &argv, " store=active" ); ++ ++ 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) + { +@@ -1164,6 +1583,142 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags + gc_free (&gc); + } + ++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_netbits_only( r6->network, r6->netbits, &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); ++} ++ + /* + * The --redirect-gateway option requires OS-specific code below + * to get the current default gateway. +diff --git a/route.h b/route.h +index c5cbb7c..6a7704f 100644 +--- a/route.h ++++ b/route.h +@@ -92,6 +92,19 @@ struct route_option_list { + 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_list { + 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,25 @@ struct iroute { + 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 delete_route_ipv6 (const 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 +184,11 @@ void add_route_to_option_list (struct route_option_list *l, + 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 +196,30 @@ bool init_route_list (struct route_list *rl, + 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); + +diff --git a/socket.c b/socket.c +index 4720398..c04edc9 100644 +--- a/socket.c ++++ b/socket.c +@@ -342,6 +342,24 @@ ip_addr_dotted_quad_safe (const char *dotted_quad) + } + } + ++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) + { +@@ -2032,6 +2050,55 @@ print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc) + 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) +@@ -2337,6 +2404,58 @@ link_socket_write_udp_posix_sendmsg (struct link_socket *sock, + + #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) + { +diff --git a/socket.h b/socket.h +index eef98d1..17943e7 100644 +--- a/socket.h ++++ b/socket.h +@@ -351,6 +351,8 @@ const char *print_link_socket_actual (const struct link_socket_actual *act, + #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) +@@ -404,6 +406,7 @@ int openvpn_inet_aton (const char *dotted_quad, struct in_addr *addr); + 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); + +diff --git a/syshead.h b/syshead.h +index b81ce59..fe4af3f 100644 +--- a/syshead.h ++++ b/syshead.h +@@ -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 + #include ++/* The following two headers are needed of USE_PF_INET6 */ ++#include ++#include + #endif + + #ifdef HAVE_SYS_MMAN_H +diff --git a/tun.c b/tun.c +index d03e8c7..4be71de 100644 +--- a/tun.c ++++ b/tun.c +@@ -56,13 +56,14 @@ static void netsh_ifconfig (const struct tuntap_options *to, + 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 + #endif + +@@ -129,30 +130,6 @@ guess_tuntap_dev (const char *dev, + 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 @@ init_tun (const char *dev, /* --dev option */ + 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 @@ init_tun (const char *dev, /* --dev option */ + + 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,40 @@ init_tun_post (struct tuntap *tt, + #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); ++} ++ ++void delete_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; ++ delete_route_ipv6 (&r6, tt, 0, es); ++} ++#endif ++ ++ + /* execute the ifconfig command through the shell */ + void + do_ifconfig (struct tuntap *tt, +@@ -574,10 +621,16 @@ do_ifconfig (struct tuntap *tt, + 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 +642,13 @@ do_ifconfig (struct tuntap *tt, + 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 +707,19 @@ do_ifconfig (struct tuntap *tt, + 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 +742,18 @@ do_ifconfig (struct tuntap *tt, + ); + 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 +777,7 @@ do_ifconfig (struct tuntap *tt, + + 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 +809,53 @@ do_ifconfig (struct tuntap *tt, + + 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 +917,42 @@ do_ifconfig (struct tuntap *tt, + ); + 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 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 +979,27 @@ do_ifconfig (struct tuntap *tt, + ); + 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 +1065,22 @@ do_ifconfig (struct tuntap *tt, + 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 +1119,19 @@ do_ifconfig (struct tuntap *tt, + 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 +1171,34 @@ do_ifconfig (struct tuntap *tt, + 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 set address MyTap 2001:608:8003::d store=active */ ++ argv_printf (&argv, ++ "%s%sc interface ipv6 set address %s %s store=active", ++ 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 +1231,16 @@ open_null (struct tuntap *tt) + #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 +1336,16 @@ close_tun_generic (struct tuntap *tt) + #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. +@@ -1222,9 +1464,9 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 + #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 +1486,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 + #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 +1494,7 @@ tuncfg (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, + 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 +1637,7 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) + #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,8 +1648,11 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 + bool is_tun; + struct strioctl strioc_if, strioc_ppa; + +- ipv6_support (ipv6, true, tt); +- memset(&ifr, 0x0, sizeof(ifr)); ++ /* improved generic TUN/TAP driver from ++ * http://www.whiteboard.ne.jp/~admin2/tuntap/ ++ * has IPv6 support ++ */ ++ CLEAR(ifr); + + if (tt->type == DEV_TYPE_NULL) + { +@@ -1561,6 +1806,18 @@ solaris_close_tun (struct tuntap *tt) + { + 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 +1870,20 @@ close_tun (struct tuntap *tt) + } + + 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 +1940,9 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) + */ + + 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) +@@ -1697,12 +1963,31 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 + } + } + ++/* 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); + } + } +@@ -1765,33 +2050,51 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) + #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); +@@ -1799,6 +2102,65 @@ close_tun (struct tuntap *tt) + } + } + ++#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) + { +@@ -1810,6 +2172,7 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) + { + return read (tt->fd, buf, len); + } ++#endif /* NETBSD_MULTI_AF */ + + #elif defined(TARGET_FREEBSD) + +@@ -1823,9 +2186,9 @@ freebsd_modify_read_write_return (int len) + } + + 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) + { +@@ -1911,9 +2274,9 @@ dragonfly_modify_read_write_return (int len) + } + + 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) + { +@@ -1982,6 +2345,61 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) + 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 +@@ -3967,7 +4385,7 @@ fork_register_dns_action (struct tuntap *tt) + } + + 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]; +@@ -3978,7 +4396,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 + + /*netcmd_semaphore_lock ();*/ + +- ipv6_support (ipv6, false, tt); ++ msg( M_INFO, "open_tun, tt->ipv6=%d", tt->ipv6 ); + + if (tt->type == DEV_TYPE_NULL) + { +@@ -4432,6 +4850,26 @@ close_tun (struct tuntap *tt) + + if (tt) + { ++ if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup ) ++ { ++ struct argv argv; ++ argv_init (&argv); ++ ++ /* remove route pointing to interface */ ++ delete_route_connected_v6_net(tt, NULL); ++ ++ /* netsh interface ipv6 delete address \"%s\" %s */ ++ const char * ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc); ++ argv_printf (&argv, ++ "%s%sc interface ipv6 delete address %s %s", ++ get_win_sys_path(), ++ NETSH_PATH_SUFFIX, ++ tt->actual_name, ++ ifconfig_ipv6_local ); ++ ++ netsh_command (&argv, 1); ++ argv_reset (&argv); ++ } + #if 1 + if (tt->ipapi_context_defined) + { +@@ -4535,9 +4973,9 @@ ipset2ascii_all (struct gc_arena *gc) + #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 +diff --git a/tun.h b/tun.h +index 011ab54..f28b8d8 100644 +--- a/tun.h ++++ b/tun.h +@@ -130,6 +130,7 @@ struct tuntap + 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 @@ struct tuntap + 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 @@ tuntap_defined (const struct tuntap *tt) + 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 write_tun (struct tuntap* tt, uint8_t *buf, int len); + 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 @@ struct tuntap *init_tun (const char *dev, /* --dev option */ + 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, +diff --git a/win32.c b/win32.c +index 2b7bf7b..cf6cc2d 100644 +--- a/win32.c ++++ b/win32.c +@@ -874,16 +874,21 @@ win_safe_filename (const char *fn) + 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 @@ env_block (const struct env_set *es) + 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; + } +diff --git a/win32.h b/win32.h +index b6a162e..829933f 100644 +--- a/win32.h ++++ b/win32.h +@@ -269,6 +269,8 @@ char *get_win_sys_path (void); + + /* 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(); -- cgit v1.2.3