aboutsummaryrefslogtreecommitdiffstats
path: root/main/openvpn/openvpn-2.2.0-ipv6-20110522-1.patch
diff options
context:
space:
mode:
authorCarlo Landmeter <clandmeter@gmail.com>2011-06-04 14:33:13 +0000
committerCarlo Landmeter <clandmeter@gmail.com>2011-06-04 14:41:13 +0000
commitf80c232c3e5f32cd904117a1d91273b8887d17e2 (patch)
tree19d79f1a862aae8d76e7ed348ac26bec241ec4b3 /main/openvpn/openvpn-2.2.0-ipv6-20110522-1.patch
parent21bbd56f158e6be27059a8543aa133fbb50bf047 (diff)
downloadaports-f80c232c3e5f32cd904117a1d91273b8887d17e2.tar.bz2
aports-f80c232c3e5f32cd904117a1d91273b8887d17e2.tar.xz
main/openvpn: multiple changes like ipv6 and new initd
added ipv6 patch from: http://www.greenie.net/ipv6/openvpn.html move easy-rsa into subpkg and depend on openssl update init.d and conf.d from latest gentoo release added up/down scripts from latest gentoo release ref #618
Diffstat (limited to 'main/openvpn/openvpn-2.2.0-ipv6-20110522-1.patch')
-rw-r--r--main/openvpn/openvpn-2.2.0-ipv6-20110522-1.patch4179
1 files changed, 4179 insertions, 0 deletions
diff --git a/main/openvpn/openvpn-2.2.0-ipv6-20110522-1.patch b/main/openvpn/openvpn-2.2.0-ipv6-20110522-1.patch
new file mode 100644
index 0000000000..85819de42f
--- /dev/null
+++ b/main/openvpn/openvpn-2.2.0-ipv6-20110522-1.patch
@@ -0,0 +1,4179 @@
+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/<both>: client-tap, v4+v6, ping6 on connected addr
++
++ * TEST FAIL: Linux/<both>: client-tap, v6, route6 (gateway missing)
++
++Do 21. Okt 19:36:49 CEST 2010
++
++ * t_client.sh.in: cherrypick commit f25fe91a40aa3f and 6f1e61b41be52
++ (proper exit codes to signal "SKIP" if we do not want to run)
++
++So 16. Jan 17:25:23 CET 2011
++
++ * tun.c, route.c: cherrypick 121755c2cb4891f and f0eac1a5979096c67
++ (TAP driver and "topology subnet" support for Solaris)
++
++ * tun.c: add IPv6 configuration for TAP interfaces (<device>:1 inet6)
++
++ * tun.c: on close_tun on Solaris, unplumb IPv6 TUN or TAP interfaces
++
++ * TEST SUCCESS: OpenSolaris: client-tun, v4+v6
++ TEST SUCCESS: OpenSolaris: client-tap, v4+v6, ping6 on connected addr
++ TEST FAIL: OpenSolaris: client-tap, v6, route6 (gateway missing)
++
++So 24. Apr 16:51:45 CEST 2011
++
++ * rebase to "beta2.2" branch (at 2.2RC2 tag)
++
++ * mroute.c: remove mroute_helper_lock/_unlock() calls for IPv6
++ * socket.c: remove locking with L_INET_NTOA mutex
++ (all the threading stuff got removed by David Sommerseth for 2.2)
++
++ * mroute.c: remove duplicate mroute_helper_add_iroute6() and
++ mroute_helper_del_iroute6() - "git rebase" artefact
++
++ * ChangeLog.IPv6 and TODO.IPv6: add to commit
++
++ * options.c: tag as 20110424-2 (2.2RC2)
++
++ * TEST SUCCESS: Linux/ifconfig: client-tun/net30+subnet, v4+v6
++
++ * TEST SUCCESS: Linux/iproute2: client-tun/net30+subnet, v4+v6
++
++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<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
++ inet 10.9.0.18 -> 10.9.0.17 netmask 0xffffffff
++ inet6 fe80::a00:20ff:fece:d299%tun0 -> prefixlen 64 scopeid 0x3
++ inet6 2001:608:4:eff::2000:3 -> prefixlen 64
++ inet6 2001:608:4:eff::1:3 -> prefixlen 64
++
++ (pool was changed, previous address still active on tun0, breakage)
++
++ * semi-fixed for NetBSD, 28.2.10, always do tun0 destroy / tun0 create
++ before actual ifconfig -- tunnel still lingers after OpenVPN quits
++
++4b.) verify this - on FreeBSD, tun0 is auto-destroyed if created by
++ opening /dev/tun (and lingers if created by "ifconfig tun0 create")
++
++ -> use for persistant tunnels on not-linux?
++
++5.) add new option "ifconfig-ipv6-push"
++ (per-client static IPv6 assignment, -> radiusplugin, etc)
++
++ * implemented, 14.1.10 *
++
++6.) add new option "route-ipv6-gateway"
++
++7.) add "full" gateway handling for IPv6 in route.c
++ (right now, the routes are just sent down the tun interface, if the
++ operating system in questions supports that, without care for the
++ gateway address - which does not work for gateways that are supposed
++ to point elsewhere. Also, it doesn't work for TAP interfaces.
++
++8.) full IPv6 support for TAP interfaces
++ (main issue should be routes+gateway - and testing :-) )
++
++ test 2010/09/24: TAP itself works on linux/ifconfig+iproute2, but
++ route-via-tap doesn't work at all (route points to "tap0" which fails)
++
++17:51:14.075412 fe:ab:6e:c5:53:71 > 33:33:ff:00:00:01, ethertype IPv6 (0x86dd), length 86: 2001:608:4:a053::1:0 > ff02::1:ff00:1: ICMP6, neighbor solicitation, who has 2001:608:4:a001::1, length 32
++
++ how is iroute-via-tap supposed to work??
++
++9.) verify that iroute-ipv6 and route-ipv6 interact in the same way as
++ documented for iroute/route:
++
++ A's subnet, OpenVPN must push this route to all clients
++ EXCEPT for A, since the subnet is already owned by A.
++ OpenVPN accomplishes this by not
++ not pushing a route to a client
++ if it matches one of the client's iroutes.
++
++10.) extend "ifconfig-ipv6" to handle specification of /netbits, pushing
++ of /netbits, and correctly ifconfig'ing this
++ (default, if not specified: /64)
++
++11.) do not add ipv6-routes if tun-ipv6 is not set - complain instead
++
++ * done * 12.1.10
++
++12.) handle incoming [::] and [fe80:...] packets in tun-p2mp MULTI mode
++ (most likely those are DAD packets)
++ silently ignore DAD?
++ Or accept-and-forward iff (multicast && client2client)?
++ handle NS/NA
++
++13.) from Martin List-Petersen:
++
++ One thing, and I guess this requires modifications in
++ network-manager-openvpn: It also works, BUT ignores "push
++ route-ipv6-gateway" and "push route-ipv6 ...." (obviously routes pushed
++ from the server) entirely.
++
++14.) from ##openvpn-discussion:
++
++ new features should be #ifdef'ed
++
++ (check whether this is feasible at all)
++
++15.) IPv6 related environment variables
++
++ - document all of them in openvpn.8
++ - make sure that all existing IPv4 stuff has IPv6 counterparts
++
++16.) OpenBSD
++ - implement ifconfig/route for IPv6
++ - revert ifconfig/open_tun order to "normal" (separate commit!!!)
++ (openvpn-devel, Subject: OpenBSD)
++ - test
++
++17.) client-option (Elwood)
++ - ignore-v6-push-options yes/no
++ - ignore-v6-route-push ("as for IPv4 routes")
++
++18.) fail-save? "what if 'ip -6 addr add' fails" -> fail, or fallback to v4?
++ (-> recomment setting "ignore-v6-push-options yes")
++
++19.) safety check: if connecting over IPv6 (v6 transport) and the pushed
++ route-ipv6 network encompasses the server IPv6 address, make sure
++ we at least log a warning (until we can fiddle with external routing
++ to make this work correctly).
++
++20.) show "route add" / "route delete" commands for IPv6 in log file
++ (we show the "ifconfig" commands, so why not the routes?)
++
++ 2010-08-07: this is a null-feature - it's already there, but with
++ different debug level (M_INFO vs. D_ROUTE) so user
++ didn't notice
++
++21.) enable ipv6-only server operations
++ - decouple ipv6 pool handling from ipv4 pool
++ - make sure Rest of OpenVPN doesn't assume "there will always be IPv4"
++
++22.) implement --learn-address for IPv6
++
++23.) FreeBSD 8 seems to require explicit setting of the "ifconfig" IPv6
++ route, while FreeBSD 6+7 don't --> more testing, and code fix
++
++ workaround for the time being: just add
++
++ server-ipv6 2001:608:4:a051::/64
++ route-ipv6 2001:608:4:a051::/64
++
++ to the config
++
++ (problem + workaround applies both to tun and tap style devices)
++
++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, "<connection> cannot be used with --mode server");
+ #endif
++#if 0
+ if (options->tun_ipv6)
+ msg (M_USAGE, "--tun-ipv6 cannot be used with --mode server");
++#endif
+ if (options->shaper)
+ msg (M_USAGE, "--shaper cannot be used with --mode server");
+ if (options->inetd)
+@@ -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 <iphlpapi.h>
+ #include <wininet.h>
++/* The following two headers are needed of USE_PF_INET6 */
++#include <winsock2.h>
++#include <ws2tcpip.h>
+ #endif
+
+ #ifdef HAVE_SYS_MMAN_H
+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 <stropts.h>
+ #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<x> interface
++ */
++ argv_printf (&argv, "%s %s destroy", IFCONFIG_PATH, actual );
++ argv_msg (M_INFO, &argv);
++ openvpn_execve_check (&argv, es, 0, "NetBSD ifconfig destroy failed");
++
++ argv_printf (&argv, "%s %s create", IFCONFIG_PATH, actual );
++ argv_msg (M_INFO, &argv);
++ openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig create failed");
++
+ if (tun)
+ argv_printf (&argv,
+ "%s %s %s %s mtu %d netmask 255.255.255.255 up",
+@@ -817,6 +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();