]> TLD Linux GIT Repositories - packages/openvpn.git/commitdiff
- from PLD
authorMarcin Krol <hawk@tld-linux.org>
Tue, 5 May 2015 13:03:22 +0000 (13:03 +0000)
committerMarcin Krol <hawk@tld-linux.org>
Tue, 5 May 2015 13:03:22 +0000 (13:03 +0000)
12 files changed:
openvpn-ipv6_payload.patch [new file with mode: 0644]
openvpn-pam.patch [new file with mode: 0644]
openvpn-service-generator [new file with mode: 0644]
openvpn-tunnel.upstart [new file with mode: 0644]
openvpn-update-resolv-conf [new file with mode: 0644]
openvpn.init [new file with mode: 0644]
openvpn.spec [new file with mode: 0644]
openvpn.sysconfig [new file with mode: 0644]
openvpn.target [new file with mode: 0644]
openvpn.tmpfiles [new file with mode: 0644]
openvpn.upstart [new file with mode: 0644]
openvpn@.service [new file with mode: 0644]

diff --git a/openvpn-ipv6_payload.patch b/openvpn-ipv6_payload.patch
new file mode 100644 (file)
index 0000000..977e5c9
--- /dev/null
@@ -0,0 +1,10435 @@
+diff -durN openvpn-2.2.2.orig/ChangeLog.IPv6 openvpn-2.2.2/ChangeLog.IPv6
+--- openvpn-2.2.2.orig/ChangeLog.IPv6  1970-01-01 01:00:00.000000000 +0100
++++ openvpn-2.2.2/ChangeLog.IPv6       2012-06-01 10:40:28.000000000 +0200
+@@ -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 -durN openvpn-2.2.2.orig/README.IPv6 openvpn-2.2.2/README.IPv6
+--- openvpn-2.2.2.orig/README.IPv6     1970-01-01 01:00:00.000000000 +0100
++++ openvpn-2.2.2/README.IPv6  2012-06-01 10:40:28.000000000 +0200
+@@ -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 -durN openvpn-2.2.2.orig/TODO.IPv6 openvpn-2.2.2/TODO.IPv6
+--- openvpn-2.2.2.orig/TODO.IPv6       1970-01-01 01:00:00.000000000 +0100
++++ openvpn-2.2.2/TODO.IPv6    2012-06-01 10:40:28.000000000 +0200
+@@ -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 -durN openvpn-2.2.2.orig/forward.c openvpn-2.2.2/forward.c
+--- openvpn-2.2.2.orig/forward.c       2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/forward.c    2012-06-01 10:40:28.000000000 +0200
+@@ -262,7 +262,8 @@
+ static void
+ check_add_routes_action (struct context *c, const bool errors)
+ {
+-  do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->plugins, c->c2.es);
++  do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list,
++          c->c1.tuntap, c->plugins, c->c2.es);
+   update_time ();
+   event_timeout_clear (&c->c2.route_wakeup);
+   event_timeout_clear (&c->c2.route_wakeup_expire);
+diff -durN openvpn-2.2.2.orig/helper.c openvpn-2.2.2/helper.c
+--- openvpn-2.2.2.orig/helper.c        2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/helper.c     2012-06-01 10:40:28.000000000 +0200
+@@ -142,6 +142,55 @@
+ #if P2MP
+ #if P2MP_SERVER
++
++  /* 
++   *
++   * HELPER DIRECTIVE for IPv6
++   *
++   * server-ipv6 2001:db8::/64
++   *
++   * EXPANDS TO:
++   *
++   * tun-ipv6
++   * push "tun-ipv6"
++   * ifconfig-ipv6 2001:db8::1 2001:db8::2
++   * if !nopool: 
++   *   ifconfig-ipv6-pool 2001:db8::1:0/64
++   * 
++   */
++   if ( o->server_ipv6_defined )
++     {
++      if ( ! o->server_defined )
++        {
++          msg (M_USAGE, "--server-ipv6 must be used together with --server");
++        }
++      if ( o->server_flags & SF_NOPOOL )
++        {
++          msg( M_USAGE, "--server-ipv6 is incompatible with 'nopool' option" );
++        }
++      if ( o->ifconfig_ipv6_pool_defined )
++        {
++          msg( M_USAGE, "--server-ipv6 already defines an ifconfig-ipv6-pool, so you can't also specify --ifconfig-pool explicitly");
++        }
++
++        /* local ifconfig is "base address + 1" and "+2" */
++      o->ifconfig_ipv6_local = 
++              print_in6_addr( add_in6_addr( o->server_network_ipv6, 1), 0, &o->gc );
++      o->ifconfig_ipv6_remote = 
++              print_in6_addr( add_in6_addr( o->server_network_ipv6, 2), 0, &o->gc );
++
++      /* pool starts at "base address + 0x10000" */
++      ASSERT( o->server_netbits_ipv6 < 96 );          /* want 32 bits */
++      o->ifconfig_ipv6_pool_defined = true;
++      o->ifconfig_ipv6_pool_base = 
++              add_in6_addr( o->server_network_ipv6, 0x10000 );
++      o->ifconfig_ipv6_pool_netbits = o->server_netbits_ipv6;
++
++      o->tun_ipv6 = true;
++
++      push_option( o, "tun-ipv6", M_USAGE );
++     }
++
+   /*
+    *
+    * HELPER DIRECTIVE:
+diff -durN openvpn-2.2.2.orig/init.c openvpn-2.2.2/init.c
+--- openvpn-2.2.2.orig/init.c  2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/init.c       2012-06-01 10:40:28.000000000 +0200
+@@ -843,7 +843,7 @@
+       msg (M_FATAL|M_OPTERR,
+            "options --mktun or --rmtun should only be used together with --dev");
+       tuncfg (options->dev, options->dev_type, options->dev_node,
+-            options->tun_ipv6, options->persist_mode,
++            options->persist_mode,
+             options->username, options->groupname, &options->tuntap_options);
+       if (options->persist_mode && options->lladdr)
+         set_lladdr(options->dev, options->lladdr, NULL);
+@@ -1066,6 +1066,8 @@
+ {
+   if (c->options.routes && !c->c1.route_list)
+     c->c1.route_list = new_route_list (c->options.max_routes, &c->gc);
++  if (c->options.routes_ipv6 && !c->c1.route_ipv6_list)
++    c->c1.route_ipv6_list = new_route_ipv6_list (c->options.max_routes, &c->gc);
+ }
+@@ -1108,6 +1110,45 @@
+     }
+ }
++static void
++do_init_route_ipv6_list (const struct options *options,
++                  struct route_ipv6_list *route_ipv6_list,
++                  bool fatal,
++                  struct env_set *es)
++{
++  const char *gw = NULL;
++  int dev = dev_type_enum (options->dev, options->dev_type);
++  int metric = 0;
++
++  if (dev != DEV_TYPE_TUN )
++    msg( M_WARN, "IPv6 routes on TAP devices are going to fail on some platforms (need gateway spec)" );      /* TODO-GERT */
++
++  gw = options->ifconfig_ipv6_remote;         /* default GW = remote end */
++#if 0                                 /* not yet done for IPv6 - TODO!*/
++  if ( options->route_ipv6_default_gateway )          /* override? */
++    gw = options->route_ipv6_default_gateway;
++#endif
++
++  if (options->route_default_metric)
++    metric = options->route_default_metric;
++
++  if (!init_route_ipv6_list (route_ipv6_list,
++                      options->routes_ipv6,
++                      gw,
++                      metric,
++                      es))
++    {
++      if (fatal)
++      openvpn_exit (OPENVPN_EXIT_STATUS_ERROR);       /* exit point */
++    }
++  else
++    {
++      /* copy routes to environment */
++      setenv_routes_ipv6 (es, route_ipv6_list);
++    }
++}
++
++
+ /*
+  * Called after all initialization has been completed.
+  */
+@@ -1172,12 +1213,13 @@
+ void
+ do_route (const struct options *options,
+         struct route_list *route_list,
++        struct route_ipv6_list *route_ipv6_list,
+         const struct tuntap *tt,
+         const struct plugin_list *plugins,
+         struct env_set *es)
+ {
+-  if (!options->route_noexec && route_list)
+-    add_routes (route_list, tt, ROUTE_OPTION_FLAGS (options), es);
++  if (!options->route_noexec && ( route_list || route_ipv6_list ) )
++    add_routes (route_list, route_ipv6_list, tt, ROUTE_OPTION_FLAGS (options), es);
+   if (plugin_defined (plugins, OPENVPN_PLUGIN_ROUTE_UP))
+     {
+@@ -1234,11 +1276,16 @@
+                          c->options.topology,
+                          c->options.ifconfig_local,
+                          c->options.ifconfig_remote_netmask,
++                         c->options.ifconfig_ipv6_local,
++                         c->options.ifconfig_ipv6_remote,
+                          addr_host (&c->c1.link_socket_addr.local),
+                          addr_host (&c->c1.link_socket_addr.remote),
+                          !c->options.ifconfig_nowarn,
+                          c->c2.es);
++  /* flag tunnel for IPv6 config if --tun-ipv6 is set */
++  c->c1.tuntap->ipv6 = c->options.tun_ipv6;
++
+   init_tun_post (c->c1.tuntap,
+                &c->c2.frame,
+                &c->options.tuntap_options);
+@@ -1270,6 +1317,8 @@
+       /* parse and resolve the route option list */
+       if (c->options.routes && c->c1.route_list && c->c2.link_socket)
+       do_init_route_list (&c->options, c->c1.route_list, &c->c2.link_socket->info, false, c->c2.es);
++      if (c->options.routes_ipv6 && c->c1.route_ipv6_list )
++      do_init_route_ipv6_list (&c->options, c->c1.route_ipv6_list, false, c->c2.es);
+       /* do ifconfig */
+       if (!c->options.ifconfig_noexec
+@@ -1286,7 +1335,7 @@
+       /* open the tun device */
+       open_tun (c->options.dev, c->options.dev_type, c->options.dev_node,
+-              c->options.tun_ipv6, c->c1.tuntap);
++              c->c1.tuntap);
+       /* set the hardware address */
+       if (c->options.lladdr)
+@@ -1315,7 +1364,8 @@
+       /* possibly add routes */
+       if (!c->options.route_delay_defined)
+-      do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->plugins, c->c2.es);
++      do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list,
++                c->c1.tuntap, c->plugins, c->c2.es);
+       /*
+        * Did tun/tap driver give us an MTU?
+@@ -1389,8 +1439,9 @@
+ #endif
+         /* delete any routes we added */
+-        if (c->c1.route_list)
+-          delete_routes (c->c1.route_list, c->c1.tuntap, ROUTE_OPTION_FLAGS (&c->options), c->c2.es);
++        if (c->c1.route_list || c->c1.route_ipv6_list )
++          delete_routes (c->c1.route_list, c->c1.route_ipv6_list,
++                         c->c1.tuntap, ROUTE_OPTION_FLAGS (&c->options), c->c2.es);
+         /* actually close tun/tap device based on --down-pre flag */
+         if (!c->options.down_pre)
+diff -durN openvpn-2.2.2.orig/init.h openvpn-2.2.2/init.h
+--- openvpn-2.2.2.orig/init.h  2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/init.h       2012-06-01 10:40:28.000000000 +0200
+@@ -63,6 +63,7 @@
+ void do_route (const struct options *options,
+              struct route_list *route_list,
++             struct route_ipv6_list *route_ipv6_list,
+              const struct tuntap *tt,
+              const struct plugin_list *plugins,
+              struct env_set *es);
+diff -durN openvpn-2.2.2.orig/misc.c openvpn-2.2.2/misc.c
+--- openvpn-2.2.2.orig/misc.c  2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/misc.c       2012-06-01 10:40:28.000000000 +0200
+@@ -1001,7 +1001,9 @@
+       {
+         const char *str = construct_name_value (name_tmp, val_tmp, &gc);
+         env_set_add (es, str);
+-        /*msg (M_INFO, "SETENV_ES '%s'", str);*/
++#if DEBUG_VERBOSE_SETENV
++        msg (M_INFO, "SETENV_ES '%s'", str);
++#endif
+       }
+       else
+       env_set_del (es, name_tmp);
+diff -durN openvpn-2.2.2.orig/mroute.c openvpn-2.2.2/mroute.c
+--- openvpn-2.2.2.orig/mroute.c        2011-12-13 21:17:06.000000000 +0100
++++ openvpn-2.2.2/mroute.c     2012-06-01 10:42:51.000000000 +0200
+@@ -88,12 +88,33 @@
+     }
+ }
++static inline void
++mroute_get_in6_addr (struct mroute_addr *ma, const struct in6_addr src, unsigned int mask)
++{
++  if (ma)
++    {
++      ma->type = MR_ADDR_IPV6 | mask;
++      ma->netbits = 0;
++      ma->len = 16;
++      *(struct in6_addr *)ma->addr = src;
++    }
++}
++
+ static inline bool
+ mroute_is_mcast (const in_addr_t addr)
+ {
+   return ((addr & htonl(IP_MCAST_SUBNET_MASK)) == htonl(IP_MCAST_NETWORK));
+ }
++/* RFC 4291, 2.7, "binary 11111111 at the start of an address identifies 
++ *                 the address as being a multicast address"
++ */
++static inline bool
++mroute_is_mcast_ipv6 (const struct in6_addr addr)
++{
++  return (addr.s6_addr[0] == 0xff);
++}
++
+ #ifdef ENABLE_PF
+ static unsigned int
+@@ -157,13 +178,29 @@
+           }
+         break;
+       case 6:
+-        {
+-            if( !ipv6warned ) {
+-              msg (M_WARN, "IPv6 in tun mode is not supported in OpenVPN 2.2");
+-              ipv6warned = true;
+-            }
+-          break;
+-        }
++        if (BLEN (buf) >= (int) sizeof (struct openvpn_ipv6hdr))
++          {
++            const struct openvpn_ipv6hdr *ipv6 = (const struct openvpn_ipv6hdr *) BPTR (buf);
++#if 0                         /* very basic debug */
++            struct gc_arena gc = gc_new ();
++            msg( M_INFO, "IPv6 packet! src=%s, dst=%s",
++                      print_in6_addr( ipv6->saddr, 0, &gc ),
++                      print_in6_addr( ipv6->daddr, 0, &gc ));
++            gc_free (&gc);
++#endif
++
++            mroute_get_in6_addr (src, ipv6->saddr, 0);
++            mroute_get_in6_addr (dest, ipv6->daddr, 0);
++
++            if (mroute_is_mcast_ipv6 (ipv6->daddr))
++              ret |= MROUTE_EXTRACT_MCAST;
++
++            ret |= MROUTE_EXTRACT_SUCCEEDED;
++          }
++        break;
++      default:
++          msg (M_WARN, "IP packet with unknown IP version=%d seen",
++                       OPENVPN_IPH_GET_VER (*BPTR(buf)));
+       }
+     }
+   return ret;
+@@ -257,14 +294,36 @@
+  * Zero off the host bits in an address, leaving
+  * only the network bits, using the netbits member of
+  * struct mroute_addr as the controlling parameter.
++ *
++ * TODO: this is called for route-lookup for every yet-unhashed
++ * destination address, so for lots of active net-iroutes, this
++ * might benefit from some "zeroize 32 bit at a time" improvements
+  */
+ void
+ mroute_addr_mask_host_bits (struct mroute_addr *ma)
+ {
+   in_addr_t addr = ntohl(*(in_addr_t*)ma->addr);
+-  ASSERT ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4);
+-  addr &= netbits_to_netmask (ma->netbits);
+-  *(in_addr_t*)ma->addr = htonl (addr);
++  if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4)
++    {
++      addr &= netbits_to_netmask (ma->netbits);
++      *(in_addr_t*)ma->addr = htonl (addr);
++    }
++  else if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV6)
++    {
++      int byte = ma->len-1;           /* rightmost byte in address */
++      int bits_to_clear = 128 - ma->netbits;
++
++      while( byte >= 0 && bits_to_clear > 0 )
++        {
++        if ( bits_to_clear >= 8 )
++          { ma->addr[byte--] = 0; bits_to_clear -= 8; }
++        else
++          { ma->addr[byte--] &= (~0 << bits_to_clear); bits_to_clear = 0; }
++        }
++      ASSERT( bits_to_clear == 0 );
++    }
++  else
++      ASSERT(0);
+ }
+ /*
+@@ -342,17 +401,24 @@
+         }
+         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
+@@ -422,6 +488,40 @@
+       mroute_helper_regenerate (mh);
+     }
+ }
++
++/* this is a bit inelegant, we really should have a helper to that 
++ * is only passed the netbits value, and not the whole struct iroute *
++ * - thus one helper could do IPv4 and IPv6.  For the sake of "not change
++ * code unrelated to IPv4" this is left for later cleanup, for now.
++ */
++void
++mroute_helper_add_iroute6 (struct mroute_helper *mh, 
++                           const struct iroute_ipv6 *ir6)
++{
++  if (ir6->netbits >= 0)
++    {
++      ASSERT (ir6->netbits < MR_HELPER_NET_LEN);
++      ++mh->cache_generation;
++      ++mh->net_len_refcount[ir6->netbits];
++      if (mh->net_len_refcount[ir6->netbits] == 1)
++      mroute_helper_regenerate (mh);
++    }
++}
++
++void
++mroute_helper_del_iroute6 (struct mroute_helper *mh, 
++                         const struct iroute_ipv6 *ir6)
++{
++  if (ir6->netbits >= 0)
++    {
++      ASSERT (ir6->netbits < MR_HELPER_NET_LEN);
++      ++mh->cache_generation;
++      --mh->net_len_refcount[ir6->netbits];
++      ASSERT (mh->net_len_refcount[ir6->netbits] >= 0);
++      if (!mh->net_len_refcount[ir6->netbits])
++      mroute_helper_regenerate (mh);
++    }
++}
+ void
+ mroute_helper_free (struct mroute_helper *mh)
+diff -durN openvpn-2.2.2.orig/mroute.c~ openvpn-2.2.2/mroute.c~
+--- openvpn-2.2.2.orig/mroute.c~       1970-01-01 01:00:00.000000000 +0100
++++ openvpn-2.2.2/mroute.c~    2012-06-01 10:40:28.000000000 +0200
+@@ -0,0 +1,518 @@
++/*
++ *  OpenVPN -- An application to securely tunnel IP networks
++ *             over a single TCP/UDP port, with support for SSL/TLS-based
++ *             session authentication and key exchange,
++ *             packet encryption, packet authentication, and
++ *             packet compression.
++ *
++ *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
++ *
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License version 2
++ *  as published by the Free Software Foundation.
++ *
++ *  This program is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with this program (see the file COPYING included with this
++ *  distribution); if not, write to the Free Software Foundation, Inc.,
++ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include "syshead.h"
++
++#if P2MP_SERVER
++
++#include "mroute.h"
++#include "proto.h"
++#include "error.h"
++#include "socket.h"
++
++#include "memdbg.h"
++
++void
++mroute_addr_init (struct mroute_addr *addr)
++{
++  CLEAR (*addr);
++}
++
++/*
++ * Ethernet multicast addresses.
++ */
++
++static inline bool
++is_mac_mcast_addr (const uint8_t *mac)
++{
++  return (bool) mac[0] & 1;
++}
++
++static inline bool
++is_mac_mcast_maddr (const struct mroute_addr *addr)
++{
++  return (addr->type & MR_ADDR_MASK) == MR_ADDR_ETHER && is_mac_mcast_addr (addr->addr); 
++}
++
++/*
++ * Don't learn certain addresses.
++ */
++bool
++mroute_learnable_address (const struct mroute_addr *addr)
++{
++  int i;
++  bool not_all_zeros = false;
++  bool not_all_ones = false;
++
++  for (i = 0; i < addr->len; ++i)
++    {
++      int b = addr->addr[i];
++      if (b != 0x00)
++      not_all_zeros = true;
++      if (b != 0xFF)
++      not_all_ones = true;
++    }
++  return not_all_zeros && not_all_ones && !is_mac_mcast_maddr (addr);
++}
++
++static inline void
++mroute_get_in_addr_t (struct mroute_addr *ma, const in_addr_t src, unsigned int mask)
++{
++  if (ma)
++    {
++      ma->type = MR_ADDR_IPV4 | mask;
++      ma->netbits = 0;
++      ma->len = 4;
++      *(in_addr_t*)ma->addr = src;
++    }
++}
++
++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
++mroute_extract_addr_arp (struct mroute_addr *src,
++                       struct mroute_addr *dest,
++                       const struct buffer *buf)
++{
++  unsigned int ret = 0;
++  if (BLEN (buf) >= (int) sizeof (struct openvpn_arp))
++    {
++      const struct openvpn_arp *arp = (const struct openvpn_arp *) BPTR (buf);
++      if (arp->mac_addr_type == htons(0x0001)
++        && arp->proto_addr_type == htons(0x0800)
++        && arp->mac_addr_size == 0x06
++        && arp->proto_addr_size == 0x04)
++      {
++        mroute_get_in_addr_t (src, arp->ip_src, MR_ARP);
++        mroute_get_in_addr_t (dest, arp->ip_dest, MR_ARP);
++
++        /* multicast packet? */
++        if (mroute_is_mcast (arp->ip_dest))
++          ret |= MROUTE_EXTRACT_MCAST;
++
++        ret |= MROUTE_EXTRACT_SUCCEEDED;
++      }
++    }
++  return ret;
++}
++
++#endif
++
++unsigned int
++mroute_extract_addr_ipv4 (struct mroute_addr *src,
++                        struct mroute_addr *dest,
++                        const struct buffer *buf)
++{
++  unsigned int ret = 0;
++  static bool ipv6warned = false;
++
++  if (BLEN (buf) >= 1)
++    {
++      switch (OPENVPN_IPH_GET_VER (*BPTR(buf)))
++      {
++      case 4:
++        if (BLEN (buf) >= (int) sizeof (struct openvpn_iphdr))
++          {
++            const struct openvpn_iphdr *ip = (const struct openvpn_iphdr *) BPTR (buf);
++
++            mroute_get_in_addr_t (src, ip->saddr, 0);
++            mroute_get_in_addr_t (dest, ip->daddr, 0);
++
++            /* multicast packet? */
++            if (mroute_is_mcast (ip->daddr))
++              ret |= MROUTE_EXTRACT_MCAST;
++
++            /* IGMP message? */
++            if (ip->protocol == OPENVPN_IPPROTO_IGMP)
++              ret |= MROUTE_EXTRACT_IGMP;
++
++            ret |= MROUTE_EXTRACT_SUCCEEDED;
++          }
++        break;
++      case 6:
++        {
++            if( !ipv6warned ) {
++              msg (M_WARN, "IPv6 in tun mode is not supported in OpenVPN 2.2");
++              ipv6warned = true;
++            }
++          break;
++        }
++      }
++    }
++  return ret;
++}
++
++unsigned int
++mroute_extract_addr_ether (struct mroute_addr *src,
++                         struct mroute_addr *dest,
++                         struct mroute_addr *esrc,
++                         struct mroute_addr *edest,
++                         const struct buffer *buf)
++{
++  unsigned int ret = 0;
++  if (BLEN (buf) >= (int) sizeof (struct openvpn_ethhdr))
++    {
++      const struct openvpn_ethhdr *eth = (const struct openvpn_ethhdr *) BPTR (buf);
++      if (src)
++      {
++        src->type = MR_ADDR_ETHER;
++        src->netbits = 0;
++        src->len = 6;
++        memcpy (src->addr, eth->source, 6);
++      }
++      if (dest)
++      {
++        dest->type = MR_ADDR_ETHER;
++        dest->netbits = 0;
++        dest->len = 6;
++        memcpy (dest->addr, eth->dest, 6);
++
++        /* ethernet broadcast/multicast packet? */
++        if (is_mac_mcast_addr (eth->dest))
++          ret |= MROUTE_EXTRACT_BCAST;
++      }
++        
++      ret |= MROUTE_EXTRACT_SUCCEEDED;
++
++#ifdef ENABLE_PF
++      if (esrc || edest)
++      {
++        struct buffer b = *buf;
++        if (buf_advance (&b, sizeof (struct openvpn_ethhdr)))
++          {
++            switch (ntohs (eth->proto))
++              {
++              case OPENVPN_ETH_P_IPV4:
++                ret |= (mroute_extract_addr_ipv4 (esrc, edest, &b) << MROUTE_SEC_SHIFT);
++                break;
++              case OPENVPN_ETH_P_ARP:
++                ret |= (mroute_extract_addr_arp (esrc, edest, &b) << MROUTE_SEC_SHIFT);
++                break;
++              }
++          }
++      }
++#endif
++    }
++  return ret;
++}
++
++/*
++ * Translate a struct openvpn_sockaddr (osaddr)
++ * to a struct mroute_addr (addr).
++ */
++bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr,
++                                    const struct openvpn_sockaddr *osaddr,
++                                    bool use_port)
++{
++  if (osaddr->sa.sin_family == AF_INET)
++    {
++      if (use_port)
++      {
++        addr->type = MR_ADDR_IPV4 | MR_WITH_PORT;
++        addr->netbits = 0;
++        addr->len = 6;
++        memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
++        memcpy (addr->addr + 4, &osaddr->sa.sin_port, 2);
++      }
++      else
++      {
++        addr->type = MR_ADDR_IPV4;
++        addr->netbits = 0;
++        addr->len = 4;
++        memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
++      }
++      return true;
++    }
++  return false;
++}
++
++/*
++ * 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);
++  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);
++}
++
++/*
++ * The mroute_addr hash function takes into account the
++ * address type, number of bits in the network address,
++ * and the actual address.
++ */
++uint32_t
++mroute_addr_hash_function (const void *key, uint32_t iv)
++{
++  return hash_func (mroute_addr_hash_ptr ((const struct mroute_addr *) key),
++                  mroute_addr_hash_len ((const struct mroute_addr *) key),
++                  iv);
++}
++
++bool
++mroute_addr_compare_function (const void *key1, const void *key2)
++{
++  return mroute_addr_equal ((const struct mroute_addr *) key1,
++                          (const struct mroute_addr *) key2);
++}
++
++const char *
++mroute_addr_print (const struct mroute_addr *ma,
++                 struct gc_arena *gc)
++{
++  return mroute_addr_print_ex (ma, MAPF_IA_EMPTY_IF_UNDEF, gc);
++}
++
++const char *
++mroute_addr_print_ex (const struct mroute_addr *ma,
++                    const unsigned int flags,
++                    struct gc_arena *gc)
++{
++  struct buffer out = alloc_buf_gc (64, gc);
++  if (ma)
++    {
++      struct mroute_addr maddr = *ma;
++
++      switch (maddr.type & MR_ADDR_MASK)
++      {
++      case MR_ADDR_ETHER:
++        buf_printf (&out, "%s", format_hex_ex (ma->addr, 6, 0, 1, ":", gc)); 
++        break;
++      case MR_ADDR_IPV4:
++        {
++          struct buffer buf;
++          in_addr_t addr;
++          int port;
++          bool status;
++          buf_set_read (&buf, maddr.addr, maddr.len);
++          addr = buf_read_u32 (&buf, &status);
++          if (status)
++            {
++              if ((flags & MAPF_SHOW_ARP) && (maddr.type & MR_ARP))
++                buf_printf (&out, "ARP/");
++              buf_printf (&out, "%s", print_in_addr_t (addr, (flags & MAPF_IA_EMPTY_IF_UNDEF) ? IA_EMPTY_IF_UNDEF : 0, gc));
++              if (maddr.type & MR_WITH_NETBITS)
++                {
++                  if (flags & MAPF_SUBNET)
++                    {
++                      const in_addr_t netmask = netbits_to_netmask (maddr.netbits);
++                      buf_printf (&out, "/%s", print_in_addr_t (netmask, 0, gc));
++                    }
++                  else
++                    buf_printf (&out, "/%d", maddr.netbits);
++                }
++            }
++          if (maddr.type & MR_WITH_PORT)
++            {
++              port = buf_read_u16 (&buf);
++              if (port >= 0)
++                buf_printf (&out, ":%d", port);
++            }
++        }
++        break;
++      case MR_ADDR_IPV6:
++        {
++          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
++ * currently used CIDR netlengths, so we don't
++ * have to cycle through all 33.
++ */
++
++struct mroute_helper *
++mroute_helper_init (int ageable_ttl_secs)
++{
++  struct mroute_helper *mh;
++  ALLOC_OBJ_CLEAR (mh, struct mroute_helper);
++  mh->ageable_ttl_secs = ageable_ttl_secs;
++  return mh;
++}
++
++static void
++mroute_helper_regenerate (struct mroute_helper *mh)
++{
++  int i, j = 0;
++  for (i = MR_HELPER_NET_LEN - 1; i >= 0; --i)
++    {
++      if (mh->net_len_refcount[i] > 0)
++      mh->net_len[j++] = (uint8_t) i;
++    }
++  mh->n_net_len = j;
++
++#ifdef ENABLE_DEBUG
++  if (check_debug_level (D_MULTI_DEBUG))
++    {
++      struct gc_arena gc = gc_new ();
++      struct buffer out = alloc_buf_gc (256, &gc);
++      buf_printf (&out, "MROUTE CIDR netlen:");
++      for (i = 0; i < mh->n_net_len; ++i)
++      {
++        buf_printf (&out, " /%d", mh->net_len[i]);
++      }
++      dmsg (D_MULTI_DEBUG, "%s", BSTR (&out));
++      gc_free (&gc);
++    }
++#endif
++}
++
++void
++mroute_helper_add_iroute (struct mroute_helper *mh, const struct iroute *ir)
++{
++  if (ir->netbits >= 0)
++    {
++      ASSERT (ir->netbits < MR_HELPER_NET_LEN);
++      ++mh->cache_generation;
++      ++mh->net_len_refcount[ir->netbits];
++      if (mh->net_len_refcount[ir->netbits] == 1)
++      mroute_helper_regenerate (mh);
++    }
++}
++
++void
++mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir)
++{
++  if (ir->netbits >= 0)
++    {
++      ASSERT (ir->netbits < MR_HELPER_NET_LEN);
++      ++mh->cache_generation;
++      --mh->net_len_refcount[ir->netbits];
++      ASSERT (mh->net_len_refcount[ir->netbits] >= 0);
++      if (!mh->net_len_refcount[ir->netbits])
++      mroute_helper_regenerate (mh);
++    }
++}
++
++/* this is a bit inelegant, we really should have a helper to that 
++ * is only passed the netbits value, and not the whole struct iroute *
++ * - thus one helper could do IPv4 and IPv6.  For the sake of "not change
++ * code unrelated to IPv4" this is left for later cleanup, for now.
++ */
++void
++mroute_helper_add_iroute6 (struct mroute_helper *mh, 
++                           const struct iroute_ipv6 *ir6)
++{
++  if (ir6->netbits >= 0)
++    {
++      ASSERT (ir6->netbits < MR_HELPER_NET_LEN);
++      ++mh->cache_generation;
++      ++mh->net_len_refcount[ir6->netbits];
++      if (mh->net_len_refcount[ir6->netbits] == 1)
++      mroute_helper_regenerate (mh);
++    }
++}
++
++void
++mroute_helper_del_iroute6 (struct mroute_helper *mh, 
++                         const struct iroute_ipv6 *ir6)
++{
++  if (ir6->netbits >= 0)
++    {
++      ASSERT (ir6->netbits < MR_HELPER_NET_LEN);
++      ++mh->cache_generation;
++      --mh->net_len_refcount[ir6->netbits];
++      ASSERT (mh->net_len_refcount[ir6->netbits] >= 0);
++      if (!mh->net_len_refcount[ir6->netbits])
++      mroute_helper_regenerate (mh);
++    }
++}
++
++void
++mroute_helper_free (struct mroute_helper *mh)
++{
++  free (mh);
++}
++
++#else
++static void dummy(void) {}
++#endif /* P2MP_SERVER */
+diff -durN openvpn-2.2.2.orig/mroute.h openvpn-2.2.2/mroute.h
+--- openvpn-2.2.2.orig/mroute.h        2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/mroute.h     2012-06-01 10:40:28.000000000 +0200
+@@ -85,7 +85,7 @@
+ /*
+  * Number of bits in an address.  Should be raised for IPv6.
+  */
+-#define MR_HELPER_NET_LEN 32
++#define MR_HELPER_NET_LEN 129
+ /*
+  * Used to help maintain CIDR routing table.
+@@ -127,6 +127,8 @@
+ void mroute_helper_free (struct mroute_helper *mh);
+ void mroute_helper_add_iroute (struct mroute_helper *mh, const struct iroute *ir);
+ void mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir);
++void mroute_helper_add_iroute6 (struct mroute_helper *mh, const struct iroute_ipv6 *ir6);
++void mroute_helper_del_iroute6 (struct mroute_helper *mh, const struct iroute_ipv6 *ir6);
+ /*
+  * Given a raw packet in buf, return the src and dest
+diff -durN openvpn-2.2.2.orig/multi.c openvpn-2.2.2/multi.c
+--- openvpn-2.2.2.orig/multi.c 2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/multi.c      2012-06-01 10:40:28.000000000 +0200
+@@ -316,25 +316,18 @@
+    */
+   if (t->options.ifconfig_pool_defined)
+     {
+-      if (dev == DEV_TYPE_TAP)
+-      {
+-        m->ifconfig_pool = ifconfig_pool_init (IFCONFIG_POOL_INDIV,
+-                                               t->options.ifconfig_pool_start,
+-                                               t->options.ifconfig_pool_end,
+-                                               t->options.duplicate_cn);
+-      }
+-      else if (dev == DEV_TYPE_TUN)
+-      {
+-        m->ifconfig_pool = ifconfig_pool_init (
+-          (t->options.topology == TOP_NET30) ? IFCONFIG_POOL_30NET : IFCONFIG_POOL_INDIV,
+-          t->options.ifconfig_pool_start,
+-          t->options.ifconfig_pool_end,
+-          t->options.duplicate_cn);
+-      }
+-      else
+-      {
+-        ASSERT (0);
+-      }
++      int pool_type = IFCONFIG_POOL_INDIV;
++
++      if ( dev == DEV_TYPE_TUN && t->options.topology == TOP_NET30 )
++      pool_type = IFCONFIG_POOL_30NET;
++
++      m->ifconfig_pool = ifconfig_pool_init (pool_type,
++                               t->options.ifconfig_pool_start,
++                               t->options.ifconfig_pool_end,
++                               t->options.duplicate_cn,
++                               t->options.ifconfig_ipv6_pool_defined,
++                               t->options.ifconfig_ipv6_pool_base,
++                               t->options.ifconfig_ipv6_pool_netbits );
+       /* reload pool data from file */
+       if (t->c1.ifconfig_pool_persist)
+@@ -429,10 +422,14 @@
+                  struct multi_instance *mi)
+ {
+   const struct iroute *ir;
++  const struct iroute_ipv6 *ir6;
+   if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN)
+     {
+       for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
+       mroute_helper_del_iroute (m->route_helper, ir);
++
++      for ( ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next )
++      mroute_helper_del_iroute6 (m->route_helper, ir6);
+     }
+ }
+@@ -1078,6 +1075,37 @@
+   }
+ }
++static struct multi_instance *
++multi_learn_in6_addr  (struct multi_context *m,
++                     struct multi_instance *mi,
++                     struct in6_addr a6,
++                     int netbits, /* -1 if host route, otherwise # of network bits in address */
++                     bool primary)
++{
++  struct mroute_addr addr;
++
++  addr.len = 16;
++  addr.type = MR_ADDR_IPV6;
++  addr.netbits = 0;
++  memcpy( &addr.addr, &a6, sizeof(a6) );
++
++  if (netbits >= 0)
++    {
++      addr.type |= MR_WITH_NETBITS;
++      addr.netbits = (uint8_t) netbits;
++      mroute_addr_mask_host_bits( &addr );
++    }
++
++  {
++    struct multi_instance *owner = multi_learn_addr (m, mi, &addr, 0);
++#ifdef MANAGEMENT_DEF_AUTH
++    if (management && owner)
++      management_learn_addr (management, &mi->context.c2.mda_context, &addr, primary);
++#endif
++    return owner;
++  }
++}
++
+ /*
+  * A new client has connected, add routes (server -> client)
+  * to internal routing table.
+@@ -1088,6 +1116,7 @@
+ {
+   struct gc_arena gc = gc_new ();
+   const struct iroute *ir;
++  const struct iroute_ipv6 *ir6;
+   if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN)
+     {
+       mi->did_iroutes = true;
+@@ -1107,6 +1136,22 @@
+       
+         multi_learn_in_addr_t (m, mi, ir->network, ir->netbits, false);
+       }
++      for ( ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next )
++      {
++        if (ir6->netbits >= 0)
++          msg (D_MULTI_LOW, "MULTI: internal route %s/%d -> %s",
++               print_in6_addr (ir6->network, 0, &gc),
++               ir6->netbits,
++               multi_instance_string (mi, false, &gc));
++        else
++          msg (D_MULTI_LOW, "MULTI: internal route %s -> %s",
++               print_in6_addr (ir6->network, 0, &gc),
++               multi_instance_string (mi, false, &gc));
++
++        mroute_helper_add_iroute6 (m->route_helper, ir6);
++      
++        multi_learn_in6_addr (m, mi, ir6->network, ir6->netbits, false);
++      }
+     }
+   gc_free (&gc);
+ }
+@@ -1192,21 +1237,37 @@
+       mi->context.c2.push_ifconfig_defined = true;
+       mi->context.c2.push_ifconfig_local = mi->context.options.push_ifconfig_local;
+       mi->context.c2.push_ifconfig_remote_netmask = mi->context.options.push_ifconfig_remote_netmask;
++
++      /* the current implementation does not allow "static IPv4, pool IPv6",
++       * (see below) so issue a warning if that happens - don't break the
++       * session, though, as we don't even know if this client WANTS IPv6
++       */
++      if ( mi->context.c1.tuntap->ipv6 &&
++         mi->context.options.ifconfig_ipv6_pool_defined &&
++         ! mi->context.options.push_ifconfig_ipv6_defined )
++      {
++        msg( M_INFO, "MULTI_sva: WARNING: if --ifconfig-push is used for IPv4, automatic IPv6 assignment from --ifconfig-ipv6-pool does not work.  Use --ifconfig-ipv6-push for IPv6 then." );
++      }
+     }
+   else if (m->ifconfig_pool && mi->vaddr_handle < 0) /* otherwise, choose a pool address */
+     {
+       in_addr_t local=0, remote=0;
++      struct in6_addr remote_ipv6;
+       const char *cn = NULL;
+       if (!mi->context.options.duplicate_cn)
+       cn = tls_common_name (mi->context.c2.tls_multi, true);
+-      mi->vaddr_handle = ifconfig_pool_acquire (m->ifconfig_pool, &local, &remote, cn);
++      mi->vaddr_handle = ifconfig_pool_acquire (m->ifconfig_pool, &local, &remote, &remote_ipv6, cn);
+       if (mi->vaddr_handle >= 0)
+       {
+         const int tunnel_type = TUNNEL_TYPE (mi->context.c1.tuntap);
+         const int tunnel_topology = TUNNEL_TOPOLOGY (mi->context.c1.tuntap);
++        msg( M_INFO, "MULTI_sva: pool returned IPv4=%s, IPv6=%s", 
++                  print_in_addr_t( remote, 0, &gc ),
++                  print_in6_addr( remote_ipv6, 0, &gc ) );
++
+         /* set push_ifconfig_remote_netmask from pool ifconfig address(es) */
+         mi->context.c2.push_ifconfig_local = remote;
+         if (tunnel_type == DEV_TYPE_TAP || (tunnel_type == DEV_TYPE_TUN && tunnel_topology == TOP_SUBNET))
+@@ -1228,12 +1289,46 @@
+         else
+           msg (D_MULTI_ERRORS, "MULTI: no --ifconfig-pool netmask parameter is available to push to %s",
+                multi_instance_string (mi, false, &gc));
++
++        if ( mi->context.options.ifconfig_ipv6_pool_defined )
++          {
++            mi->context.c2.push_ifconfig_ipv6_local = remote_ipv6;
++            mi->context.c2.push_ifconfig_ipv6_remote = 
++                  mi->context.c1.tuntap->local_ipv6;
++            mi->context.c2.push_ifconfig_ipv6_netbits = 
++                  mi->context.options.ifconfig_ipv6_pool_netbits;
++            mi->context.c2.push_ifconfig_ipv6_defined = true;
++          }
+       }
+       else
+       {
+         msg (D_MULTI_ERRORS, "MULTI: no free --ifconfig-pool addresses are available");
+       }
+     }
++
++  /* IPv6 push_ifconfig is a bit problematic - since IPv6 shares the 
++   * pool handling with IPv4, the combination "static IPv4, dynamic IPv6"
++   * will fail (because no pool will be allocated in this case).
++   * OTOH, this doesn't make too much sense in reality - and the other
++   * way round ("dynamic IPv4, static IPv6") or "both static" makes sense
++   * -> and so it's implemented right now
++   */
++  if ( mi->context.c1.tuntap->ipv6 &&
++       mi->context.options.push_ifconfig_ipv6_defined )
++    {
++      mi->context.c2.push_ifconfig_ipv6_local = 
++          mi->context.options.push_ifconfig_ipv6_local;
++      mi->context.c2.push_ifconfig_ipv6_remote = 
++          mi->context.options.push_ifconfig_ipv6_remote;
++      mi->context.c2.push_ifconfig_ipv6_netbits = 
++          mi->context.options.push_ifconfig_ipv6_netbits;
++      mi->context.c2.push_ifconfig_ipv6_defined = true;
++
++      msg( M_INFO, "MULTI_sva: push_ifconfig_ipv6 %s/%d", 
++          print_in6_addr( mi->context.c2.push_ifconfig_ipv6_local, 0, &gc ),
++          mi->context.c2.push_ifconfig_ipv6_netbits );
++    }
++
+   gc_free (&gc);
+ }
+@@ -1272,6 +1367,11 @@
+                           SA_SET_IF_NONZERO);
+       }
+     }
++
++    /* TODO: I'm not exactly sure what these environment variables are
++     *       used for, but if we have them for IPv4, we should also have
++     *       them for IPv6, no?
++     */
+ }
+ /*
+@@ -1661,6 +1761,15 @@
+                      print_in_addr_t (mi->context.c2.push_ifconfig_local, 0, &gc));
+               }
++            if (mi->context.c2.push_ifconfig_ipv6_defined)
++              {
++                multi_learn_in6_addr (m, mi, mi->context.c2.push_ifconfig_ipv6_local, -1, true);
++                /* TODO: find out where addresses are "unlearned"!! */
++                msg (D_MULTI_LOW, "MULTI: primary virtual IPv6 for %s: %s",
++                     multi_instance_string (mi, false, &gc),
++                     print_in6_addr (mi->context.c2.push_ifconfig_ipv6_local, 0, &gc));
++              }
++
+             /* add routes locally, pointing to new client, if
+                --iroute options have been specified */
+             multi_add_iroutes (m, mi);
+diff -durN openvpn-2.2.2.orig/openvpn.8 openvpn-2.2.2/openvpn.8
+--- openvpn-2.2.2.orig/openvpn.8       2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/openvpn.8    2012-06-01 10:40:28.000000000 +0200
+@@ -794,6 +794,8 @@
+ .B \-\-dev tunX.
+ A warning will be displayed
+ if no specific IPv6 TUN support for your OS has been compiled into OpenVPN.
++
++See below for further IPv6-related configuration options.
+ .\"*********************************************************
+ .TP
+ .B \-\-dev-node node
+@@ -4949,6 +4951,57 @@
+ .B \-\-verb
+ option can be used BEFORE this option to produce debugging information.
+ .\"*********************************************************
++.SS IPv6 Related Options
++.\"*********************************************************
++The following options exist to support IPv6 tunneling in peer-to-peer
++and client-server mode.  As of now, this is just very basic
++documentation of the IPv6-related options. More documentation can be
++found on http://www.greenie.net/ipv6/openvpn.html.
++.TP
++.B --ifconfig-ipv6 ipv6addr/bits ipv6remote
++configure IPv6 address
++.B ipv6addr/bits
++on the ``tun'' device.  The second parameter is used as route target for
++.B --route-ipv6
++if no gateway is specified.
++.TP
++.B --route-ipv6 ipv6addr/bits [gateway] [metric]
++setup IPv6 routing in the system to send the specified IPv6 network
++into OpenVPN's ``tun'' device
++.TP
++.B --server-ipv6 ipv6addr/bits
++convenience-function to enable a number of IPv6 related options at
++once, namely
++.B --ifconfig-ipv6, --ifconfig-ipv6-pool, --tun-ipv6
++and
++.B --push tun-ipv6
++Is only accepted if ``--mode server'' or ``--server'' is set.
++.TP
++.B --ifconfig-ipv6-pool ipv6addr/bits
++Specify an IPv6 address pool for dynamic assignment to clients.  The
++pool starts at
++.B ipv6addr
++and increments by +1 for every new client (linear mode).  The
++.B /bits
++setting controls the size of the pool.
++.TP
++.B --ifconfig-ipv6-push ipv6addr/bits ipv6remote
++for ccd/ per-client static IPv6 interface configuration, see
++.B --client-config-dir
++and
++.B --ifconfig-push
++for more details.
++.TP
++.B --iroute-ipv6 ipv6addr/bits
++for ccd/ per-client static IPv6 route configuration, see
++.B --iroute
++for more details how to setup and use this, and how
++.B --iroute
++and
++.B --route
++interact.
++
++.\"*********************************************************
+ .SH SCRIPTING AND ENVIRONMENTAL VARIABLES
+ OpenVPN exports a series
+ of environmental variables for use by user-defined scripts.
+diff -durN openvpn-2.2.2.orig/openvpn.h openvpn-2.2.2/openvpn.h
+--- openvpn-2.2.2.orig/openvpn.h       2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/openvpn.h    2012-06-01 10:40:28.000000000 +0200
+@@ -165,6 +165,9 @@
+   /* list of --route directives */
+   struct route_list *route_list;
++  /* list of --route-ipv6 directives */
++  struct route_ipv6_list *route_ipv6_list;
++
+   /* --status file */
+   struct status_output *status_output;
+   bool status_output_owned;
+@@ -417,6 +420,11 @@
+   in_addr_t push_ifconfig_local;
+   in_addr_t push_ifconfig_remote_netmask;
++  bool            push_ifconfig_ipv6_defined;
++  struct in6_addr push_ifconfig_ipv6_local;
++  int             push_ifconfig_ipv6_netbits;
++  struct in6_addr push_ifconfig_ipv6_remote;
++
+   /* client authentication state, CAS_SUCCEEDED must be 0 */
+ # define CAS_SUCCEEDED 0
+ # define CAS_PENDING   1
+diff -durN openvpn-2.2.2.orig/options.c openvpn-2.2.2/options.c
+--- openvpn-2.2.2.orig/options.c       2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/options.c    2012-06-01 10:40:28.000000000 +0200
+@@ -79,6 +79,7 @@
+ #ifdef ENABLE_EUREPHIA
+   " [eurephia]"
+ #endif
++  " [IPv6 payload 20110522-1 (2.2.0)]"
+   " built on " __DATE__
+ ;
+@@ -172,6 +173,8 @@
+   "                  addresses outside of the subnets used by either peer.\n"
+   "                  TAP: configure device to use IP address l as a local\n"
+   "                  endpoint and rn as a subnet mask.\n"
++  "--ifconfig-ipv6 l r : configure device to use IPv6 address l as local\n"
++  "                      endpoint (as a /64) and r as remote endpoint\n"
+   "--ifconfig-noexec : Don't actually execute ifconfig/netsh command, instead\n"
+   "                    pass --ifconfig parms by environment to scripts.\n"
+   "--ifconfig-nowarn : Don't warn if the --ifconfig option on this side of the\n"
+@@ -182,6 +185,10 @@
+   "                  netmask default: 255.255.255.255\n"
+   "                  gateway default: taken from --route-gateway or --ifconfig\n"
+   "                  Specify default by leaving blank or setting to \"nil\".\n"
++  "--route-ipv6 network/bits [gateway] [metric] :\n"
++  "                  Add IPv6 route to routing table after connection\n"
++  "                  is established.  Multiple routes can be specified.\n"
++  "                  gateway default: taken from --route-ipv6-gateway or --ifconfig\n"
+   "--max-routes n :  Specify the maximum number of routes that may be defined\n"
+   "                  or pulled from a server.\n"
+   "--route-gateway gw|'dhcp' : Specify a default gateway for use with --route.\n"
+@@ -370,6 +377,7 @@
+   "\n"
+   "Multi-Client Server options (when --mode server is used):\n"
+   "--server network netmask : Helper option to easily configure server mode.\n"
++  "--server-ipv6 network/bits : Configure IPv6 server mode.\n"
+   "--server-bridge [IP netmask pool-start-IP pool-end-IP] : Helper option to\n"
+   "                    easily configure ethernet bridging server mode.\n"
+   "--push \"option\" : Push a config file option back to the peer for remote\n"
+@@ -383,10 +391,16 @@
+   "--ifconfig-pool-persist file [seconds] : Persist/unpersist ifconfig-pool\n"
+   "                  data to file, at seconds intervals (default=600).\n"
+   "                  If seconds=0, file will be treated as read-only.\n"
++  "--ifconfig-ipv6-pool base-IP/bits : set aside an IPv6 network block\n"
++  "                  to be dynamically allocated to connecting clients.\n"
+   "--ifconfig-push local remote-netmask : Push an ifconfig option to remote,\n"
+   "                  overrides --ifconfig-pool dynamic allocation.\n"
+   "                  Only valid in a client-specific config file.\n"
++  "--ifconfig-ipv6-push local/bits remote : Push an ifconfig-ipv6 option to\n"
++  "                  remote, overrides --ifconfig-ipv6-pool allocation.\n"
++  "                  Only valid in a client-specific config file.\n"
+   "--iroute network [netmask] : Route subnet to client.\n"
++  "--iroute-ipv6 network/bits : Route IPv6 subnet to client.\n"
+   "                  Sets up internal routes only.\n"
+   "                  Only valid in a client-specific config file.\n"
+   "--disable       : Client is disabled.\n"
+@@ -871,6 +885,78 @@
+   return ret;
+ }
++/* helper: parse a text string containing an IPv6 address + netbits
++ * in "standard format" (2001:dba::/32)
++ * "/nn" is optional, default to /64 if missing
++ *
++ * return true if parsing succeeded, modify *network and *netbits
++ * return address part without "/nn" in *printable_ipv6 (if != NULL)
++ */
++bool
++get_ipv6_addr( const char * prefix_str, struct in6_addr *network,
++             unsigned int * netbits, char ** printable_ipv6, int msglevel )
++{
++    int rc;
++    char * sep, * endp;
++    int bits;
++    struct in6_addr t_network;
++
++    sep = strchr( prefix_str, '/' );
++    if ( sep == NULL )
++      {
++      bits = 64;
++      }
++    else
++      {
++      bits = strtol( sep+1, &endp, 10 );
++      if ( *endp != '\0' || bits < 0 || bits > 128 )
++        {
++          msg (msglevel, "IPv6 prefix '%s': invalid '/bits' spec", prefix_str);
++          return false;
++        }
++      }
++
++    /* temporary replace '/' in caller-provided string with '\0', otherwise
++     * inet_pton() will refuse prefix string
++     * (alternative would be to strncpy() the prefix to temporary buffer)
++     */
++
++    if ( sep != NULL ) *sep = '\0';
++
++    rc = inet_pton( AF_INET6, prefix_str, &t_network );
++
++    if ( rc == 1 && printable_ipv6 != NULL )
++      {
++      *printable_ipv6 = string_alloc( prefix_str, NULL );
++      }
++
++    if ( sep != NULL ) *sep = '/';
++
++    if ( rc != 1 )
++      {
++      msg (msglevel, "IPv6 prefix '%s': invalid IPv6 address", prefix_str);
++      return false;
++      }
++
++    if ( netbits != NULL )
++      {
++      *netbits = bits;
++      }
++    if ( network != NULL )
++      {
++      *network = t_network;
++      }
++    return true;              /* parsing OK, values set */
++}
++
++static bool ipv6_addr_safe_hexplusbits( const char * ipv6_prefix_spec )
++{
++    struct in6_addr t_addr;
++    unsigned int t_bits;
++
++    return get_ipv6_addr( ipv6_prefix_spec, &t_addr, &t_bits, NULL, M_WARN );
++}
++
+ static char *
+ string_substitute (const char *src, int from, int to, struct gc_arena *gc)
+ {
+@@ -989,6 +1075,8 @@
+ #if P2MP_SERVER
+   msg (D_SHOW_PARMS, "  server_network = %s", print_in_addr_t (o->server_network, 0, &gc));
+   msg (D_SHOW_PARMS, "  server_netmask = %s", print_in_addr_t (o->server_netmask, 0, &gc));
++  msg (D_SHOW_PARMS, "  server_network_ipv6 = %s", print_in6_addr (o->server_network_ipv6, 0, &gc) );
++  SHOW_INT (server_netbits_ipv6);
+   msg (D_SHOW_PARMS, "  server_bridge_ip = %s", print_in_addr_t (o->server_bridge_ip, 0, &gc));
+   msg (D_SHOW_PARMS, "  server_bridge_netmask = %s", print_in_addr_t (o->server_bridge_netmask, 0, &gc));
+   msg (D_SHOW_PARMS, "  server_bridge_pool_start = %s", print_in_addr_t (o->server_bridge_pool_start, 0, &gc));
+@@ -1009,6 +1097,9 @@
+   msg (D_SHOW_PARMS, "  ifconfig_pool_netmask = %s", print_in_addr_t (o->ifconfig_pool_netmask, 0, &gc));
+   SHOW_STR (ifconfig_pool_persist_filename);
+   SHOW_INT (ifconfig_pool_persist_refresh_freq);
++  SHOW_BOOL (ifconfig_ipv6_pool_defined);
++  msg (D_SHOW_PARMS, "  ifconfig_ipv6_pool_base = %s", print_in6_addr (o->ifconfig_ipv6_pool_base, 0, &gc));
++  SHOW_INT (ifconfig_ipv6_pool_netbits);
+   SHOW_INT (n_bcast_buf);
+   SHOW_INT (tcp_queue_limit);
+   SHOW_INT (real_hash_size);
+@@ -1022,6 +1113,9 @@
+   SHOW_BOOL (push_ifconfig_defined);
+   msg (D_SHOW_PARMS, "  push_ifconfig_local = %s", print_in_addr_t (o->push_ifconfig_local, 0, &gc));
+   msg (D_SHOW_PARMS, "  push_ifconfig_remote_netmask = %s", print_in_addr_t (o->push_ifconfig_remote_netmask, 0, &gc));
++  SHOW_BOOL (push_ifconfig_ipv6_defined);
++  msg (D_SHOW_PARMS, "  push_ifconfig_ipv6_local = %s/%d", print_in6_addr (o->push_ifconfig_ipv6_local, 0, &gc), o->push_ifconfig_ipv6_netbits );
++  msg (D_SHOW_PARMS, "  push_ifconfig_ipv6_remote = %s", print_in6_addr (o->push_ifconfig_ipv6_remote, 0, &gc));
+   SHOW_BOOL (enable_c2c);
+   SHOW_BOOL (duplicate_cn);
+   SHOW_INT (cf_max);
+@@ -1076,6 +1170,25 @@
+   o->iroutes = ir;
+ }
++static void
++option_iroute_ipv6 (struct options *o,
++             const char *prefix_str,
++             int msglevel)
++{
++  struct iroute_ipv6 *ir;
++
++  ALLOC_OBJ_GC (ir, struct iroute_ipv6, &o->gc);
++
++  if ( get_ipv6_addr (prefix_str, &ir->network, &ir->netbits, NULL, msglevel ) < 0 )
++    {
++      msg (msglevel, "in --iroute-ipv6 %s: Bad IPv6 prefix specification",
++         prefix_str);
++      return;
++    }
++
++  ir->next = o->iroutes_ipv6;
++  o->iroutes_ipv6 = ir;
++}
+ #endif /* P2MP_SERVER */
+ #endif /* P2MP */
+@@ -1113,6 +1226,13 @@
+     options->routes = new_route_option_list (options->max_routes, &options->gc);
+ }
++void
++rol6_check_alloc (struct options *options)
++{
++  if (!options->routes_ipv6)
++    options->routes_ipv6 = new_route_ipv6_option_list (options->max_routes, &options->gc);
++}
++
+ #ifdef ENABLE_DEBUG
+ static void
+ show_connection_entry (const struct connection_entry *o)
+@@ -1203,6 +1323,9 @@
+   SHOW_STR (ifconfig_remote_netmask);
+   SHOW_BOOL (ifconfig_noexec);
+   SHOW_BOOL (ifconfig_nowarn);
++  SHOW_STR (ifconfig_ipv6_local);
++  SHOW_INT (ifconfig_ipv6_netbits);
++  SHOW_STR (ifconfig_ipv6_remote);
+ #ifdef HAVE_GETTIMEOFDAY
+   SHOW_INT (shaper);
+@@ -1863,8 +1986,10 @@
+       if (options->connection_list)
+       msg (M_USAGE, "<connection> cannot be used with --mode server");
+ #endif
++#if 0
+       if (options->tun_ipv6)
+       msg (M_USAGE, "--tun-ipv6 cannot be used with --mode server");
++#endif
+       if (options->shaper)
+       msg (M_USAGE, "--shaper cannot be used with --mode server");
+       if (options->inetd)
+@@ -1889,6 +2014,11 @@
+       msg (M_USAGE, "--up-delay cannot be used with --mode server");
+       if (!options->ifconfig_pool_defined && options->ifconfig_pool_persist_filename)
+       msg (M_USAGE, "--ifconfig-pool-persist must be used with --ifconfig-pool");
++      if (options->ifconfig_ipv6_pool_defined && !options->ifconfig_ipv6_local )
++      msg (M_USAGE, "--ifconfig-ipv6-pool needs --ifconfig-ipv6");
++      if (options->ifconfig_ipv6_local && !options->tun_ipv6 )
++      msg (M_INFO, "Warning: --ifconfig-ipv6 without --tun-ipv6 will not do IPv6");
++
+       if (options->auth_user_pass_file)
+       msg (M_USAGE, "--auth-user-pass cannot be used with --mode server (it should be used on the client side only)");
+       if (options->ccd_exclusive && !options->client_config_dir)
+@@ -1920,6 +2050,8 @@
+        */
+       if (options->ifconfig_pool_defined || options->ifconfig_pool_persist_filename)
+       msg (M_USAGE, "--ifconfig-pool/--ifconfig-pool-persist requires --mode server");
++      if (options->ifconfig_ipv6_pool_defined)
++      msg (M_USAGE, "--ifconfig-ipv6-pool requires --mode server");
+       if (options->real_hash_size != defaults.real_hash_size
+         || options->virtual_hash_size != defaults.virtual_hash_size)
+       msg (M_USAGE, "--hash-size requires --mode server");
+@@ -2461,6 +2593,8 @@
+                    o->topology,
+                    o->ifconfig_local,
+                    o->ifconfig_remote_netmask,
++                   o->ifconfig_ipv6_local,
++                   o->ifconfig_ipv6_remote,
+                    (in_addr_t)0,
+                    (in_addr_t)0,
+                    false,
+@@ -3786,6 +3920,30 @@
+         goto err;
+       }
+     }
++  else if (streq (p[0], "ifconfig-ipv6") && p[1] && p[2] )
++    {
++      unsigned int netbits;
++      char * ipv6_local;
++      
++      VERIFY_PERMISSION (OPT_P_UP);
++      if ( get_ipv6_addr( p[1], NULL, &netbits, &ipv6_local, msglevel ) &&
++           ipv6_addr_safe( p[2] ) )
++        {
++        if ( netbits < 64 || netbits > 124 )
++          {
++            msg( msglevel, "ifconfig-ipv6: /netbits must be between 64 and 124, not '/%d'", netbits );
++            goto err;
++          }
++        options->ifconfig_ipv6_local = ipv6_local;
++        options->ifconfig_ipv6_netbits = netbits;
++        options->ifconfig_ipv6_remote = p[2];
++        }
++      else
++      {
++        msg (msglevel, "ifconfig-ipv6 parms '%s' and '%s' must be valid addresses", p[1], p[2]);
++        goto err;
++      }
++    }
+   else if (streq (p[0], "ifconfig-noexec"))
+     {
+       VERIFY_PERMISSION (OPT_P_UP);
+@@ -4586,6 +4744,26 @@
+       }
+       add_route_to_option_list (options->routes, p[1], p[2], p[3], p[4]);
+     }
++  else if (streq (p[0], "route-ipv6") && p[1])
++    {
++      VERIFY_PERMISSION (OPT_P_ROUTE);
++      rol6_check_alloc (options);
++      if (pull_mode)
++      {
++        if (!ipv6_addr_safe_hexplusbits (p[1]))
++          {
++            msg (msglevel, "route-ipv6 parameter network/IP '%s' must be a valid address", p[1]);
++            goto err;
++          }
++        if (p[2] && !ipv6_addr_safe (p[2]))
++          {
++            msg (msglevel, "route-ipv6 parameter gateway '%s' must be a valid address", p[2]);
++            goto err;
++          }
++        /* p[3] is metric, if present */
++      }
++      add_route_ipv6_to_option_list (options->routes_ipv6, p[1], p[2], p[3]);
++    }
+   else if (streq (p[0], "max-routes") && p[1])
+     {
+       int max_routes;
+@@ -4797,6 +4975,33 @@
+           }
+       }
+     }
++  else if (streq (p[0], "server-ipv6") && p[1] )
++    {
++      const int lev = M_WARN;
++      struct in6_addr network;
++      unsigned int netbits = 0;
++
++      VERIFY_PERMISSION (OPT_P_GENERAL);
++      if ( ! get_ipv6_addr (p[1], &network, &netbits, NULL, lev) )
++      {
++        msg (msglevel, "error parsing --server-ipv6 parameter");
++        goto err;
++      }
++      if ( netbits != 64 )
++      {
++        msg( msglevel, "--server-ipv6 settings: only /64 supported right now (not /%d)", netbits );
++        goto err;
++      }
++      options->server_ipv6_defined = true;
++      options->server_network_ipv6 = network;
++      options->server_netbits_ipv6 = netbits;
++
++      if (p[2])               /* no "nopool" options or similar for IPv6 */
++      {
++        msg (msglevel, "error parsing --server-ipv6: %s is not a recognized flag", p[3]);
++        goto err;
++      }
++    }
+   else if (streq (p[0], "server-bridge") && p[1] && p[2] && p[3] && p[4])
+     {
+       const int lev = M_WARN;
+@@ -4881,6 +5086,28 @@
+       VERIFY_PERMISSION (OPT_P_GENERAL);
+       options->topology = TOP_P2P;
+     }
++  else if (streq (p[0], "ifconfig-ipv6-pool") && p[1] )
++    {
++      const int lev = M_WARN;
++      struct in6_addr network;
++      unsigned int netbits = 0;
++
++      VERIFY_PERMISSION (OPT_P_GENERAL);
++      if ( ! get_ipv6_addr (p[1], &network, &netbits, NULL, lev ) )
++      {
++        msg (msglevel, "error parsing --ifconfig-ipv6-pool parameters");
++        goto err;
++      }
++      if ( netbits != 64 )
++      {
++        msg( msglevel, "--ifconfig-ipv6-pool settings: only /64 supported right now (not /%d)", netbits );
++        goto err;
++      }
++
++      options->ifconfig_ipv6_pool_defined = true;
++      options->ifconfig_ipv6_pool_base = network;
++      options->ifconfig_ipv6_pool_netbits = netbits;
++    }
+   else if (streq (p[0], "hash-size") && p[1] && p[2])
+     {
+       int real, virtual;
+@@ -5076,6 +5303,11 @@
+       }
+       option_iroute (options, p[1], netmask, msglevel);
+     }
++  else if (streq (p[0], "iroute-ipv6") && p[1])
++    {
++      VERIFY_PERMISSION (OPT_P_INSTANCE);
++      option_iroute_ipv6 (options, p[1], msglevel);
++    }
+   else if (streq (p[0], "ifconfig-push") && p[1] && p[2])
+     {
+       in_addr_t local, remote_netmask;
+@@ -5114,6 +5346,43 @@
+         goto err;
+       }
+     }
++  else if (streq (p[0], "ifconfig-ipv6-push") && p[1] )
++    {
++      struct in6_addr local, remote;
++      unsigned int netbits;
++
++      VERIFY_PERMISSION (OPT_P_INSTANCE);
++
++      if ( ! get_ipv6_addr( p[1], &local, &netbits, NULL, msglevel ) )
++      {
++        msg (msglevel, "cannot parse --ifconfig-ipv6-push addresses");
++        goto err;
++      }
++
++      if ( p[2] )
++      {
++        if ( !get_ipv6_addr( p[2], &remote, NULL, NULL, msglevel ) )
++          {
++            msg( msglevel, "cannot parse --ifconfig-ipv6-push addresses");
++            goto err;
++          }
++      }
++      else
++      {
++        if ( ! options->ifconfig_ipv6_local ||
++             ! get_ipv6_addr( options->ifconfig_ipv6_local, &remote, 
++                              NULL, NULL, msglevel ) )
++          {
++            msg( msglevel, "second argument to --ifconfig-ipv6-push missing and no global --ifconfig-ipv6 address set");
++            goto err;
++          }
++      }
++
++      options->push_ifconfig_ipv6_defined = true;
++      options->push_ifconfig_ipv6_local = local;
++      options->push_ifconfig_ipv6_netbits = netbits;
++      options->push_ifconfig_ipv6_remote = remote;
++    }
+   else if (streq (p[0], "disable"))
+     {
+       VERIFY_PERMISSION (OPT_P_INSTANCE);
+diff -durN openvpn-2.2.2.orig/options.h openvpn-2.2.2/options.h
+--- openvpn-2.2.2.orig/options.h       2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/options.h    2012-06-01 10:44:37.000000000 +0200
+@@ -205,6 +205,9 @@
+   int topology; /* one of the TOP_x values from proto.h */
+   const char *ifconfig_local;
+   const char *ifconfig_remote_netmask;
++  const char *ifconfig_ipv6_local;
++  int         ifconfig_ipv6_netbits;
++  const char *ifconfig_ipv6_remote;
+   bool ifconfig_noexec;
+   bool ifconfig_nowarn;
+ #ifdef HAVE_GETTIMEOFDAY
+@@ -326,6 +329,7 @@
+   bool route_delay_defined;
+   int max_routes;
+   struct route_option_list *routes;
++  struct route_ipv6_option_list *routes_ipv6;                 /* IPv6 */
+   bool route_nopull;
+   bool route_gateway_via_dhcp;
+   bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
+@@ -363,6 +367,9 @@
+   bool server_defined;
+   in_addr_t server_network;
+   in_addr_t server_netmask;
++  bool server_ipv6_defined;                           /* IPv6 */
++  struct in6_addr server_network_ipv6;                        /* IPv6 */
++  unsigned int    server_netbits_ipv6;                        /* IPv6 */
+ # define SF_NOPOOL (1<<0)
+ # define SF_TCP_NODELAY_HELPER (1<<1)
+@@ -384,6 +391,11 @@
+   in_addr_t ifconfig_pool_netmask;
+   const char *ifconfig_pool_persist_filename;
+   int ifconfig_pool_persist_refresh_freq;
++
++  bool   ifconfig_ipv6_pool_defined;                  /* IPv6 */
++  struct in6_addr ifconfig_ipv6_pool_base;            /* IPv6 */
++  int    ifconfig_ipv6_pool_netbits;                  /* IPv6 */
++
+   int real_hash_size;
+   int virtual_hash_size;
+   const char *client_connect_script;
+@@ -395,12 +407,17 @@
+   int n_bcast_buf;
+   int tcp_queue_limit;
+   struct iroute *iroutes;
++  struct iroute_ipv6 *iroutes_ipv6;                   /* IPv6 */
+   bool push_ifconfig_defined;
+   in_addr_t push_ifconfig_local;
+   in_addr_t push_ifconfig_remote_netmask;
+   bool push_ifconfig_constraint_defined;
+   in_addr_t push_ifconfig_constraint_network;
+   in_addr_t push_ifconfig_constraint_netmask;
++  bool            push_ifconfig_ipv6_defined;         /* IPv6 */
++  struct in6_addr push_ifconfig_ipv6_local;           /* IPv6 */
++  int                   push_ifconfig_ipv6_netbits;           /* IPv6 */
++  struct in6_addr push_ifconfig_ipv6_remote;          /* IPv6 */
+   bool enable_c2c;
+   bool duplicate_cn;
+   int cf_max;
+@@ -723,6 +740,10 @@
+                           unsigned int *option_types_found,
+                           struct env_set *es);
++bool get_ipv6_addr( const char * prefix_str, struct in6_addr *network,
++                  unsigned int * netbits, char ** printable_ipv6, 
++                  int msglevel );
++
+ /*
+  * inline functions
+  */
+diff -durN openvpn-2.2.2.orig/options.h~ openvpn-2.2.2/options.h~
+--- openvpn-2.2.2.orig/options.h~      1970-01-01 01:00:00.000000000 +0100
++++ openvpn-2.2.2/options.h~   2012-06-01 10:44:29.000000000 +0200
+@@ -0,0 +1,780 @@
++/*
++ *  OpenVPN -- An application to securely tunnel IP networks
++ *             over a single UDP port, with support for SSL/TLS-based
++ *             session authentication and key exchange,
++ *             packet encryption, packet authentication, and
++ *             packet compression.
++ *
++ *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
++ *
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License version 2
++ *  as published by the Free Software Foundation.
++ *
++ *  This program is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with this program (see the file COPYING included with this
++ *  distribution); if not, write to the Free Software Foundation, Inc.,
++ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++/*
++ * 2004-01-28: Added Socks5 proxy support
++ *   (Christof Meerwald, http://cmeerw.org)
++ */
++
++#ifndef OPTIONS_H
++#define OPTIONS_H
++
++#include "basic.h"
++#include "common.h"
++#include "mtu.h"
++#include "route.h"
++#include "tun.h"
++#include "socket.h"
++#include "plugin.h"
++#include "manage.h"
++#include "proxy.h"
++#include "lzo.h"
++#include "pushlist.h"
++
++/*
++ * Maximum number of parameters associated with an option,
++ * including the option name itself.
++ */
++#define MAX_PARMS 16
++
++/*
++ * Max size of options line and parameter.
++ */
++#define OPTION_PARM_SIZE 256
++#define OPTION_LINE_SIZE 256
++
++extern const char title_string[];
++
++#if P2MP
++
++/* certain options are saved before --pull modifications are applied */
++struct options_pre_pull
++{
++  bool tuntap_options_defined;
++  struct tuntap_options tuntap_options;
++
++  bool routes_defined;
++  struct route_option_list *routes;
++
++  int foreign_option_index;
++};
++
++#endif
++
++struct connection_entry
++{
++  int proto;
++  int local_port;
++  bool local_port_defined;
++  int remote_port;
++  bool port_option_used;
++  const char *local;
++  const char *remote;
++  bool remote_float;
++  bool bind_defined;
++  bool bind_local;
++  int connect_retry_seconds;
++  bool connect_retry_defined;
++  int connect_retry_max;
++  int connect_timeout;
++  bool connect_timeout_defined;
++#ifdef ENABLE_HTTP_PROXY
++  struct http_proxy_options *http_proxy_options;
++#endif  
++#ifdef ENABLE_SOCKS
++  const char *socks_proxy_server;
++  int socks_proxy_port;
++  const char *socks_proxy_authfile;
++  bool socks_proxy_retry;
++#endif
++
++# define CE_DISABLED (1<<0)
++#if HTTP_PROXY_FALLBACK
++# define CE_HTTP_PROXY_FALLBACK (1<<1)
++  time_t ce_http_proxy_fallback_timestamp; /* time when fallback http_proxy_options was last updated */
++#endif
++
++  unsigned int flags;
++};
++
++struct remote_entry
++{
++  const char *remote;
++  int remote_port;
++  int proto;
++};
++
++#ifdef ENABLE_CONNECTION
++
++#define CONNECTION_LIST_SIZE 64
++
++struct connection_list
++{
++  int len;
++  int current;
++  int n_cycles;
++  bool no_advance;
++  struct connection_entry *array[CONNECTION_LIST_SIZE];
++};
++
++struct remote_list
++{
++  int len;
++  struct remote_entry *array[CONNECTION_LIST_SIZE];
++};
++
++#endif
++
++#if HTTP_PROXY_FALLBACK
++struct hpo_store
++{
++  struct http_proxy_options hpo;
++  char server[80];
++};
++#endif
++
++/* Command line options */
++struct options
++{
++  struct gc_arena gc;
++  bool gc_owned;
++
++  /* first config file */
++  const char *config;
++
++  /* major mode */
++# define MODE_POINT_TO_POINT 0
++# define MODE_SERVER         1
++  int mode;
++
++  /* enable forward compatibility for post-2.1 features */
++  bool forward_compatible;
++
++  /* persist parms */
++  bool persist_config;
++  int persist_mode;
++
++#ifdef USE_CRYPTO
++  const char *key_pass_file;
++  bool show_ciphers;
++  bool show_digests;
++  bool show_engines;
++#ifdef USE_SSL
++  bool show_tls_ciphers;
++#endif
++  bool genkey;
++#endif
++
++  /* Networking parms */
++  struct connection_entry ce;
++
++#ifdef ENABLE_CONNECTION
++  char *remote_ip_hint;
++  struct connection_list *connection_list;
++  struct remote_list *remote_list;
++  bool force_connection_list;
++#endif
++
++#ifdef GENERAL_PROXY_SUPPORT
++  struct auto_proxy_info *auto_proxy_info;
++#endif
++
++#if HTTP_PROXY_FALLBACK
++  bool http_proxy_fallback;
++  struct http_proxy_options *http_proxy_override;
++  struct hpo_store *hpo_store; /* used to store dynamic proxy info given by management interface */
++#endif
++
++  bool remote_random;
++  const char *ipchange;
++  const char *dev;
++  const char *dev_type;
++  const char *dev_node;
++  const char *lladdr;
++  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
++  int shaper;
++#endif
++  int tun_mtu;           /* MTU of tun device */
++  int tun_mtu_extra;
++  bool tun_mtu_extra_defined;
++  int link_mtu;          /* MTU of device over which tunnel packets pass via TCP/UDP */
++  bool tun_mtu_defined;  /* true if user overriding parm with command line option */
++  bool link_mtu_defined; /* true if user overriding parm with command line option */
++
++  int proto_force;
++
++  /* Advanced MTU negotiation and datagram fragmentation options */
++  int mtu_discover_type; /* used if OS supports setting Path MTU discovery options on socket */
++
++#ifdef ENABLE_OCC
++  bool mtu_test;
++#endif
++
++  int fragment;                 /* internal fragmentation size */
++
++  bool mlock;
++
++  int keepalive_ping;           /* a proxy for ping/ping-restart */
++  int keepalive_timeout;
++
++  int inactivity_timeout;       /* --inactive */
++  int inactivity_minimum_bytes;
++
++  int ping_send_timeout;        /* Send a TCP/UDP ping to remote every n seconds */
++  int ping_rec_timeout;         /* Expect a TCP/UDP ping from remote at least once every n seconds */
++  bool ping_timer_remote;       /* Run ping timer only if we have a remote address */
++  bool tun_ipv6;                /* Build tun dev that supports IPv6 */
++
++# define PING_UNDEF   0
++# define PING_EXIT    1
++# define PING_RESTART 2
++  int ping_rec_timeout_action;  /* What action to take on ping_rec_timeout (exit or restart)? */
++
++#ifdef ENABLE_OCC
++  int explicit_exit_notification;  /* Explicitly tell peer when we are exiting via OCC_EXIT message */
++#endif
++
++  bool persist_tun;             /* Don't close/reopen TUN/TAP dev on SIGUSR1 or PING_RESTART */
++  bool persist_local_ip;        /* Don't re-resolve local address on SIGUSR1 or PING_RESTART */
++  bool persist_remote_ip;       /* Don't re-resolve remote address on SIGUSR1 or PING_RESTART */
++  bool persist_key;             /* Don't re-read key files on SIGUSR1 or PING_RESTART */
++
++  int mssfix;                   /* Upper bound on TCP MSS */
++  bool mssfix_default;          /* true if --mssfix was supplied without a parameter */
++
++#if PASSTOS_CAPABILITY
++  bool passtos;                  
++#endif
++
++  int resolve_retry_seconds;    /* If hostname resolve fails, retry for n seconds */
++
++  struct tuntap_options tuntap_options;
++
++  /* Misc parms */
++  const char *username;
++  const char *groupname;
++  const char *chroot_dir;
++  const char *cd_dir;
++#ifdef HAVE_SETCON
++  char *selinux_context;
++#endif
++  const char *writepid;
++  const char *up_script;
++  const char *down_script;
++  bool down_pre;
++  bool up_delay;
++  bool up_restart;
++  bool daemon;
++
++  int remap_sigusr1;
++
++  /* inetd modes defined in socket.h */
++  int inetd;
++
++  bool log;
++  bool suppress_timestamps;
++  int nice;
++  int verbosity;
++  int mute;
++
++#ifdef ENABLE_DEBUG
++  int gremlin;
++#endif
++
++  const char *status_file;
++  int status_file_version;
++  int status_file_update_freq;
++
++  /* optimize TUN/TAP/UDP writes */
++  bool fast_io;
++
++#ifdef USE_LZO
++  /* LZO_x flags from lzo.h */
++  unsigned int lzo;
++#endif
++
++  /* buffer sizes */
++  int rcvbuf;
++  int sndbuf;
++
++  /* socket flags */
++  unsigned int sockflags;
++
++  /* route management */
++  const char *route_script;
++  const char *route_default_gateway;
++  int route_default_metric;
++  bool route_noexec;
++  int route_delay;
++  int route_delay_window;
++  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 */
++
++#ifdef ENABLE_OCC
++  /* Enable options consistency check between peers */
++  bool occ;
++#endif
++
++#ifdef ENABLE_MANAGEMENT
++  const char *management_addr;
++  int management_port;
++  const char *management_user_pass;
++  int management_log_history_cache;
++  int management_echo_buffer_size;
++  int management_state_buffer_size;
++  const char *management_write_peer_info_file;
++
++  const char *management_client_user;
++  const char *management_client_group;
++
++  /* Mask of MF_ values of manage.h */
++  unsigned int management_flags;
++#endif
++
++#ifdef ENABLE_PLUGIN
++  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)
++# define SF_NO_PUSH_ROUTE_GATEWAY (1<<2)
++  unsigned int server_flags;
++
++  bool server_bridge_proxy_dhcp;
++
++  bool server_bridge_defined;
++  in_addr_t server_bridge_ip;
++  in_addr_t server_bridge_netmask;
++  in_addr_t server_bridge_pool_start;
++  in_addr_t server_bridge_pool_end;
++
++  struct push_list push_list;
++  bool ifconfig_pool_defined;
++  in_addr_t ifconfig_pool_start;
++  in_addr_t ifconfig_pool_end;
++  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 *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;
++  int cf_per;
++  int max_clients;
++  int max_routes_per_client;
++
++  const char *auth_user_pass_verify_script;
++  bool auth_user_pass_verify_script_via_file;
++  unsigned int ssl_flags; /* set to SSLF_x flags from ssl.h */
++#if PORT_SHARE
++  char *port_share_host;
++  int port_share_port;
++#endif
++#endif
++
++  bool client;
++  bool pull; /* client pull of config options from server */
++  int push_continuation;
++  const char *auth_user_pass_file;
++  struct options_pre_pull *pre_pull;
++
++  int server_poll_timeout;
++
++  int scheduled_exit_interval;
++
++#endif
++
++#ifdef USE_CRYPTO
++  /* Cipher parms */
++  const char *shared_secret_file;
++#if ENABLE_INLINE_FILES
++  const char *shared_secret_file_inline;
++#endif
++  int key_direction;
++  bool ciphername_defined;
++  const char *ciphername;
++  bool authname_defined;
++  const char *authname;
++  int keysize;
++  const char *prng_hash;
++  int prng_nonce_secret_len;
++  const char *engine;
++  bool replay;
++  bool mute_replay_warnings;
++  int replay_window;
++  int replay_time;
++  const char *packet_id_file;
++  bool use_iv;
++  bool test_crypto;
++
++#ifdef USE_SSL
++  /* TLS (control channel) parms */
++  bool tls_server;
++  bool tls_client;
++  const char *ca_file;
++  const char *ca_path;
++  const char *dh_file;
++  const char *cert_file;
++  const char *priv_key_file;
++  const char *pkcs12_file;
++  const char *cipher_list;
++  const char *tls_verify;
++  const char *tls_export_cert;
++  const char *tls_remote;
++  const char *crl_file;
++
++#if ENABLE_INLINE_FILES
++  const char *ca_file_inline;
++  const char *cert_file_inline;
++  char *priv_key_file_inline;
++  const char *dh_file_inline;
++  const char *pkcs12_file_inline; /* contains the base64 encoding of pkcs12 file */
++#endif
++
++  int ns_cert_type; /* set to 0, NS_SSL_SERVER, or NS_SSL_CLIENT */
++  unsigned remote_cert_ku[MAX_PARMS];
++  const char *remote_cert_eku;
++
++#ifdef ENABLE_PKCS11
++  const char *pkcs11_providers[MAX_PARMS];
++  unsigned pkcs11_private_mode[MAX_PARMS];
++  bool pkcs11_protected_authentication[MAX_PARMS];
++  bool pkcs11_cert_private[MAX_PARMS];
++  int pkcs11_pin_cache_period;
++  const char *pkcs11_id;
++  bool pkcs11_id_management;
++#endif
++
++#ifdef WIN32
++  const char *cryptoapi_cert;
++#endif
++
++  /* data channel key exchange method */
++  int key_method;
++
++  /* Per-packet timeout on control channel */
++  int tls_timeout;
++
++  /* Data channel key renegotiation parameters */
++  int renegotiate_bytes;
++  int renegotiate_packets;
++  int renegotiate_seconds;
++
++  /* Data channel key handshake must finalize
++     within n seconds of handshake initiation. */
++  int handshake_window;
++
++#ifdef ENABLE_X509ALTUSERNAME
++  /* Field used to be the username in X509 cert. */
++  char *x509_username_field;
++#endif
++
++  /* Old key allowed to live n seconds after new key goes active */
++  int transition_window;
++
++  /* Special authentication MAC for TLS control channel */
++  const char *tls_auth_file;          /* shared secret */
++#if ENABLE_INLINE_FILES
++  const char *tls_auth_file_inline;
++#endif
++
++  /* Allow only one session */
++  bool single_session;
++
++#ifdef ENABLE_PUSH_PEER_INFO
++  bool push_peer_info;
++#endif
++
++  bool tls_exit;
++
++#endif /* USE_SSL */
++#endif /* USE_CRYPTO */
++
++  /* special state parms */
++  int foreign_option_index;
++
++#ifdef WIN32
++  const char *exit_event_name;
++  bool exit_event_initial_state;
++  bool show_net_up;
++  int route_method;
++#endif
++};
++
++#define streq(x, y) (!strcmp((x), (y)))
++
++/*
++ * Option classes.
++ */
++#define OPT_P_GENERAL         (1<<0)
++#define OPT_P_UP              (1<<1)
++#define OPT_P_ROUTE           (1<<2)
++#define OPT_P_IPWIN32         (1<<3)
++#define OPT_P_SCRIPT          (1<<4)
++#define OPT_P_SETENV          (1<<5)
++#define OPT_P_SHAPER          (1<<6)
++#define OPT_P_TIMER           (1<<7)
++#define OPT_P_PERSIST         (1<<8)
++#define OPT_P_PERSIST_IP      (1<<9)
++#define OPT_P_COMP            (1<<10) /* TODO */
++#define OPT_P_MESSAGES        (1<<11)
++#define OPT_P_CRYPTO          (1<<12) /* TODO */
++#define OPT_P_TLS_PARMS       (1<<13) /* TODO */
++#define OPT_P_MTU             (1<<14) /* TODO */
++#define OPT_P_NICE            (1<<15)
++#define OPT_P_PUSH            (1<<16)
++#define OPT_P_INSTANCE        (1<<17)
++#define OPT_P_CONFIG          (1<<18)
++#define OPT_P_EXPLICIT_NOTIFY (1<<19)
++#define OPT_P_ECHO            (1<<20)
++#define OPT_P_INHERIT         (1<<21)
++#define OPT_P_ROUTE_EXTRAS    (1<<22)
++#define OPT_P_PULL_MODE       (1<<23)
++#define OPT_P_PLUGIN          (1<<24)
++#define OPT_P_SOCKBUF         (1<<25)
++#define OPT_P_SOCKFLAGS       (1<<26)
++#define OPT_P_CONNECTION      (1<<27)
++
++#define OPT_P_DEFAULT   (~(OPT_P_INSTANCE|OPT_P_PULL_MODE))
++
++#if P2MP
++#define PULL_DEFINED(opt) ((opt)->pull)
++#if P2MP_SERVER
++#define PUSH_DEFINED(opt) ((opt)->push_list)
++#endif
++#endif
++
++#ifndef PULL_DEFINED
++#define PULL_DEFINED(opt) (false)
++#endif
++
++#ifndef PUSH_DEFINED
++#define PUSH_DEFINED(opt) (false)
++#endif
++
++#ifdef WIN32
++#define ROUTE_OPTION_FLAGS(o) ((o)->route_method & ROUTE_METHOD_MASK)
++#else
++#define ROUTE_OPTION_FLAGS(o) (0)
++#endif
++
++#ifdef HAVE_GETTIMEOFDAY
++#define SHAPER_DEFINED(opt) ((opt)->shaper)
++#else
++#define SHAPER_DEFINED(opt) (false)
++#endif
++
++#ifdef ENABLE_PLUGIN
++#define PLUGIN_OPTION_LIST(opt) ((opt)->plugin_list)
++#else
++#define PLUGIN_OPTION_LIST(opt) (NULL)
++#endif
++
++#ifdef MANAGEMENT_DEF_AUTH
++#define MAN_CLIENT_AUTH_ENABLED(opt) ((opt)->management_flags & MF_CLIENT_AUTH)
++#else
++#define MAN_CLIENT_AUTH_ENABLED(opt) (false)
++#endif
++
++void parse_argv (struct options *options,
++               const int argc,
++               char *argv[],
++               const int msglevel,
++               const unsigned int permission_mask,
++               unsigned int *option_types_found,
++               struct env_set *es);
++
++void notnull (const char *arg, const char *description);
++
++void usage_small (void);
++
++void init_options (struct options *o, const bool init_gc);
++void uninit_options (struct options *o);
++
++void setenv_settings (struct env_set *es, const struct options *o);
++void show_settings (const struct options *o);
++
++bool string_defined_equal (const char *s1, const char *s2);
++
++#ifdef ENABLE_OCC
++
++const char *options_string_version (const char* s, struct gc_arena *gc);
++
++char *options_string (const struct options *o,
++                    const struct frame *frame,
++                    struct tuntap *tt,
++                    bool remote,
++                    struct gc_arena *gc);
++
++bool options_cmp_equal_safe (char *actual, const char *expected, size_t actual_n);
++void options_warning_safe (char *actual, const char *expected, size_t actual_n);
++bool options_cmp_equal (char *actual, const char *expected);
++void options_warning (char *actual, const char *expected);
++
++#endif
++
++void options_postprocess (struct options *options);
++
++void pre_pull_save (struct options *o);
++void pre_pull_restore (struct options *o);
++
++bool apply_push_options (struct options *options,
++                       struct buffer *buf,
++                       unsigned int permission_mask,
++                       unsigned int *option_types_found,
++                       struct env_set *es);
++
++bool is_persist_option (const struct options *o);
++bool is_stateful_restart (const struct options *o);
++
++void options_detach (struct options *o);
++
++void options_server_import (struct options *o,
++                          const char *filename,
++                          int msglevel,
++                          unsigned int permission_mask,
++                          unsigned int *option_types_found,
++                          struct env_set *es);
++
++void pre_pull_default (struct options *o);
++
++void rol_check_alloc (struct options *options);
++
++int parse_line (const char *line,
++              char *p[],
++              const int n,
++              const char *file,
++              const int line_num,
++              int msglevel,
++              struct gc_arena *gc);
++
++/*
++ * parse/print topology coding
++ */
++
++int parse_topology (const char *str, const int msglevel);
++const char *print_topology (const int topology);
++
++/*
++ * Manage auth-retry variable
++ */
++
++#if P2MP
++
++#define AR_NONE       0
++#define AR_INTERACT   1
++#define AR_NOINTERACT 2
++
++int auth_retry_get (void);
++bool auth_retry_set (const int msglevel, const char *option);
++const char *auth_retry_print (void);
++
++#endif
++
++void options_string_import (struct options *options,
++                          const char *config,
++                          const int msglevel,
++                          const unsigned int permission_mask,
++                          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
++ */
++static inline bool
++connection_list_defined (const struct options *o)
++{
++#ifdef ENABLE_CONNECTION
++  return o->connection_list != NULL;
++#else
++  return false;
++#endif
++}
++
++static inline void
++connection_list_set_no_advance (struct options *o)
++{
++#ifdef ENABLE_CONNECTION
++  if (o->connection_list)
++    o->connection_list->no_advance = true;
++#endif
++}
++
++#if HTTP_PROXY_FALLBACK
++
++struct http_proxy_options *
++parse_http_proxy_fallback (struct context *c,
++                         const char *server,
++                         const char *port,
++                         const char *flags,
++                         const int msglevel);
++
++#endif /* HTTP_PROXY_FALLBACK */
++
++#endif
+diff -durN openvpn-2.2.2.orig/pool.c openvpn-2.2.2/pool.c
+--- openvpn-2.2.2.orig/pool.c  2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/pool.c       2012-06-01 10:40:28.000000000 +0200
+@@ -132,7 +132,10 @@
+ }
+ struct ifconfig_pool *
+-ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn)
++ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, 
++                  const bool duplicate_cn,
++                  const bool ipv6_pool, const struct in6_addr ipv6_base, 
++                  const int ipv6_netbits )
+ {
+   struct gc_arena gc = gc_new ();
+   struct ifconfig_pool *pool = NULL;
+@@ -157,11 +160,31 @@
+       ASSERT (0);
+     }
++  /* IPv6 pools are always "INDIV" type */
++  pool->ipv6 = ipv6_pool;
++
++  if ( pool->ipv6 )
++    {
++      pool->base_ipv6 = ipv6_base;
++      pool->size_ipv6 = ipv6_netbits>96? ( 1<<(128-ipv6_netbits) ) 
++                                     : IFCONFIG_POOL_MAX;
++
++      msg( D_IFCONFIG_POOL, "IFCONFIG POOL IPv6: (IPv4) size=%d, size_ipv6=%d, netbits=%d, base_ipv6=%s",
++                          pool->size, pool->size_ipv6, ipv6_netbits,
++                          print_in6_addr( pool->base_ipv6, 0, &gc ));
++
++      /* the current code is very simple and assumes that the IPv6
++       * pool is at least as big as the IPv4 pool, and we don't need
++       * to do separate math etc. for IPv6
++       */
++      ASSERT( pool->size < pool->size_ipv6 );
++    }
++
+   ALLOC_ARRAY_CLEAR (pool->list, struct ifconfig_pool_entry, pool->size);
+-  msg (D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d",
++  msg (D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d, ipv6=%d",
+        print_in_addr_t (pool->base, 0, &gc),
+-       pool->size);
++       pool->size, pool->ipv6 );
+   gc_free (&gc);
+   return pool;
+@@ -181,7 +204,7 @@
+ }
+ ifconfig_pool_handle
+-ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, const char *common_name)
++ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, struct in6_addr *remote_ipv6, const char *common_name)
+ {
+   int i;
+@@ -214,6 +237,12 @@
+       default:
+         ASSERT (0);
+       }
++
++      /* IPv6 pools are always INDIV (--linear) */
++      if ( pool->ipv6 && remote_ipv6 )
++      {
++        *remote_ipv6 = add_in6_addr( pool->base_ipv6, i );
++      }
+     }
+   return i;
+ }
+@@ -288,6 +317,19 @@
+   return ret;
+ }
++static struct in6_addr
++ifconfig_pool_handle_to_ipv6_base (const struct ifconfig_pool* pool, ifconfig_pool_handle hand)
++{
++  struct in6_addr ret = in6addr_any;
++
++  /* IPv6 pools are always INDIV (--linear) */
++  if (hand >= 0 && hand < pool->size_ipv6 )
++    {
++      ret = add_in6_addr( pool->base_ipv6, hand );
++    }
++  return ret;
++}
++
+ static void
+ ifconfig_pool_set (struct ifconfig_pool* pool, const char *cn, const in_addr_t addr, const bool fixed)
+ {
+@@ -317,9 +359,20 @@
+         if (e->common_name)
+           {
+             const in_addr_t ip = ifconfig_pool_handle_to_ip_base (pool, i);
+-            status_printf (out, "%s,%s",
+-                           e->common_name,
+-                           print_in_addr_t (ip, 0, &gc));
++            if ( pool->ipv6 )
++              {
++                struct in6_addr ip6 = ifconfig_pool_handle_to_ipv6_base (pool, i);
++                status_printf (out, "%s,%s,%s",
++                               e->common_name,
++                               print_in_addr_t (ip, 0, &gc),
++                               print_in6_addr (ip6, 0, &gc));
++              }
++            else
++              {
++                status_printf (out, "%s,%s",
++                               e->common_name,
++                               print_in_addr_t (ip, 0, &gc));
++              }
+           }
+       }
+       gc_free (&gc);
+@@ -409,6 +462,9 @@
+             int c = *BSTR(&in);
+             if (c == '#' || c == ';')
+               continue;
++            msg( M_INFO, "ifconfig_pool_read(), in='%s', TODO: IPv6",
++                              BSTR(&in) );
++
+             if (buf_parse (&in, ',', cn_buf, buf_size)
+                 && buf_parse (&in, ',', ip_buf, buf_size))
+               {
+@@ -416,6 +472,7 @@
+                 const in_addr_t addr = getaddr (GETADDR_HOST_ORDER, ip_buf, 0, &succeeded, NULL);
+                 if (succeeded)
+                   {
++                    msg( M_INFO, "succeeded -> ifconfig_pool_set()");
+                     ifconfig_pool_set (pool, cn_buf, addr, persist->fixed);
+                   }
+               }
+@@ -471,7 +528,7 @@
+ #else
+       cn = buf;
+ #endif
+-      h = ifconfig_pool_acquire (p, &local, &remote, cn);
++      h = ifconfig_pool_acquire (p, &local, &remote, NULL, cn);
+       if (h < 0)
+       break;
+       msg (M_INFO | M_NOPREFIX, "IFCONFIG_POOL TEST pass 1: l=%s r=%s cn=%s",
+@@ -506,7 +563,7 @@
+ #else
+       cn = buf;
+ #endif
+-      h = ifconfig_pool_acquire (p, &local, &remote, cn);
++      h = ifconfig_pool_acquire (p, &local, &remote, NULL, cn);
+       if (h < 0)
+       break;
+       msg (M_INFO | M_NOPREFIX, "IFCONFIG_POOL TEST pass 3: l=%s r=%s cn=%s",
+diff -durN openvpn-2.2.2.orig/pool.h openvpn-2.2.2/pool.h
+--- openvpn-2.2.2.orig/pool.h  2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/pool.h       2012-06-01 10:40:28.000000000 +0200
+@@ -52,6 +52,9 @@
+   int size;
+   int type;
+   bool duplicate_cn;
++  bool ipv6;
++  struct in6_addr base_ipv6;
++  unsigned int size_ipv6;
+   struct ifconfig_pool_entry *list;
+ };
+@@ -63,13 +66,13 @@
+ typedef int ifconfig_pool_handle;
+-struct ifconfig_pool *ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn);
++struct ifconfig_pool *ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn, const bool ipv6_pool, const struct in6_addr ipv6_base, const int ipv6_netbits );
+ void ifconfig_pool_free (struct ifconfig_pool *pool);
+ bool ifconfig_pool_verify_range (const int msglevel, const in_addr_t start, const in_addr_t end);
+-ifconfig_pool_handle ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, const char *common_name);
++ifconfig_pool_handle ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, struct in6_addr *remote_ipv6, const char *common_name);
+ bool ifconfig_pool_release (struct ifconfig_pool* pool, ifconfig_pool_handle hand, const bool hard);
+diff -durN openvpn-2.2.2.orig/proto.h openvpn-2.2.2/proto.h
+--- openvpn-2.2.2.orig/proto.h 2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/proto.h      2012-06-01 10:40:28.000000000 +0200
+@@ -108,6 +108,21 @@
+ };
+ /*
++ * IPv6 header
++ */
++struct openvpn_ipv6hdr {
++        uint8_t               version_prio;
++        uint8_t               flow_lbl[3];
++        uint16_t      payload_len;
++        uint8_t               nexthdr;
++        uint8_t               hop_limit;
++
++        struct  in6_addr        saddr;
++        struct  in6_addr        daddr;
++};
++
++
++/*
+  * UDP header
+  */
+ struct openvpn_udphdr {
+diff -durN openvpn-2.2.2.orig/push.c openvpn-2.2.2/push.c
+--- openvpn-2.2.2.orig/push.c  2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/push.c       2012-06-01 10:40:28.000000000 +0200
+@@ -189,8 +189,26 @@
+   const int safe_cap = BCAP (&buf) - extra;
+   bool push_sent = false;
++  msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap );
++
+   buf_printf (&buf, "%s", cmd);
++  if ( c->c2.push_ifconfig_ipv6_defined )
++    {
++      /* IPv6 is put into buffer first, could be lengthy */
++      /* TODO: push "/netbits" as well, to allow non-/64 subnet sizes
++       *       (needs changes in options.c, options.h, and other places)
++       */
++      buf_printf( &buf, ",ifconfig-ipv6 %s %s",
++                  print_in6_addr( c->c2.push_ifconfig_ipv6_local, 0, &gc),
++                  print_in6_addr( c->c2.push_ifconfig_ipv6_remote, 0, &gc) );
++      if (BLEN (&buf) >= safe_cap)
++      {
++        msg (M_WARN, "--push ifconfig-ipv6 option is too long");
++        goto fail;
++      }
++    }
++
+   while (e)
+     {
+       if (e->enable)
+diff -durN openvpn-2.2.2.orig/route.c openvpn-2.2.2/route.c
+--- openvpn-2.2.2.orig/route.c 2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/route.c      2012-06-01 10:40:28.000000000 +0200
+@@ -35,6 +35,7 @@
+ #include "socket.h"
+ #include "manage.h"
+ #include "win32.h"
++#include "options.h"
+ #include "memdbg.h"
+@@ -68,6 +69,15 @@
+   return ret;
+ }
++struct route_ipv6_option_list *
++new_route_ipv6_option_list (const int max_routes, struct gc_arena *a)
++{
++  struct route_ipv6_option_list *ret;
++  ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_option_list, struct route_ipv6_option, max_routes, a);
++  ret->capacity = max_routes;
++  return ret;
++}
++
+ struct route_option_list *
+ clone_route_option_list (const struct route_option_list *src, struct gc_arena *a)
+ {
+@@ -95,6 +105,15 @@
+   return ret;
+ }
++struct route_ipv6_list *
++new_route_ipv6_list (const int max_routes, struct gc_arena *a)
++{
++  struct route_ipv6_list *ret;
++  ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_list, struct route_ipv6, max_routes, a);
++  ret->capacity = max_routes;
++  return ret;
++}
++
+ static const char *
+ route_string (const struct route *r, struct gc_arena *gc)
+ {
+@@ -311,6 +330,68 @@
+   return false;
+ }
++static bool
++init_route_ipv6 (struct route_ipv6 *r6,
++               const struct route_ipv6_option *r6o,
++               const struct route_ipv6_list *rl6 )
++{
++  r6->option = r6o;
++  r6->defined = false;
++
++  if ( !get_ipv6_addr( r6o->prefix, &r6->network, &r6->netbits, NULL, M_WARN ))
++    goto fail;
++
++  /* gateway */
++  if (is_route_parm_defined (r6o->gateway))
++    {
++      if ( inet_pton( AF_INET6, r6o->gateway, &r6->gateway ) != 1 )
++        {
++        msg( M_WARN, PACKAGE_NAME "ROUTE6: cannot parse gateway spec '%s'", r6o->gateway );
++        }
++    }
++  else if (rl6->remote_endpoint_defined)
++    {
++      r6->gateway = rl6->remote_endpoint_ipv6;
++    }
++  else
++    {
++      msg (M_WARN, PACKAGE_NAME " ROUTE6: " PACKAGE_NAME " needs a gateway parameter for a --route-ipv6 option and no default was specified by either --route-ipv6-gateway or --ifconfig-ipv6 options");
++      goto fail;
++    }
++
++  /* metric */
++
++  r6->metric_defined = false;
++  r6->metric = 0;
++  if (is_route_parm_defined (r6o->metric))
++    {
++      r6->metric = atoi (r6o->metric);
++      if (r6->metric < 0)
++      {
++        msg (M_WARN, PACKAGE_NAME " ROUTE: route metric for network %s (%s) must be >= 0",
++             r6o->prefix,
++             r6o->metric);
++        goto fail;
++      }
++      r6->metric_defined = true;
++    }
++  else if (rl6->default_metric_defined)
++    {
++      r6->metric = rl6->default_metric;
++      r6->metric_defined = true;
++    }
++
++  r6->defined = true;
++
++  return true;
++
++ fail:
++  msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve route for host/network: %s",
++       r6o->prefix);
++  r6->defined = false;
++  return false;
++}
++
+ void
+ add_route_to_option_list (struct route_option_list *l,
+                         const char *network,
+@@ -331,6 +412,23 @@
+ }
+ void
++add_route_ipv6_to_option_list (struct route_ipv6_option_list *l,
++                        const char *prefix,
++                        const char *gateway,
++                        const char *metric)
++{
++  struct route_ipv6_option *ro;
++  if (l->n >= l->capacity)
++    msg (M_FATAL, PACKAGE_NAME " ROUTE: cannot add more than %d IPv6 routes -- please increase the max-routes option in the client configuration file",
++       l->capacity);
++  ro = &l->routes_ipv6[l->n];
++  ro->prefix = prefix;
++  ro->gateway = gateway;
++  ro->metric = metric;
++  ++l->n;
++}
++
++void
+ clear_route_list (struct route_list *rl)
+ {
+   const int capacity = rl->capacity;
+@@ -340,6 +438,15 @@
+ }
+ void
++clear_route_ipv6_list (struct route_ipv6_list *rl6)
++{
++  const int capacity = rl6->capacity;
++  const size_t rl6_size = array_mult_safe (sizeof(struct route_ipv6), capacity, sizeof(struct route_ipv6_list));
++  memset(rl6, 0, rl6_size);
++  rl6->capacity = capacity;
++}
++
++void
+ route_list_add_default_gateway (struct route_list *rl,
+                               struct env_set *es,
+                               const in_addr_t addr)
+@@ -469,6 +576,72 @@
+   return ret;
+ }
++bool
++init_route_ipv6_list (struct route_ipv6_list *rl6,
++               const struct route_ipv6_option_list *opt6,
++               const char *remote_endpoint,
++               int default_metric,
++               struct env_set *es)
++{
++  struct gc_arena gc = gc_new ();
++  bool ret = true;
++
++  clear_route_ipv6_list (rl6);
++
++  rl6->flags = opt6->flags;
++
++  if (default_metric)
++    {
++      rl6->default_metric = default_metric;
++      rl6->default_metric_defined = true;
++    }
++
++  /* "default_gateway" is stuff for "redirect-gateway", which we don't
++   * do for IPv6 yet -> TODO
++   */
++    {
++      dmsg (D_ROUTE, "ROUTE6: default_gateway=UNDEF");
++    }
++
++  if ( is_route_parm_defined( remote_endpoint ))
++    {
++      if ( inet_pton( AF_INET6, remote_endpoint, 
++                      &rl6->remote_endpoint_ipv6) == 1 )
++        {
++        rl6->remote_endpoint_defined = true;
++        }
++      else
++      {
++        msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve default gateway: %s", remote_endpoint);
++          ret = false;
++      }
++    }
++  else
++    rl6->remote_endpoint_defined = false;
++
++
++  if (!(opt6->n >= 0 && opt6->n <= rl6->capacity))
++    msg (M_FATAL, PACKAGE_NAME " ROUTE6: (init) number of route options (%d) is greater than route list capacity (%d)", opt6->n, rl6->capacity);
++
++  /* parse the routes from opt to rl6 */
++  {
++    int i, j = 0;
++    for (i = 0; i < opt6->n; ++i)
++      {
++      if (!init_route_ipv6 (&rl6->routes_ipv6[j],
++                            &opt6->routes_ipv6[i],
++                            rl6 ))
++        ret = false;
++      else
++        ++j;
++      }
++    rl6->n = j;
++  }
++
++  gc_free (&gc);
++  return ret;
++}
++
+ static void
+ add_route3 (in_addr_t network,
+           in_addr_t netmask,
+@@ -704,10 +877,13 @@
+ }
+ void
+-add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
++add_routes (struct route_list *rl, struct route_ipv6_list *rl6,
++          const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+ {
+-  redirect_default_route_to_vpn (rl, tt, flags, es);
+-  if (!rl->routes_added)
++  if (rl) 
++      redirect_default_route_to_vpn (rl, tt, flags, es);
++
++  if (rl && !rl->routes_added)
+     {
+       int i;
+@@ -732,12 +908,27 @@
+       }
+       rl->routes_added = true;
+     }
++
++  if (rl6 && !rl6->routes_added)
++    {
++      int i;
++
++      for (i = 0; i < rl6->n; ++i)
++      {
++        struct route_ipv6 *r = &rl6->routes_ipv6[i];
++        if (flags & ROUTE_DELETE_FIRST)
++          delete_route_ipv6 (r, tt, flags, es);
++        add_route_ipv6 (r, tt, flags, es);
++      }
++      rl6->routes_added = true;
++    }
+ }
+ void
+-delete_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
++delete_routes (struct route_list *rl, struct route_ipv6_list *rl6,
++             const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+ {
+-  if (rl->routes_added)
++  if (rl && rl->routes_added)
+     {
+       int i;
+       for (i = rl->n - 1; i >= 0; --i)
+@@ -747,9 +938,28 @@
+       }
+       rl->routes_added = false;
+     }
+-  undo_redirect_default_route_to_vpn (rl, tt, flags, es);
+-  clear_route_list (rl);
++  if ( rl )
++    {
++      undo_redirect_default_route_to_vpn (rl, tt, flags, es);
++      clear_route_list (rl);
++    }
++
++  if ( rl6 && rl6->routes_added )
++    {
++      int i;
++      for (i = rl6->n - 1; i >= 0; --i)
++      {
++        const struct route_ipv6 *r6 = &rl6->routes_ipv6[i];
++        delete_route_ipv6 (r6, tt, flags, es);
++      }
++      rl6->routes_added = false;
++    }
++
++  if ( rl6 )
++    {
++      clear_route_ipv6_list (rl6);
++    }
+ }
+ #ifdef ENABLE_DEBUG
+@@ -832,6 +1042,34 @@
+     setenv_route (es, &rl->routes[i], i + 1);
+ }
++static void
++setenv_route_ipv6 (struct env_set *es, const struct route_ipv6 *r6, int i)
++{
++  struct gc_arena gc = gc_new ();
++  if (r6->defined)
++    {
++      struct buffer name1 = alloc_buf_gc( 256, &gc );
++      struct buffer val = alloc_buf_gc( 256, &gc );
++      struct buffer name2 = alloc_buf_gc( 256, &gc );
++
++      buf_printf( &name1, "route_ipv6_network_%d", i );
++      buf_printf( &val, "%s/%d", print_in6_addr( r6->network, 0, &gc ),
++                               r6->netbits );
++      setenv_str( es, BSTR(&name1), BSTR(&val) );
++
++      buf_printf( &name2, "route_ipv6_gateway_%d", i );
++      setenv_str( es, BSTR(&name2), print_in6_addr( r6->gateway, 0, &gc ));
++    }
++  gc_free (&gc);
++}
++void
++setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6)
++{
++  int i;
++  for (i = 0; i < rl6->n; ++i)
++    setenv_route_ipv6 (es, &rl6->routes_ipv6[i], i + 1);
++}
++
+ void
+ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+ {
+@@ -1025,6 +1263,187 @@
+   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)
+ {
+@@ -1161,6 +1580,142 @@
+ #endif
+   argv_reset (&argv);
++  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);
+ }
+diff -durN openvpn-2.2.2.orig/route.h openvpn-2.2.2/route.h
+--- openvpn-2.2.2.orig/route.h 2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/route.h      2012-06-01 10:40:28.000000000 +0200
+@@ -92,6 +92,19 @@
+   struct route_option routes[EMPTY_ARRAY_SIZE];
+ };
++struct route_ipv6_option {
++  const char *prefix;         /* e.g. "2001:db8:1::/64" */
++  const char *gateway;                /* e.g. "2001:db8:0::2" */
++  const char *metric;         /* e.g. "5" */
++};
++
++struct route_ipv6_option_list {
++  unsigned int flags;
++  int capacity;
++  int n;
++  struct route_ipv6_option routes_ipv6[EMPTY_ARRAY_SIZE];
++};
++
+ struct route {
+   bool defined;
+   const struct route_option *option;
+@@ -113,6 +126,31 @@
+   struct route routes[EMPTY_ARRAY_SIZE];
+ };
++struct route_ipv6 {
++  bool defined;
++  const struct route_ipv6_option *option;
++  struct in6_addr network;
++  unsigned int netbits;
++  struct in6_addr gateway;
++  bool metric_defined;
++  int metric;
++};
++
++struct route_ipv6_list {
++  bool routes_added;
++  unsigned int flags;
++  int default_metric;
++  bool default_metric_defined;
++  struct in6_addr remote_endpoint_ipv6;
++  bool remote_endpoint_defined;
++  bool did_redirect_default_gateway;                  /* TODO (?) */
++  bool did_local;                                     /* TODO (?) */
++  int capacity;
++  int n;
++  struct route_ipv6 routes_ipv6[EMPTY_ARRAY_SIZE];
++};
++
++
+ #if P2MP
+ /* internal OpenVPN route */
+ struct iroute {
+@@ -120,15 +158,25 @@
+   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 @@
+                              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 @@
+                     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 -durN openvpn-2.2.2.orig/socket.c openvpn-2.2.2/socket.c
+--- openvpn-2.2.2.orig/socket.c        2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/socket.c     2012-06-01 10:40:28.000000000 +0200
+@@ -342,6 +342,24 @@
+   }
+ }
++bool
++ipv6_addr_safe (const char *ipv6_text_addr)
++{
++  /* verify non-NULL */
++  if (!ipv6_text_addr)
++    return false;
++
++  /* verify length is within limits */
++  if (strlen (ipv6_text_addr) > INET6_ADDRSTRLEN )
++    return false;
++
++  /* verify that string will convert to IPv6 address */
++  {
++    struct in6_addr a6;
++    return inet_pton( AF_INET6, ipv6_text_addr, &a6 ) == 1;
++  }
++}
++
+ static bool
+ dns_addr_safe (const char *addr)
+ {
+@@ -2032,6 +2050,55 @@
+   return BSTR (&out);
+ }
++/*
++ * Convert an in6_addr in host byte order
++ * to an ascii representation of an IPv6 address
++ */
++const char *
++print_in6_addr (struct in6_addr a6, unsigned int flags, struct gc_arena *gc)
++{
++  struct buffer out = alloc_buf_gc (64, gc);
++  char tmp_out_buf[64];               /* inet_ntop wants pointer to buffer */
++
++  if ( memcmp(&a6, &in6addr_any, sizeof(a6)) != 0 || 
++       !(flags & IA_EMPTY_IF_UNDEF))
++    {
++      inet_ntop (AF_INET6, &a6, tmp_out_buf, sizeof(tmp_out_buf)-1);
++      buf_printf (&out, "%s", tmp_out_buf );
++    }
++  return BSTR (&out);
++}
++
++/* add some offset to an ipv6 address
++ * (add in steps of 32 bits, taking overflow into next round)
++ */
++#ifndef s6_addr32
++# ifdef TARGET_SOLARIS
++#  define s6_addr32 _S6_un._S6_u32
++# else
++#  define s6_addr32 __u6_addr.__u6_addr32
++# endif
++#endif
++#ifndef UINT32_MAX
++# define UINT32_MAX (4294967295U)
++#endif
++struct in6_addr add_in6_addr( struct in6_addr base, uint32_t add )
++{
++    int i;
++    uint32_t h;
++
++    for( i=3; i>=0 && add > 0 ; i-- )
++    {
++      h = ntohl( base.s6_addr32[i] );
++      base.s6_addr32[i] = htonl( (h+add) & UINT32_MAX );
++      /* 32-bit overrun? 
++       * caveat: can't do "h+add > UINT32_MAX" with 32bit math!
++         */
++      add = ( h > UINT32_MAX - add )?  1: 0;
++    }
++    return base;
++}
++
+ /* set environmental variables for ip/port in *addr */
+ void
+ setenv_sockaddr (struct env_set *es, const char *name_prefix, const struct openvpn_sockaddr *addr, const bool flags)
+@@ -2337,6 +2404,58 @@
+ #ifdef WIN32
++/*
++ * inet_ntop() and inet_pton() wrap-implementations using
++ * WSAAddressToString() and WSAStringToAddress() functions
++ */
++const char *
++inet_ntop(int af, const void *src, char *dst, socklen_t size)
++{
++  struct sockaddr_storage ss;
++  unsigned long s = size;
++
++  CLEAR(ss);
++  ss.ss_family = af;
++
++  switch(af) {
++    case AF_INET:
++      ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src;
++      break;
++    case AF_INET6:
++      ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src;
++      break;
++    default:
++      ASSERT (0);
++  }
++  // cannot direclty use &size because of strict aliasing rules
++  return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)?
++          dst : NULL;
++}
++
++int
++inet_pton(int af, const char *src, void *dst)
++{
++  struct sockaddr_storage ss;
++  int size = sizeof(ss);
++  char src_copy[INET6_ADDRSTRLEN+1];
++
++  CLEAR(ss);
++  // stupid non-const API
++  strncpynt(src_copy, src, INET6_ADDRSTRLEN+1);
++
++  if (WSAStringToAddress(src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) {
++    switch(af) {
++      case AF_INET:
++      *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr;
++      return 1;
++      case AF_INET6:
++      *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr;
++      return 1;
++    }
++  }
++  return 0;
++}
++
+ int
+ socket_recv_queue (struct link_socket *sock, int maxsize)
+ {
+diff -durN openvpn-2.2.2.orig/socket.h openvpn-2.2.2/socket.h
+--- openvpn-2.2.2.orig/socket.h        2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/socket.h     2012-06-01 10:40:28.000000000 +0200
+@@ -351,6 +351,8 @@
+ #define IA_EMPTY_IF_UNDEF (1<<0)
+ #define IA_NET_ORDER      (1<<1)
+ const char *print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc);
++const char *print_in6_addr  (struct in6_addr addr6, unsigned int flags, struct gc_arena *gc);
++struct in6_addr add_in6_addr( struct in6_addr base, uint32_t add );
+ #define SA_IP_PORT        (1<<0)
+ #define SA_SET_IF_NONZERO (1<<1)
+@@ -404,6 +406,7 @@
+ bool ip_addr_dotted_quad_safe (const char *dotted_quad);
+ bool ip_or_dns_addr_safe (const char *addr, const bool allow_fqdn);
+ bool mac_addr_safe (const char *mac_addr);
++bool ipv6_addr_safe (const char *ipv6_text_addr);
+ socket_descriptor_t create_socket_tcp (void);
+diff -durN openvpn-2.2.2.orig/syshead.h openvpn-2.2.2/syshead.h
+--- openvpn-2.2.2.orig/syshead.h       2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/syshead.h    2012-06-01 10:40:28.000000000 +0200
+@@ -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 -durN openvpn-2.2.2.orig/tun.c openvpn-2.2.2/tun.c
+--- openvpn-2.2.2.orig/tun.c   2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/tun.c        2012-06-01 10:46:55.000000000 +0200
+@@ -56,13 +56,14 @@
+                           const in_addr_t ip,
+                           const in_addr_t netmask,
+                           const unsigned int flags);
++static void netsh_command (const struct argv *a, int n);
+ static const char *netsh_get_id (const char *dev_node, struct gc_arena *gc);
+ #endif
+ #ifdef TARGET_SOLARIS
+-static void solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual);
++static void solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual, bool unplumb_inet6);
+ #include <stropts.h>
+ #endif
+@@ -129,30 +130,6 @@
+   return dev;
+ }
+-/*
+- * Called by the open_tun function of OSes to check if we
+- * explicitly support IPv6.
+- *
+- * In this context, explicit means that the OS expects us to
+- * do something special to the tun socket in order to support
+- * IPv6, i.e. it is not transparent.
+- *
+- * ipv6_explicitly_supported should be set to false if we don't
+- * have any explicit IPv6 code in the tun device handler.
+- *
+- * If ipv6_explicitly_supported is true, then we have explicit
+- * OS-specific tun dev code for handling IPv6.  If so, tt->ipv6
+- * is set according to the --tun-ipv6 command line option.
+- */
+-static void
+-ipv6_support (bool ipv6, bool ipv6_explicitly_supported, struct tuntap* tt)
+-{
+-  tt->ipv6 = false;
+-  if (ipv6_explicitly_supported)
+-    tt->ipv6 = ipv6;
+-  else if (ipv6)
+-    msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS");
+-}
+ /* --ifconfig-nowarn disables some options sanity checking */
+ static const char ifconfig_warn_how_to_silence[] = "(silence this warning with --ifconfig-nowarn)";
+@@ -423,6 +400,8 @@
+         int topology,          /* one of the TOP_x values */
+         const char *ifconfig_local_parm,          /* --ifconfig parm 1 */
+         const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */
++        const char *ifconfig_ipv6_local_parm,     /* --ifconfig parm 1 IPv6 */
++        const char *ifconfig_ipv6_remote_parm,    /* --ifconfig parm 2 IPv6 */
+         in_addr_t local_public,
+         in_addr_t remote_public,
+         const bool strict_warn,
+@@ -537,6 +516,40 @@
+       tt->did_ifconfig_setup = true;
+     }
++
++  if (ifconfig_ipv6_local_parm && ifconfig_ipv6_remote_parm)
++    {
++      const char *ifconfig_ipv6_local = NULL;
++      const char *ifconfig_ipv6_remote = NULL;
++
++      /*
++       * Convert arguments to binary IPv6 addresses.
++       */
++
++      if ( inet_pton( AF_INET6, ifconfig_ipv6_local_parm, &tt->local_ipv6 ) != 1 ||
++           inet_pton( AF_INET6, ifconfig_ipv6_remote_parm, &tt->remote_ipv6 ) != 1 ) 
++      {
++        msg( M_FATAL, "init_tun: problem converting IPv6 ifconfig addresses %s and %s to binary", ifconfig_ipv6_local_parm, ifconfig_ipv6_remote_parm );
++      }
++      tt->netbits_ipv6 = 64;
++
++      /*
++       * Set ifconfig parameters
++       */
++      ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc);
++      ifconfig_ipv6_remote = print_in6_addr (tt->remote_ipv6, 0, &gc);
++
++      /*
++       * Set environmental variables with ifconfig parameters.
++       */
++      if (es)
++      {
++        setenv_str (es, "ifconfig_ipv6_local", ifconfig_ipv6_local);
++        setenv_str (es, "ifconfig_ipv6_remote", ifconfig_ipv6_remote);
++      }
++      tt->did_ifconfig_ipv6_setup = true;
++    }
++
+   gc_free (&gc);
+   return tt;
+ }
+@@ -559,6 +572,40 @@
+ #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 @@
+       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 @@
+       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 @@
+                 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 @@
+                         );
+       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 @@
+         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 @@
+       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 @@
+                         );
+       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 @@
+                         );
+       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 @@
+         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 @@
+           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 @@
+       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 @@
+ #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 @@
+ #if !PEDANTIC
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+   struct ifreq ifr;
+-  /*
+-   * Set tt->ipv6 to true if
+-   * (a) we have the capability of supporting --tun-ipv6, and
+-   * (b) --tun-ipv6 was specified.
++  /* warn if a very old linux version is used & --tun-ipv6 set
+    */
+-  ipv6_support (ipv6, LINUX_IPV6, tt);
++#if LINUX_IPV6 == 0
++  if ( tt->ipv6 )
++    msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS");
++#endif
+   /*
+    * We handle --dev null specially, we do not open /dev/null for this.
+@@ -1222,9 +1464,9 @@
+ #else
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+-  open_tun_generic (dev, dev_type, dev_node, ipv6, false, true, tt);
++  open_tun_generic (dev, dev_type, dev_node, false, true, tt);
+ }
+ #endif /* HAVE_LINUX_IF_TUN_H */
+@@ -1244,7 +1486,7 @@
+ #endif
+ void
+-tuncfg (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, int persist_mode, const char *username, const char *groupname, const struct tuntap_options *options)
++tuncfg (const char *dev, const char *dev_type, const char *dev_node, int persist_mode, const char *username, const char *groupname, const struct tuntap_options *options)
+ {
+   struct tuntap *tt;
+@@ -1252,7 +1494,7 @@
+   clear_tuntap (tt);
+   tt->type = dev_type_enum (dev, dev_type);
+   tt->options = *options;
+-  open_tun (dev, dev_type, dev_node, ipv6, tt);
++  open_tun (dev, dev_type, dev_node, tt);
+   if (ioctl (tt->fd, TUNSETPERSIST, persist_mode) < 0)
+     msg (M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
+   if (username != NULL)
+@@ -1395,7 +1637,7 @@
+ #endif
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+   int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
+   struct lifreq ifr;
+@@ -1406,8 +1648,11 @@
+   bool is_tun;
+   struct strioctl  strioc_if, strioc_ppa;
+-  ipv6_support (ipv6, true, tt);
+-  memset(&ifr, 0x0, sizeof(ifr));
++  /* improved generic TUN/TAP driver from
++   * http://www.whiteboard.ne.jp/~admin2/tuntap/
++   * has IPv6 support
++   */
++  CLEAR(ifr);
+   if (tt->type == DEV_TYPE_NULL)
+     {
+@@ -1561,6 +1806,18 @@
+ {
+   if (tt)
+     {
++      /* IPv6 interfaces need to be 'manually' de-configured */
++      if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup )
++      {
++        struct argv argv;
++        argv_init (&argv);
++        argv_printf( &argv, "%s %s inet6 unplumb",
++                     IFCONFIG_PATH, tt->actual_name );
++        argv_msg (M_INFO, &argv);
++        openvpn_execve_check (&argv, NULL, 0, "Solaris ifconfig inet6 unplumb failed");
++        argv_reset (&argv);
++      }
++
+       if (tt->ip_fd >= 0)
+       {
+           struct lifreq ifr;
+@@ -1613,11 +1870,20 @@
+ }
+ static void
+-solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual)
++solaris_error_close (struct tuntap *tt, const struct env_set *es, 
++                     const char *actual, bool unplumb_inet6 )
+ {
+   struct argv argv;
+   argv_init (&argv);
++  if (unplumb_inet6)
++    {
++      argv_printf( &argv, "%s %s inet6 unplumb",
++                 IFCONFIG_PATH, actual );
++      argv_msg (M_INFO, &argv);
++      openvpn_execve_check (&argv, es, 0, "Solaris ifconfig inet6 unplumb failed");
++    }
++
+   argv_printf (&argv,
+                   "%s %s unplumb",
+                   IFCONFIG_PATH,
+@@ -1674,9 +1940,9 @@
+  */
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+-  open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
++  open_tun_generic (dev, dev_type, dev_node, true, true, tt);
+   /* Enable multicast on the interface */
+   if (tt->fd >= 0)
+@@ -1697,12 +1963,31 @@
+     }
+ }
++/* the current way OpenVPN handles tun devices on OpenBSD leads to
++ * lingering tunX interfaces after close -> for a full cleanup, they
++ * need to be explicitely destroyed
++ */
++
+ void
+ close_tun (struct tuntap* tt)
+ {
+   if (tt)
+     {
++      struct gc_arena gc = gc_new ();
++      struct argv argv;
++
++      /* setup command, close tun dev (clears tt->actual_name!), run command
++       */
++
++      argv_init (&argv);
++      argv_printf (&argv, "%s %s destroy",
++                          IFCONFIG_PATH, tt->actual_name);
++
+       close_tun_generic (tt);
++
++      argv_msg (M_INFO, &argv);
++      openvpn_execve_check (&argv, NULL, 0, "OpenBSD 'destroy tun interface' failed (non-critical)");
++
+       free (tt);
+     }
+ }
+@@ -1765,33 +2050,51 @@
+ #elif defined(TARGET_NETBSD)
+ /*
+- * NetBSD does not support IPv6 on tun out of the box,
+- * but there exists a patch. When this patch is applied,
+- * only two things are left to openvpn:
+- * 1. Activate multicasting (this has already been done
+- *    before by the kernel, but we make sure that nobody
+- *    has deactivated multicasting inbetween.
+- * 2. Deactivate "link layer mode" (otherwise NetBSD 
+- *    prepends the address family to the packet, and we
+- *    would run into the same trouble as with OpenBSD.
++ * NetBSD before 4.0 does not support IPv6 on tun out of the box,
++ * but there exists a patch (sys/net/if_tun.c, 1.79->1.80, see PR 32944).
++ *
++ * NetBSD 4.0 and up do, but we need to put the tun interface into
++ * "multi_af" mode, which will prepend the address family to all packets
++ * (same as OpenBSD and FreeBSD).  If this is not enabled, the kernel
++ * silently drops all IPv6 packets on output and gets confused on input.
++ *
++ * On earlier versions, multi_af is not available at all, so we have
++ * two different NetBSD code variants here :-(
++ *
+  */
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+-    open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
++#ifdef NETBSD_MULTI_AF
++    open_tun_generic (dev, dev_type, dev_node, true, true, tt);
++#else
++    open_tun_generic (dev, dev_type, dev_node, false, true, tt);
++#endif
++
+     if (tt->fd >= 0)
+       {
+         int i = IFF_POINTOPOINT|IFF_MULTICAST;
+         ioctl (tt->fd, TUNSIFMODE, &i);  /* multicast on */
+         i = 0;
+         ioctl (tt->fd, TUNSLMODE, &i);   /* link layer mode off */
++
++#ifdef NETBSD_MULTI_AF
++        i = 1;
++        if (ioctl (tt->fd, TUNSIFHEAD, &i) < 0)       /* multi-af mode on */
++        {
++          msg (M_WARN | M_ERRNO, "ioctl(TUNSIFHEAD): %s", strerror(errno));
++        }
++#endif
+       }
+ }
+ void
+ close_tun (struct tuntap *tt)
+ {
++  /* TODO: we really should cleanup non-persistant tunX with 
++   * "ifconfig tunX destroy" here...
++   */
+   if (tt)
+     {
+       close_tun_generic (tt);
+@@ -1799,6 +2102,65 @@
+     }
+ }
++#ifdef NETBSD_MULTI_AF
++
++static inline int
++netbsd_modify_read_write_return (int len)
++{
++  if (len > 0)
++    return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0;
++  else
++    return len;
++}
++
++int
++write_tun (struct tuntap* tt, uint8_t *buf, int len)
++{
++  if (tt->type == DEV_TYPE_TUN)
++    {
++      u_int32_t type;
++      struct iovec iv[2];
++      struct openvpn_iphdr *iph;
++
++      iph = (struct openvpn_iphdr *) buf;
++
++      if (tt->ipv6 && OPENVPN_IPH_GET_VER(iph->version_len) == 6)
++        type = htonl (AF_INET6);
++      else 
++        type = htonl (AF_INET);
++
++      iv[0].iov_base = (char *)&type;
++      iv[0].iov_len = sizeof (type);
++      iv[1].iov_base = buf;
++      iv[1].iov_len = len;
++
++      return netbsd_modify_read_write_return (writev (tt->fd, iv, 2));
++    }
++  else
++    return write (tt->fd, buf, len);
++}
++
++int
++read_tun (struct tuntap* tt, uint8_t *buf, int len)
++{
++  if (tt->type == DEV_TYPE_TUN)
++    {
++      u_int32_t type;
++      struct iovec iv[2];
++
++      iv[0].iov_base = (char *)&type;
++      iv[0].iov_len = sizeof (type);
++      iv[1].iov_base = buf;
++      iv[1].iov_len = len;
++
++      return netbsd_modify_read_write_return (readv (tt->fd, iv, 2));
++    }
++  else
++    return read (tt->fd, buf, len);
++}
++
++#else /* not NETBSD_MULTI_AF -> older code, IPv4 only */
++
+ int
+ write_tun (struct tuntap* tt, uint8_t *buf, int len)
+ {
+@@ -1810,6 +2172,7 @@
+ {
+     return read (tt->fd, buf, len);
+ }
++#endif        /* NETBSD_MULTI_AF */
+ #elif defined(TARGET_FREEBSD)
+@@ -1823,9 +2186,9 @@
+ }
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+-  open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
++  open_tun_generic (dev, dev_type, dev_node, true, true, tt);
+   if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
+     {
+@@ -1911,9 +2274,9 @@
+ }
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+-  open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
++  open_tun_generic (dev, dev_type, dev_node, true, true, tt);
+   if (tt->fd >= 0)
+     {
+@@ -1982,6 +2345,61 @@
+     return read (tt->fd, buf, len);
+ }
++#elif defined(TARGET_DARWIN)
++
++/* Darwin (MacOS X) is mostly "just use the generic stuff", but there
++ * is always one caveat...:
++ *
++ * If IPv6 is configured, and the tun device is closed, the IPv6 address
++ * configured to the tun interface changes to a lingering /128 route
++ * pointing to lo0.  Need to unconfigure...  (observed on 10.5)
++ */
++
++void
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
++{
++  open_tun_generic (dev, dev_type, dev_node, false, true, tt);
++}
++
++void
++close_tun (struct tuntap* tt)
++{
++  if (tt)
++    {
++      struct gc_arena gc = gc_new ();
++      struct argv argv;
++      argv_init (&argv);
++
++      if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup )
++      {
++        const char * ifconfig_ipv6_local =
++                              print_in6_addr (tt->local_ipv6, 0, &gc);
++
++          argv_printf (&argv, "%s delete -inet6 %s",
++                              ROUTE_PATH, ifconfig_ipv6_local );
++        argv_msg (M_INFO, &argv);
++        openvpn_execve_check (&argv, NULL, 0, "MacOS X 'remove inet6 route' failed (non-critical)");
++      }
++
++      close_tun_generic (tt);
++      free (tt);
++      argv_reset (&argv);
++      gc_free (&gc);
++    }
++}
++
++int
++write_tun (struct tuntap* tt, uint8_t *buf, int len)
++{
++  return write (tt->fd, buf, len);
++}
++
++int
++read_tun (struct tuntap* tt, uint8_t *buf, int len)
++{
++  return read (tt->fd, buf, len);
++}
++
+ #elif defined(WIN32)
+ int
+@@ -3967,7 +4385,7 @@
+ }
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+   struct gc_arena gc = gc_new ();
+   char device_path[256];
+@@ -3978,7 +4396,7 @@
+   /*netcmd_semaphore_lock ();*/
+-  ipv6_support (ipv6, false, tt);
++  msg( M_INFO, "open_tun, tt->ipv6=%d", tt->ipv6 );
+   if (tt->type == DEV_TYPE_NULL)
+     {
+@@ -4108,6 +4526,16 @@
+       {
+       msg( M_FATAL, "ERROR:  Tap-Win32 driver version %d.%d is buggy regarding small IPv4 packets in TUN mode.  Upgrade to Tap-Win32 9.9 (2.2.2 release or later) or use TAP mode", (int) info[0], (int) info[1] );
+       }
++
++    /* 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 */
+@@ -4432,6 +4860,26 @@
+   if (tt)
+     {
++      if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup )
++        {
++        struct argv argv;
++        argv_init (&argv);
++
++        /* remove route pointing to interface */
++        delete_route_connected_v6_net(tt, NULL);
++
++        /* netsh interface ipv6 delete address \"%s\" %s */
++        const char * ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc);
++        argv_printf (&argv,
++                  "%s%sc interface ipv6 delete address %s %s",
++                   get_win_sys_path(),
++                   NETSH_PATH_SUFFIX,
++                   tt->actual_name,
++                   ifconfig_ipv6_local );
++
++        netsh_command (&argv, 1);
++          argv_reset (&argv);
++      }
+ #if 1
+       if (tt->ipapi_context_defined)
+       {
+@@ -4535,9 +4983,9 @@
+ #else /* generic */
+ void
+-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+ {
+-  open_tun_generic (dev, dev_type, dev_node, ipv6, false, true, tt);
++  open_tun_generic (dev, dev_type, dev_node, false, true, tt);
+ }
+ void
+diff -durN openvpn-2.2.2.orig/tun.c~ openvpn-2.2.2/tun.c~
+--- openvpn-2.2.2.orig/tun.c~  1970-01-01 01:00:00.000000000 +0100
++++ openvpn-2.2.2/tun.c~       2012-06-01 10:45:07.000000000 +0200
+@@ -0,0 +1,5003 @@
++/*
++ *  OpenVPN -- An application to securely tunnel IP networks
++ *             over a single TCP/UDP port, with support for SSL/TLS-based
++ *             session authentication and key exchange,
++ *             packet encryption, packet authentication, and
++ *             packet compression.
++ *
++ *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
++ *
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License version 2
++ *  as published by the Free Software Foundation.
++ *
++ *  This program is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with this program (see the file COPYING included with this
++ *  distribution); if not, write to the Free Software Foundation, Inc.,
++ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++/*
++ * Support routines for configuring and accessing TUN/TAP
++ * virtual network adapters.
++ *
++ * This file is based on the TUN/TAP driver interface routines
++ * from VTun by Maxim Krasnyansky <max_mk@yahoo.com>.
++ */
++
++#include "syshead.h"
++
++#include "tun.h"
++#include "fdmisc.h"
++#include "common.h"
++#include "misc.h"
++#include "socket.h"
++#include "manage.h"
++#include "route.h"
++#include "win32.h"
++
++#include "memdbg.h"
++
++#ifdef WIN32
++
++/* #define SIMULATE_DHCP_FAILED */       /* simulate bad DHCP negotiation */
++
++#define NI_TEST_FIRST  (1<<0)
++#define NI_IP_NETMASK  (1<<1)
++#define NI_OPTIONS     (1<<2)
++
++static void netsh_ifconfig (const struct tuntap_options *to,
++                          const char *flex_name,
++                          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, bool unplumb_inet6);
++#include <stropts.h>
++#endif
++
++bool
++is_dev_type (const char *dev, const char *dev_type, const char *match_type)
++{
++  ASSERT (match_type);
++  if (!dev)
++    return false;
++  if (dev_type)
++    return !strcmp (dev_type, match_type);
++  else
++    return !strncmp (dev, match_type, strlen (match_type));
++}
++
++int
++dev_type_enum (const char *dev, const char *dev_type)
++{
++  if (is_dev_type (dev, dev_type, "tun"))
++    return DEV_TYPE_TUN;
++  else if (is_dev_type (dev, dev_type, "tap"))
++    return DEV_TYPE_TAP;
++  else if (is_dev_type (dev, dev_type, "null"))
++    return DEV_TYPE_NULL;
++  else
++    return DEV_TYPE_UNDEF;
++}
++
++const char *
++dev_type_string (const char *dev, const char *dev_type)
++{
++  switch (dev_type_enum (dev, dev_type))
++    {
++    case DEV_TYPE_TUN:
++      return "tun";
++    case DEV_TYPE_TAP:
++      return "tap";
++    case DEV_TYPE_NULL:
++      return "null";
++    default:
++      return "[unknown-dev-type]";
++    }
++}
++
++/*
++ * Try to predict the actual TUN/TAP device instance name,
++ * before the device is actually opened.
++ */
++const char *
++guess_tuntap_dev (const char *dev,
++                const char *dev_type,
++                const char *dev_node,
++                struct gc_arena *gc)
++{
++#ifdef WIN32
++  const int dt = dev_type_enum (dev, dev_type);
++  if (dt == DEV_TYPE_TUN || dt == DEV_TYPE_TAP)
++    {
++      return netsh_get_id (dev_node, gc);
++    }
++#endif
++
++  /* default case */
++  return dev;
++}
++
++
++/* --ifconfig-nowarn disables some options sanity checking */
++static const char ifconfig_warn_how_to_silence[] = "(silence this warning with --ifconfig-nowarn)";
++
++/*
++ * If !tun, make sure ifconfig_remote_netmask looks
++ *  like a netmask.
++ *
++ * If tun, make sure ifconfig_remote_netmask looks
++ *  like an IPv4 address.
++ */
++static void
++ifconfig_sanity_check (bool tun, in_addr_t addr, int topology)
++{
++  struct gc_arena gc = gc_new ();
++  const bool looks_like_netmask = ((addr & 0xFF000000) == 0xFF000000);
++  if (tun)
++    {
++      if (looks_like_netmask && (topology == TOP_NET30 || topology == TOP_P2P))
++      msg (M_WARN, "WARNING: Since you are using --dev tun with a point-to-point topology, the second argument to --ifconfig must be an IP address.  You are using something (%s) that looks more like a netmask. %s",
++           print_in_addr_t (addr, 0, &gc),
++           ifconfig_warn_how_to_silence);
++    }
++  else /* tap */
++    {
++      if (!looks_like_netmask)
++      msg (M_WARN, "WARNING: Since you are using --dev tap, the second argument to --ifconfig must be a netmask, for example something like 255.255.255.0. %s",
++           ifconfig_warn_how_to_silence);
++    }
++  gc_free (&gc);
++}
++
++/*
++ * For TAP-style devices, generate a broadcast address.
++ */
++static in_addr_t
++generate_ifconfig_broadcast_addr (in_addr_t local,
++                                in_addr_t netmask)
++{
++  return local | ~netmask;
++}
++
++/*
++ * Check that --local and --remote addresses do not
++ * clash with ifconfig addresses or subnet.
++ */
++static void
++check_addr_clash (const char *name,
++                int type,
++                in_addr_t public,
++                in_addr_t local,
++                in_addr_t remote_netmask)
++{
++  struct gc_arena gc = gc_new ();
++#if 0
++  msg (M_INFO, "CHECK_ADDR_CLASH type=%d public=%s local=%s, remote_netmask=%s",
++       type,
++       print_in_addr_t (public, 0, &gc),
++       print_in_addr_t (local, 0, &gc),
++       print_in_addr_t (remote_netmask, 0, &gc));
++#endif
++
++  if (public)
++    {
++      if (type == DEV_TYPE_TUN)
++      {
++        const in_addr_t test_netmask = 0xFFFFFF00;
++        const in_addr_t public_net = public & test_netmask;
++        const in_addr_t local_net = local & test_netmask;
++        const in_addr_t remote_net = remote_netmask & test_netmask;
++
++        if (public == local || public == remote_netmask)
++          msg (M_WARN,
++               "WARNING: --%s address [%s] conflicts with --ifconfig address pair [%s, %s]. %s",
++               name,
++               print_in_addr_t (public, 0, &gc),
++               print_in_addr_t (local, 0, &gc),
++               print_in_addr_t (remote_netmask, 0, &gc),
++               ifconfig_warn_how_to_silence);
++
++        if (public_net == local_net || public_net == remote_net)
++          msg (M_WARN,
++               "WARNING: potential conflict between --%s address [%s] and --ifconfig address pair [%s, %s] -- this is a warning only that is triggered when local/remote addresses exist within the same /24 subnet as --ifconfig endpoints. %s",
++               name,
++               print_in_addr_t (public, 0, &gc),
++               print_in_addr_t (local, 0, &gc),
++               print_in_addr_t (remote_netmask, 0, &gc),
++               ifconfig_warn_how_to_silence);
++      }
++      else if (type == DEV_TYPE_TAP)
++      {
++        const in_addr_t public_network = public & remote_netmask;
++        const in_addr_t virtual_network = local & remote_netmask;
++        if (public_network == virtual_network)
++          msg (M_WARN,
++               "WARNING: --%s address [%s] conflicts with --ifconfig subnet [%s, %s] -- local and remote addresses cannot be inside of the --ifconfig subnet. %s",
++               name,
++               print_in_addr_t (public, 0, &gc),
++               print_in_addr_t (local, 0, &gc),
++               print_in_addr_t (remote_netmask, 0, &gc),
++               ifconfig_warn_how_to_silence);
++      }
++    }
++  gc_free (&gc);
++}
++
++/*
++ * Issue a warning if ip/netmask (on the virtual IP network) conflicts with
++ * the settings on the local LAN.  This is designed to flag issues where
++ * (for example) the OpenVPN server LAN is running on 192.168.1.x, but then
++ * an OpenVPN client tries to connect from a public location that is also running
++ * off of a router set to 192.168.1.x.
++ */
++void
++check_subnet_conflict (const in_addr_t ip,
++                     const in_addr_t netmask,
++                     const char *prefix)
++{
++  struct gc_arena gc = gc_new ();
++  in_addr_t lan_gw = 0;
++  in_addr_t lan_netmask = 0;
++
++  if (get_default_gateway (&lan_gw, &lan_netmask))
++    {
++      const in_addr_t lan_network = lan_gw & lan_netmask; 
++      const in_addr_t network = ip & netmask;
++
++      /* do the two subnets defined by network/netmask and lan_network/lan_netmask intersect? */
++      if ((network & lan_netmask) == lan_network
++        || (lan_network & netmask) == network)
++      {
++        msg (M_WARN, "WARNING: potential %s subnet conflict between local LAN [%s/%s] and remote VPN [%s/%s]",
++             prefix,
++             print_in_addr_t (lan_network, 0, &gc),
++             print_in_addr_t (lan_netmask, 0, &gc),
++             print_in_addr_t (network, 0, &gc),
++             print_in_addr_t (netmask, 0, &gc));
++      }
++    }
++  gc_free (&gc);
++}
++
++void
++warn_on_use_of_common_subnets (void)
++{
++  struct gc_arena gc = gc_new ();
++  in_addr_t lan_gw = 0;
++  in_addr_t lan_netmask = 0;
++
++  if (get_default_gateway (&lan_gw, &lan_netmask))
++    {
++      const in_addr_t lan_network = lan_gw & lan_netmask; 
++      if (lan_network == 0xC0A80000 || lan_network == 0xC0A80100)
++      msg (M_WARN, "NOTE: your local LAN uses the extremely common subnet address 192.168.0.x or 192.168.1.x.  Be aware that this might create routing conflicts if you connect to the VPN server from public locations such as internet cafes that use the same subnet.");
++    }
++  gc_free (&gc);
++}
++
++/*
++ * Complain if --dev tap and --ifconfig is used on an OS for which
++ * we don't have a custom tap ifconfig template below.
++ */
++static void
++no_tap_ifconfig ()
++{
++  msg (M_FATAL, "Sorry but you cannot use --dev tap and --ifconfig together on this OS because I have not yet been programmed to understand the appropriate ifconfig syntax to use for TAP-style devices on this OS.  Your best alternative is to use an --up script and do the ifconfig command manually.");
++}
++
++/*
++ * Return a string to be used for options compatibility check
++ * between peers.
++ */
++const char *
++ifconfig_options_string (const struct tuntap* tt, bool remote, bool disable, struct gc_arena *gc)
++{
++  struct buffer out = alloc_buf_gc (256, gc);
++  if (tt->did_ifconfig_setup && !disable)
++    {
++      if (tt->type == DEV_TYPE_TAP || (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET))
++      {
++        buf_printf (&out, "%s %s",
++                    print_in_addr_t (tt->local & tt->remote_netmask, 0, gc),
++                    print_in_addr_t (tt->remote_netmask, 0, gc));
++      }
++      else if (tt->type == DEV_TYPE_TUN)
++      {
++        const char *l, *r;
++        if (remote)
++          {
++            r = print_in_addr_t (tt->local, 0, gc);
++            l = print_in_addr_t (tt->remote_netmask, 0, gc);
++          }
++        else
++          {
++            l = print_in_addr_t (tt->local, 0, gc);
++            r = print_in_addr_t (tt->remote_netmask, 0, gc);
++          }
++        buf_printf (&out, "%s %s", r, l);
++      }
++      else
++      buf_printf (&out, "[undef]");
++    }
++  return BSTR (&out);
++}
++
++/*
++ * Return a status string describing wait state.
++ */
++const char *
++tun_stat (const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc)
++{
++  struct buffer out = alloc_buf_gc (64, gc);
++  if (tt)
++    {
++      if (rwflags & EVENT_READ)
++      {
++        buf_printf (&out, "T%s",
++                    (tt->rwflags_debug & EVENT_READ) ? "R" : "r");
++#ifdef WIN32
++        buf_printf (&out, "%s",
++                    overlapped_io_state_ascii (&tt->reads));
++#endif
++      }
++      if (rwflags & EVENT_WRITE)
++      {
++        buf_printf (&out, "T%s",
++                    (tt->rwflags_debug & EVENT_WRITE) ? "W" : "w");
++#ifdef WIN32
++        buf_printf (&out, "%s",
++                    overlapped_io_state_ascii (&tt->writes));
++#endif
++      }
++    }
++  else
++    {
++      buf_printf (&out, "T?");
++    }
++  return BSTR (&out);
++}
++
++/*
++ * Return true for point-to-point topology, false for subnet topology
++ */
++bool
++is_tun_p2p (const struct tuntap *tt)
++{
++  bool tun = false;
++
++  if (tt->type == DEV_TYPE_TAP || (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET))
++    tun = false;
++  else if (tt->type == DEV_TYPE_TUN)
++    tun = true;
++  else
++    msg (M_FATAL, "Error: problem with tun vs. tap setting"); /* JYFIXME -- needs to be caught earlier, in init_tun? */
++
++  return tun;
++}
++
++/*
++ * Init tun/tap object.
++ *
++ * Set up tuntap structure for ifconfig,
++ * but don't execute yet.
++ */
++struct tuntap *
++init_tun (const char *dev,       /* --dev option */
++        const char *dev_type,  /* --dev-type 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,
++        struct env_set *es)
++{
++  struct gc_arena gc = gc_new ();
++  struct tuntap *tt;
++
++  ALLOC_OBJ (tt, struct tuntap);
++  clear_tuntap (tt);
++
++  tt->type = dev_type_enum (dev, dev_type);
++  tt->topology = topology;
++
++  if (ifconfig_local_parm && ifconfig_remote_netmask_parm)
++    {
++      bool tun = false;
++      const char *ifconfig_local = NULL;
++      const char *ifconfig_remote_netmask = NULL;
++      const char *ifconfig_broadcast = NULL;
++
++      /*
++       * We only handle TUN/TAP devices here, not --dev null devices.
++       */
++      tun = is_tun_p2p (tt);
++
++      /*
++       * Convert arguments to binary IPv4 addresses.
++       */
++
++      tt->local = getaddr (
++                         GETADDR_RESOLVE
++                         | GETADDR_HOST_ORDER
++                         | GETADDR_FATAL_ON_SIGNAL
++                         | GETADDR_FATAL,
++                         ifconfig_local_parm,
++                         0,
++                         NULL,
++                         NULL);
++
++      tt->remote_netmask = getaddr (
++                                  (tun ? GETADDR_RESOLVE : 0)
++                                  | GETADDR_HOST_ORDER
++                                  | GETADDR_FATAL_ON_SIGNAL
++                                  | GETADDR_FATAL,
++                                  ifconfig_remote_netmask_parm,
++                                  0,
++                                  NULL,
++                                  NULL);
++
++      /*
++       * Look for common errors in --ifconfig parms
++       */
++      if (strict_warn)
++      {
++        ifconfig_sanity_check (tt->type == DEV_TYPE_TUN, tt->remote_netmask, tt->topology);
++
++        /*
++         * If local_public or remote_public addresses are defined,
++         * make sure they do not clash with our virtual subnet.
++         */
++
++        check_addr_clash ("local",
++                          tt->type,
++                          local_public,
++                          tt->local,
++                          tt->remote_netmask);
++
++        check_addr_clash ("remote",
++                          tt->type,
++                          remote_public,
++                          tt->local,
++                          tt->remote_netmask);
++
++        if (tt->type == DEV_TYPE_TAP || (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET))
++          check_subnet_conflict (tt->local, tt->remote_netmask, "TUN/TAP adapter");
++        else if (tt->type == DEV_TYPE_TUN)
++          check_subnet_conflict (tt->local, ~0, "TUN/TAP adapter");
++      }
++
++      /*
++       * Set ifconfig parameters
++       */
++      ifconfig_local = print_in_addr_t (tt->local, 0, &gc);
++      ifconfig_remote_netmask = print_in_addr_t (tt->remote_netmask, 0, &gc);
++
++      /*
++       * If TAP-style interface, generate broadcast address.
++       */
++      if (!tun)
++      {
++        tt->broadcast = generate_ifconfig_broadcast_addr (tt->local, tt->remote_netmask);
++        ifconfig_broadcast = print_in_addr_t (tt->broadcast, 0, &gc);
++      }
++
++      /*
++       * Set environmental variables with ifconfig parameters.
++       */
++      if (es)
++      {
++        setenv_str (es, "ifconfig_local", ifconfig_local);
++        if (tun)
++          {
++            setenv_str (es, "ifconfig_remote", ifconfig_remote_netmask);
++          }
++        else
++          {
++            setenv_str (es, "ifconfig_netmask", ifconfig_remote_netmask);
++            setenv_str (es, "ifconfig_broadcast", ifconfig_broadcast);
++          }
++      }
++
++      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;
++}
++
++/*
++ * Platform specific tun initializations
++ */
++void
++init_tun_post (struct tuntap *tt,
++             const struct frame *frame,
++             const struct tuntap_options *options)
++{
++  tt->options = *options;
++#ifdef WIN32
++  overlapped_io_init (&tt->reads, frame, FALSE, true);
++  overlapped_io_init (&tt->writes, frame, TRUE, true);
++  tt->rw_handle.read = tt->reads.overlapped.hEvent;
++  tt->rw_handle.write = tt->writes.overlapped.hEvent;
++  tt->adapter_index = ~0;
++#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,
++           const char *actual,    /* actual device name */
++           int tun_mtu,
++           const struct env_set *es)
++{
++  struct gc_arena gc = gc_new ();
++
++  if (tt->did_ifconfig_setup)
++    {
++      bool tun = false;
++      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.
++       */
++      tun = is_tun_p2p (tt);
++
++      /*
++       * Set ifconfig parameters
++       */
++      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.
++       */
++      if (!tun)
++      ifconfig_broadcast = print_in_addr_t (tt->broadcast, 0, &gc);
++
++#ifdef ENABLE_MANAGEMENT
++  if (management)
++    {
++      management_set_state (management,
++                          OPENVPN_STATE_ASSIGN_IP,
++                          NULL,
++                          tt->local,
++                          0);
++    }
++#endif
++
++
++#if defined(TARGET_LINUX)
++#ifdef CONFIG_FEATURE_IPROUTE
++      /*
++       * Set the MTU for the device
++       */
++      argv_printf (&argv,
++                        "%s link set dev %s up mtu %d",
++                        iproute_path,
++                        actual,
++                        tun_mtu
++                        );
++        argv_msg (M_INFO, &argv);
++        openvpn_execve_check (&argv, es, S_FATAL, "Linux ip link set failed");
++
++      if (tun) {
++
++              /*
++               * Set the address for the device
++               */
++              argv_printf (&argv,
++                                "%s addr add dev %s local %s peer %s",
++                                iproute_path,
++                                actual,
++                                ifconfig_local,
++                                ifconfig_remote_netmask
++                                );
++                argv_msg (M_INFO, &argv);
++                openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
++      } else {
++              argv_printf (&argv,
++                                "%s addr add dev %s %s/%d broadcast %s",
++                                iproute_path,
++                                actual,
++                                ifconfig_local,
++                                count_netmask_bits(ifconfig_remote_netmask),
++                                ifconfig_broadcast
++                                );
++                argv_msg (M_INFO, &argv);
++                openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
++      }
++      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,
++                        "%s %s %s pointopoint %s mtu %d",
++                        IFCONFIG_PATH,
++                        actual,
++                        ifconfig_local,
++                        ifconfig_remote_netmask,
++                        tun_mtu
++                        );
++      else
++      argv_printf (&argv,
++                        "%s %s %s netmask %s mtu %d broadcast %s",
++                        IFCONFIG_PATH,
++                        actual,
++                        ifconfig_local,
++                        ifconfig_remote_netmask,
++                        tun_mtu,
++                        ifconfig_broadcast
++                        );
++      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*/
++#elif defined(TARGET_SOLARIS)
++
++      /* Solaris 2.6 (and 7?) cannot set all parameters in one go...
++       * example:
++       *    ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 up
++       *    ifconfig tun2 netmask 255.255.255.255
++       */
++      if (tun)
++      {
++        argv_printf (&argv,
++                          "%s %s %s %s mtu %d up",
++                          IFCONFIG_PATH,
++                          actual,
++                          ifconfig_local,
++                          ifconfig_remote_netmask,
++                          tun_mtu
++                          );
++
++        argv_msg (M_INFO, &argv);
++        if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-1 failed"))
++          solaris_error_close (tt, es, actual, false);
++
++        argv_printf (&argv,
++                          "%s %s netmask 255.255.255.255",
++                          IFCONFIG_PATH,
++                          actual
++                          );
++      }
++      else
++        if (tt->topology == TOP_SUBNET)
++      {
++          argv_printf (&argv,
++                              "%s %s %s %s netmask %s mtu %d up",
++                              IFCONFIG_PATH,
++                              actual,
++                              ifconfig_local,
++                              ifconfig_local,
++                              ifconfig_remote_netmask,
++                              tun_mtu
++                              );
++      }
++        else
++          argv_printf (&argv,
++                            " %s %s %s netmask %s broadcast + up",
++                            IFCONFIG_PATH,
++                            actual,
++                            ifconfig_local,
++                            ifconfig_remote_netmask
++                            );
++
++      argv_msg (M_INFO, &argv);
++      if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-2 failed"))
++      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)
++      {
++        /* Add a network route for the local tun interface */
++        struct route r;
++        CLEAR (r);      
++        r.defined = true;       
++        r.network = tt->local & tt->remote_netmask;
++        r.netmask = tt->remote_netmask;
++        r.gateway = tt->local;  
++        r.metric_defined = true;
++        r.metric = 0;
++        add_route (&r, tt, 0, es);
++      }
++
++      tt->did_ifconfig = true;
++
++#elif defined(TARGET_OPENBSD)
++
++      /*
++       * OpenBSD tun devices appear to be persistent by default.  It seems in order
++       * to make this work correctly, we need to delete the previous instance
++       * (if it exists), and re-ifconfig.  Let me know if you know a better way.
++       */
++
++      argv_printf (&argv,
++                      "%s %s destroy",
++                      IFCONFIG_PATH,
++                      actual);
++      argv_msg (M_INFO, &argv);
++      openvpn_execve_check (&argv, es, 0, NULL);
++      argv_printf (&argv,
++                      "%s %s create",
++                      IFCONFIG_PATH,
++                      actual);
++      argv_msg (M_INFO, &argv);
++      openvpn_execve_check (&argv, es, 0, NULL);
++      msg (M_INFO, "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure");
++
++      /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
++      if (tun)
++      argv_printf (&argv,
++                        "%s %s %s %s mtu %d netmask 255.255.255.255 up",
++                        IFCONFIG_PATH,
++                        actual,
++                        ifconfig_local,
++                        ifconfig_remote_netmask,
++                        tun_mtu
++                        );
++      else
++      argv_printf (&argv,
++                        "%s %s %s netmask %s mtu %d broadcast %s link0",
++                        IFCONFIG_PATH,
++                        actual,
++                        ifconfig_local,
++                        ifconfig_remote_netmask,
++                        tun_mtu,
++                        ifconfig_broadcast
++                        );
++      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",
++                        IFCONFIG_PATH,
++                        actual,
++                        ifconfig_local,
++                        ifconfig_remote_netmask,
++                        tun_mtu
++                        );
++      else
++      /*
++       * NetBSD has distinct tun and tap devices
++       * so we don't need the "link0" extra parameter to specify we want to do 
++       * tunneling at the ethernet level
++       */
++              argv_printf (&argv,
++                        "%s %s %s netmask %s mtu %d broadcast %s",
++                        IFCONFIG_PATH,
++                        actual,
++                        ifconfig_local,
++                        ifconfig_remote_netmask,
++                        tun_mtu,
++                        ifconfig_broadcast
++                        );
++      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)
++
++      /*
++       * Darwin (i.e. Mac OS X) seems to exhibit similar behaviour to OpenBSD...
++       */
++
++      argv_printf (&argv,
++                      "%s %s delete",
++                      IFCONFIG_PATH,
++                      actual);
++      argv_msg (M_INFO, &argv);
++      openvpn_execve_check (&argv, es, 0, NULL);
++      msg (M_INFO, "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure");
++
++
++      /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
++      if (tun)
++      argv_printf (&argv,
++                        "%s %s %s %s mtu %d netmask 255.255.255.255 up",
++                        IFCONFIG_PATH,
++                        actual,
++                        ifconfig_local,
++                        ifconfig_remote_netmask,
++                        tun_mtu
++                        );
++      else
++        {
++          if (tt->topology == TOP_SUBNET)
++          argv_printf (&argv,
++                            "%s %s %s %s netmask %s mtu %d up",
++                            IFCONFIG_PATH,
++                            actual,
++                            ifconfig_local,
++                            ifconfig_local,
++                            ifconfig_remote_netmask,
++                            tun_mtu
++                            );
++        else
++          argv_printf (&argv,
++                            "%s %s %s netmask %s mtu %d up",
++                            IFCONFIG_PATH,
++                            actual,
++                            ifconfig_local,
++                            ifconfig_remote_netmask,
++                            tun_mtu
++                            );
++      }
++      argv_msg (M_INFO, &argv);
++      openvpn_execve_check (&argv, es, S_FATAL, "Mac OS X ifconfig failed");
++      tt->did_ifconfig = true;
++
++      /* Add a network route for the local tun interface */
++      if (!tun && tt->topology == TOP_SUBNET)
++      {
++        struct route r;
++        CLEAR (r);
++        r.defined = true;
++        r.network = tt->local & tt->remote_netmask;
++        r.netmask = tt->remote_netmask;
++        r.gateway = tt->local;
++        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 */
++      if (tun)
++      argv_printf (&argv,
++                        "%s %s %s %s mtu %d netmask 255.255.255.255 up",
++                        IFCONFIG_PATH,
++                        actual,
++                        ifconfig_local,
++                        ifconfig_remote_netmask,
++                        tun_mtu
++                        );
++      else
++      argv_printf (&argv,
++                    "%s %s %s netmask %s mtu %d up",
++                              IFCONFIG_PATH,
++                              actual,
++                              ifconfig_local,
++                              ifconfig_remote_netmask,
++                              tun_mtu
++                              );
++      
++      argv_msg (M_INFO, &argv);
++      openvpn_execve_check (&argv, es, S_FATAL, "FreeBSD ifconfig failed");
++      tt->did_ifconfig = true;
++
++      /* Add a network route for the local tun interface */
++      if (!tun && tt->topology == TOP_SUBNET)
++        {               
++          struct route r;
++          CLEAR (r);      
++          r.defined = true;       
++          r.network = tt->local & tt->remote_netmask;
++          r.netmask = tt->remote_netmask;
++          r.gateway = tt->local;  
++          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)
++      {
++      /*
++       * Make sure that both ifconfig addresses are part of the
++       * same .252 subnet.
++       */
++      if (tun)
++        {
++          verify_255_255_255_252 (tt->local, tt->remote_netmask);
++          tt->adapter_netmask = ~3;
++        }
++      else
++        {
++          tt->adapter_netmask = tt->remote_netmask;
++        }
++
++      switch (tt->options.ip_win32_type)
++        {
++        case IPW32_SET_MANUAL:
++          msg (M_INFO, "******** NOTE:  Please manually set the IP/netmask of '%s' to %s/%s (if it is not already set)",
++               actual,
++               ifconfig_local,
++               print_in_addr_t (tt->adapter_netmask, 0, &gc));
++          break;
++        case IPW32_SET_NETSH:
++          if (!strcmp (actual, "NULL"))
++            msg (M_FATAL, "Error: When using --ip-win32 netsh, if you have more than one TAP-Win32 adapter, you must also specify --dev-node");
++
++          netsh_ifconfig (&tt->options,
++                          actual,
++                          tt->local,
++                          tt->adapter_netmask,
++                          NI_IP_NETMASK|NI_OPTIONS);
++
++          break;
++        }
++      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
++      argv_reset (&argv);
++    }
++  gc_free (&gc);
++}
++
++void
++clear_tuntap (struct tuntap *tuntap)
++{
++  CLEAR (*tuntap);
++#ifdef WIN32
++  tuntap->hand = NULL;
++#else
++  tuntap->fd = -1;
++#endif
++#ifdef TARGET_SOLARIS
++  tuntap->ip_fd = -1;
++#endif
++  tuntap->ipv6 = false;
++}
++
++static void
++open_null (struct tuntap *tt)
++{
++  tt->actual_name = string_alloc ("null", NULL);
++}
++
++#ifndef WIN32
++static void
++open_tun_generic (const char *dev, const char *dev_type, const char *dev_node,
++                bool ipv6_explicitly_supported, bool dynamic,
++                struct tuntap *tt)
++{
++  char tunname[256];
++  char dynamic_name[256];
++  bool dynamic_opened = false;
++
++
++  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)
++    {
++      open_null (tt);
++    }
++  else
++    {
++      /*
++       * --dev-node specified, so open an explicit device node
++       */
++      if (dev_node)
++      {
++        openvpn_snprintf (tunname, sizeof (tunname), "%s", dev_node);
++      }
++      else
++      {
++        /*
++         * dynamic open is indicated by --dev specified without
++         * explicit unit number.  Try opening /dev/[dev]n
++         * where n = [0, 255].
++         */
++        if (dynamic && !has_digit((unsigned char *)dev))
++          {
++            int i;
++            for (i = 0; i < 256; ++i)
++              {
++                openvpn_snprintf (tunname, sizeof (tunname),
++                                  "/dev/%s%d", dev, i);
++                openvpn_snprintf (dynamic_name, sizeof (dynamic_name),
++                                  "%s%d", dev, i);
++                if ((tt->fd = open (tunname, O_RDWR)) > 0)
++                  {
++                    dynamic_opened = true;
++                    break;
++                  }
++                msg (D_READ_WRITE | M_ERRNO, "Tried opening %s (failed)", tunname);
++              }
++            if (!dynamic_opened)
++              msg (M_FATAL, "Cannot allocate TUN/TAP dev dynamically");
++          }
++        /*
++         * explicit unit number specified
++         */
++        else
++          {
++            openvpn_snprintf (tunname, sizeof (tunname), "/dev/%s", dev);
++          }
++      }
++
++      if (!dynamic_opened)
++      {
++        if ((tt->fd = open (tunname, O_RDWR)) < 0)
++          msg (M_ERR, "Cannot open TUN/TAP dev %s", tunname);
++      }
++
++      set_nonblock (tt->fd);
++      set_cloexec (tt->fd); /* don't pass fd to scripts */
++      msg (M_INFO, "TUN/TAP device %s opened", tunname);
++
++      /* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
++      tt->actual_name = string_alloc (dynamic_opened ? dynamic_name : dev, NULL);
++    }
++}
++
++static void
++close_tun_generic (struct tuntap *tt)
++{
++  if (tt->fd >= 0)
++    close (tt->fd);
++  if (tt->actual_name)
++    free (tt->actual_name);
++  clear_tuntap (tt);
++}
++
++#endif
++
++#if defined(TARGET_LINUX)
++
++#ifdef HAVE_LINUX_IF_TUN_H    /* New driver support */
++
++#ifndef HAVE_LINUX_SOCKIOS_H
++#error header file linux/sockios.h required
++#endif
++
++#if defined(HAVE_TUN_PI) && defined(HAVE_IPHDR) && defined(HAVE_IOVEC) && defined(ETH_P_IPV6) && defined(ETH_P_IP) && defined(HAVE_READV) && defined(HAVE_WRITEV)
++#define LINUX_IPV6 1
++/* #warning IPv6 ON */
++#else
++#define LINUX_IPV6 0
++/* #warning IPv6 OFF */
++#endif
++
++#if !PEDANTIC
++
++void
++open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
++{
++  struct ifreq ifr;
++
++  /* warn if a very old linux version is used & --tun-ipv6 set
++   */
++#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.
++   */
++  if (tt->type == DEV_TYPE_NULL)
++    {
++      open_null (tt);
++    }
++  else
++    {
++      /*
++       * Process --dev-node
++       */
++      const char *node = dev_node;
++      if (!node)
++      node = "/dev/net/tun";
++
++      /*
++       * Open the interface
++       */
++      if ((tt->fd = open (node, O_RDWR)) < 0)
++      {
++        msg (M_WARN | M_ERRNO, "Note: Cannot open TUN/TAP dev %s", node);
++        return;
++      }
++
++      /*
++       * Process --tun-ipv6
++       */
++      CLEAR (ifr);
++      if (!tt->ipv6)
++      ifr.ifr_flags = IFF_NO_PI;
++
++#if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN)
++      ifr.ifr_flags |= IFF_ONE_QUEUE;
++#endif
++
++      /*
++       * Figure out if tun or tap device
++       */
++      if (tt->type == DEV_TYPE_TUN)
++      {
++        ifr.ifr_flags |= IFF_TUN;
++      }
++      else if (tt->type == DEV_TYPE_TAP)
++      {
++        ifr.ifr_flags |= IFF_TAP;
++      }
++      else
++      {
++        msg (M_FATAL, "I don't recognize device %s as a tun or tap device",
++             dev);
++      }
++
++      /*
++       * Set an explicit name, if --dev is not tun or tap
++       */
++      if (strcmp(dev, "tun") && strcmp(dev, "tap"))
++      strncpynt (ifr.ifr_name, dev, IFNAMSIZ);
++
++      /*
++       * Use special ioctl that configures tun/tap device with the parms
++       * we set in ifr
++       */
++      if (ioctl (tt->fd, TUNSETIFF, (void *) &ifr) < 0)
++      {
++        msg (M_WARN | M_ERRNO, "Note: Cannot ioctl TUNSETIFF %s", dev);
++        return;
++      }
++
++      msg (M_INFO, "TUN/TAP device %s opened", ifr.ifr_name);
++
++      /*
++       * Try making the TX send queue bigger
++       */
++#if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN)
++      if (tt->options.txqueuelen) {
++      struct ifreq netifr;
++      int ctl_fd;
++
++      if ((ctl_fd = socket (AF_INET, SOCK_DGRAM, 0)) >= 0)
++        {
++          CLEAR (netifr);
++          strncpynt (netifr.ifr_name, ifr.ifr_name, IFNAMSIZ);
++          netifr.ifr_qlen = tt->options.txqueuelen;
++          if (ioctl (ctl_fd, SIOCSIFTXQLEN, (void *) &netifr) >= 0)
++            msg (D_OSBUF, "TUN/TAP TX queue length set to %d", tt->options.txqueuelen);
++          else
++            msg (M_WARN | M_ERRNO, "Note: Cannot set tx queue length on %s", ifr.ifr_name);
++          close (ctl_fd);
++        }
++      else
++        {
++          msg (M_WARN | M_ERRNO, "Note: Cannot open control socket on %s", ifr.ifr_name);
++        }
++      }
++#endif
++
++      set_nonblock (tt->fd);
++      set_cloexec (tt->fd);
++      tt->actual_name = string_alloc (ifr.ifr_name, NULL);
++    }
++  return;
++}
++
++#else
++
++void
++open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
++{
++  ASSERT (0);
++}
++
++#endif
++
++#else
++
++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, ipv6, false, true, tt);
++}
++
++#endif /* HAVE_LINUX_IF_TUN_H */
++
++#ifdef TUNSETPERSIST
++
++/*
++ * This can be removed in future
++ * when all systems will use newer
++ * linux-headers
++ */
++#ifndef TUNSETOWNER
++#define TUNSETOWNER   _IOW('T', 204, int)
++#endif
++#ifndef TUNSETGROUP
++#define TUNSETGROUP   _IOW('T', 206, int)
++#endif
++
++void
++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;
++
++  ALLOC_OBJ (tt, struct tuntap);
++  clear_tuntap (tt);
++  tt->type = dev_type_enum (dev, dev_type);
++  tt->options = *options;
++  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)
++    {
++      struct user_state user_state;
++
++      if (!get_user (username, &user_state))
++        msg (M_ERR, "Cannot get user entry for %s", username);
++      else
++        if (ioctl (tt->fd, TUNSETOWNER, user_state.pw->pw_uid) < 0)
++          msg (M_ERR, "Cannot ioctl TUNSETOWNER(%s) %s", username, dev);
++    }
++  if (groupname != NULL)
++    {
++      struct group_state group_state;
++
++      if (!get_group (groupname, &group_state))
++        msg (M_ERR, "Cannot get group entry for %s", groupname);
++      else
++        if (ioctl (tt->fd, TUNSETGROUP, group_state.gr->gr_gid) < 0)
++          msg (M_ERR, "Cannot ioctl TUNSETOWNER(%s) %s", groupname, dev);
++    }
++  close_tun (tt);
++  msg (M_INFO, "Persist state set to: %s", (persist_mode ? "ON" : "OFF"));
++}
++
++#endif /* TUNSETPERSIST */
++
++void
++close_tun (struct tuntap *tt)
++{
++  if (tt)
++    {
++      if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig)
++        {
++          struct argv argv;
++          struct gc_arena gc = gc_new ();
++          argv_init (&argv);
++
++#ifdef CONFIG_FEATURE_IPROUTE
++          if (is_tun_p2p (tt))
++            {
++              argv_printf (&argv,
++                      "%s addr del dev %s local %s peer %s",
++                      iproute_path,
++                      tt->actual_name,
++                      print_in_addr_t (tt->local, 0, &gc),
++                      print_in_addr_t (tt->remote_netmask, 0, &gc)
++                      );
++            }
++          else
++            {
++              argv_printf (&argv,
++                      "%s addr del dev %s %s/%d",
++                      iproute_path,
++                      tt->actual_name,
++                      print_in_addr_t (tt->local, 0, &gc),
++                      count_netmask_bits(print_in_addr_t (tt->remote_netmask, 0, &gc))
++                      );
++            }
++#else
++          argv_printf (&argv,
++                      "%s %s 0.0.0.0",
++                      IFCONFIG_PATH,
++                      tt->actual_name
++                      );
++#endif
++
++          argv_msg (M_INFO, &argv);
++          openvpn_execve_check (&argv, NULL, 0, "Linux ip addr del failed");
++
++          argv_reset (&argv);
++          gc_free (&gc);
++        }
++      close_tun_generic (tt);
++      free (tt);
++    }
++}
++
++int
++write_tun (struct tuntap* tt, uint8_t *buf, int len)
++{
++#if LINUX_IPV6
++  if (tt->ipv6)
++    {
++      struct tun_pi pi;
++      struct iphdr *iph;
++      struct iovec vect[2];
++      int ret;
++
++      iph = (struct iphdr *)buf;
++
++      pi.flags = 0;
++
++      if(iph->version == 6)
++      pi.proto = htons(ETH_P_IPV6);
++      else
++      pi.proto = htons(ETH_P_IP);
++
++      vect[0].iov_len = sizeof(pi);
++      vect[0].iov_base = &pi;
++      vect[1].iov_len = len;
++      vect[1].iov_base = buf;
++
++      ret = writev(tt->fd, vect, 2);
++      return(ret - sizeof(pi));
++    }
++  else
++#endif
++    return write (tt->fd, buf, len);
++}
++
++int
++read_tun (struct tuntap* tt, uint8_t *buf, int len)
++{
++#if LINUX_IPV6
++  if (tt->ipv6)
++    {
++      struct iovec vect[2];
++      struct tun_pi pi;
++      int ret;
++
++      vect[0].iov_len = sizeof(pi);
++      vect[0].iov_base = &pi;
++      vect[1].iov_len = len;
++      vect[1].iov_base = buf;
++
++      ret = readv(tt->fd, vect, 2);
++      return(ret - sizeof(pi));
++    }
++  else
++#endif
++    return read (tt->fd, buf, len);
++}
++
++#elif defined(TARGET_SOLARIS)
++
++#ifndef TUNNEWPPA
++#error I need the symbol TUNNEWPPA from net/if_tun.h
++#endif
++
++void
++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;
++  const char *ptr;
++  const char *ip_node, *arp_node;
++  const char *dev_tuntap_type;
++  int link_type;
++  bool is_tun;
++  struct strioctl  strioc_if, strioc_ppa;
++
++  /* improved generic TUN/TAP driver from
++   * http://www.whiteboard.ne.jp/~admin2/tuntap/
++   * has IPv6 support
++   */
++  CLEAR(ifr);
++
++  if (tt->type == DEV_TYPE_NULL)
++    {
++      open_null (tt);
++      return;
++    }
++
++  if (tt->type == DEV_TYPE_TUN)
++    {
++      ip_node = "/dev/udp";
++      if (!dev_node)
++      dev_node = "/dev/tun";
++      dev_tuntap_type = "tun";
++      link_type = I_PLINK;
++      is_tun = true;
++    }
++  else if (tt->type == DEV_TYPE_TAP)
++    {
++      ip_node = "/dev/udp";
++      if (!dev_node)
++      dev_node = "/dev/tap";
++      arp_node = dev_node;
++      dev_tuntap_type = "tap";
++      link_type = I_PLINK; /* was: I_LINK */
++      is_tun = false;
++    }
++  else
++    {
++      msg (M_FATAL, "I don't recognize device %s as a tun or tap device",
++         dev);
++    }
++  
++  /* get unit number */
++  if (*dev)
++    {
++      ptr = dev;
++      while (*ptr && !isdigit ((int) *ptr))
++      ptr++;
++      ppa = atoi (ptr);
++    }
++
++  if ((tt->ip_fd = open (ip_node, O_RDWR, 0)) < 0)
++    msg (M_ERR, "Can't open %s", ip_node);
++
++  if ((tt->fd = open (dev_node, O_RDWR, 0)) < 0)
++    msg (M_ERR, "Can't open %s", dev_node);
++
++  /* Assign a new PPA and get its unit number. */
++  strioc_ppa.ic_cmd = TUNNEWPPA;
++  strioc_ppa.ic_timout = 0;
++  strioc_ppa.ic_len = sizeof(ppa);
++  strioc_ppa.ic_dp = (char *)&ppa;
++  if ((ppa = ioctl (tt->fd, I_STR, &strioc_ppa)) < 0)
++    msg (M_ERR, "Can't assign new interface");
++
++  if ((if_fd = open (dev_node, O_RDWR, 0)) < 0)
++    msg (M_ERR, "Can't open %s (2)", dev_node);
++
++  if (ioctl (if_fd, I_PUSH, "ip") < 0)
++    msg (M_ERR, "Can't push IP module");
++
++  if (tt->type == DEV_TYPE_TUN)
++    {
++  /* Assign ppa according to the unit number returned by tun device */
++  if (ioctl (if_fd, IF_UNITSEL, (char *) &ppa) < 0)
++    msg (M_ERR, "Can't set PPA %d", ppa);
++    }
++
++  tt->actual_name = (char *) malloc (32);
++  check_malloc_return (tt->actual_name);
++
++  openvpn_snprintf (tt->actual_name, 32, "%s%d", dev_tuntap_type, ppa);
++
++  if (tt->type == DEV_TYPE_TAP)
++    {
++          if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0)
++            msg (M_ERR, "Can't get flags\n");
++          strncpynt (ifr.lifr_name, tt->actual_name, sizeof (ifr.lifr_name));
++          ifr.lifr_ppa = ppa;
++          /* Assign ppa according to the unit number returned by tun device */
++          if (ioctl (if_fd, SIOCSLIFNAME, &ifr) < 0)
++            msg (M_ERR, "Can't set PPA %d", ppa);
++          if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) <0)
++            msg (M_ERR, "Can't get flags\n");
++          /* Push arp module to if_fd */
++          if (ioctl (if_fd, I_PUSH, "arp") < 0)
++            msg (M_ERR, "Can't push ARP module");
++
++          /* Pop any modules on the stream */
++          while (true)
++            {
++                 if (ioctl (tt->ip_fd, I_POP, NULL) < 0)
++                     break;
++            }
++          /* Push arp module to ip_fd */
++          if (ioctl (tt->ip_fd, I_PUSH, "arp") < 0)
++            msg (M_ERR, "Can't push ARP module\n");
++
++          /* Open arp_fd */
++          if ((arp_fd = open (arp_node, O_RDWR, 0)) < 0)
++            msg (M_ERR, "Can't open %s\n", arp_node);
++          /* Push arp module to arp_fd */
++          if (ioctl (arp_fd, I_PUSH, "arp") < 0)
++            msg (M_ERR, "Can't push ARP module\n");
++
++          /* Set ifname to arp */
++          strioc_if.ic_cmd = SIOCSLIFNAME;
++          strioc_if.ic_timout = 0;
++          strioc_if.ic_len = sizeof(ifr);
++          strioc_if.ic_dp = (char *)&ifr;
++          if (ioctl(arp_fd, I_STR, &strioc_if) < 0){
++              msg (M_ERR, "Can't set ifname to arp\n");
++          }
++   }
++
++  if ((ip_muxid = ioctl (tt->ip_fd, link_type, if_fd)) < 0)
++    msg (M_ERR, "Can't link %s device to IP", dev_tuntap_type);
++
++  if (tt->type == DEV_TYPE_TAP) {
++          if ((arp_muxid = ioctl (tt->ip_fd, link_type, arp_fd)) < 0)
++            msg (M_ERR, "Can't link %s device to ARP", dev_tuntap_type);
++          close (arp_fd);
++  }
++
++  CLEAR (ifr);
++  strncpynt (ifr.lifr_name, tt->actual_name, sizeof (ifr.lifr_name));
++  ifr.lifr_ip_muxid  = ip_muxid;
++  if (tt->type == DEV_TYPE_TAP) {
++          ifr.lifr_arp_muxid = arp_muxid;
++  }
++
++  if (ioctl (tt->ip_fd, SIOCSLIFMUXID, &ifr) < 0)
++    {
++      if (tt->type == DEV_TYPE_TAP)
++        {
++              ioctl (tt->ip_fd, I_PUNLINK , arp_muxid);
++        }
++      ioctl (tt->ip_fd, I_PUNLINK, ip_muxid);
++      msg (M_ERR, "Can't set multiplexor id");
++    }
++
++  set_nonblock (tt->fd);
++  set_cloexec (tt->fd);
++  set_cloexec (tt->ip_fd);
++
++  msg (M_INFO, "TUN/TAP device %s opened", tt->actual_name);
++}
++
++static void
++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;
++        CLEAR (ifr);
++          strncpynt (ifr.lifr_name, tt->actual_name, sizeof (ifr.lifr_name));
++
++          if (ioctl (tt->ip_fd, SIOCGLIFFLAGS, &ifr) < 0)
++          msg (M_WARN | M_ERRNO, "Can't get iface flags");
++
++          if (ioctl (tt->ip_fd, SIOCGLIFMUXID, &ifr) < 0)
++          msg (M_WARN | M_ERRNO, "Can't get multiplexor id");
++
++          if (tt->type == DEV_TYPE_TAP)
++            {
++                  if (ioctl (tt->ip_fd, I_PUNLINK, ifr.lifr_arp_muxid) < 0)
++                    msg (M_WARN | M_ERRNO, "Can't unlink interface(arp)");
++            }
++
++          if (ioctl (tt->ip_fd, I_PUNLINK, ifr.lifr_ip_muxid) < 0)
++            msg (M_WARN | M_ERRNO, "Can't unlink interface(ip)");
++
++        close (tt->ip_fd);
++        tt->ip_fd = -1;
++      }
++
++      if (tt->fd >= 0)
++      {
++        close (tt->fd);
++        tt->fd = -1;
++      }
++    }
++}
++
++/*
++ * Close TUN device. 
++ */
++void
++close_tun (struct tuntap *tt)
++{
++  if (tt)
++    {
++      solaris_close_tun (tt);
++
++      if (tt->actual_name)
++      free (tt->actual_name);
++      
++      clear_tuntap (tt);
++      free (tt);
++    }
++}
++
++static void
++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,
++                  actual);
++
++  argv_msg (M_INFO, &argv);
++  openvpn_execve_check (&argv, es, 0, "Solaris ifconfig unplumb failed");
++  close_tun (tt);
++  msg (M_FATAL, "Solaris ifconfig failed");
++  argv_reset (&argv);
++}
++
++int
++write_tun (struct tuntap* tt, uint8_t *buf, int len)
++{
++  struct strbuf sbuf;
++  sbuf.len = len;
++  sbuf.buf = (char *)buf;
++  return putmsg (tt->fd, NULL, &sbuf, 0) >= 0 ? sbuf.len : -1;
++}
++
++int
++read_tun (struct tuntap* tt, uint8_t *buf, int len)
++{
++  struct strbuf sbuf;
++  int f = 0;
++
++  sbuf.maxlen = len;
++  sbuf.buf = (char *)buf;
++  return getmsg (tt->fd, NULL, &sbuf, &f) >= 0 ? sbuf.len : -1;
++}
++
++#elif defined(TARGET_OPENBSD)
++
++#if !defined(HAVE_READV) || !defined(HAVE_WRITEV)
++#error openbsd build requires readv & writev library functions
++#endif
++
++/*
++ * OpenBSD has a slightly incompatible TUN device from
++ * the rest of the world, in that it prepends a
++ * uint32 to the beginning of the IP header
++ * to designate the protocol (why not just
++ * look at the version field in the IP header to
++ * determine v4 or v6?).
++ *
++ * We strip off this field on reads and
++ * put it back on writes.
++ *
++ * I have not tested TAP devices on OpenBSD,
++ * but I have conditionalized the special
++ * TUN handling code described above to
++ * go away for TAP devices.
++ */
++
++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, true, true, tt);
++
++  /* Enable multicast on the interface */
++  if (tt->fd >= 0)
++    {
++      struct tuninfo info;
++
++      if (ioctl (tt->fd, TUNGIFINFO, &info) < 0) {
++      msg (M_WARN | M_ERRNO, "Can't get interface info: %s",
++        strerror(errno));
++      }
++
++      info.flags |= IFF_MULTICAST;
++
++      if (ioctl (tt->fd, TUNSIFINFO, &info) < 0) {
++      msg (M_WARN | M_ERRNO, "Can't set interface info: %s",
++        strerror(errno));
++      }
++    }
++}
++
++/* 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);
++    }
++}
++
++static inline int
++openbsd_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 ip *iph;
++
++      iph = (struct ip *) buf;
++
++      if (tt->ipv6 && iph->ip_v == 6)
++      type = htonl (AF_INET6);
++      else 
++      type = htonl (AF_INET);
++
++      iv[0].iov_base = &type;
++      iv[0].iov_len = sizeof (type);
++      iv[1].iov_base = buf;
++      iv[1].iov_len = len;
++
++      return openbsd_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 = &type;
++      iv[0].iov_len = sizeof (type);
++      iv[1].iov_base = buf;
++      iv[1].iov_len = len;
++
++      return openbsd_modify_read_write_return (readv (tt->fd, iv, 2));
++    }
++  else
++    return read (tt->fd, buf, len);
++}
++
++#elif defined(TARGET_NETBSD)
++
++/*
++ * 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, struct tuntap *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);
++      free (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)
++{
++    return write (tt->fd, buf, len);
++}
++
++int
++read_tun (struct tuntap* tt, uint8_t *buf, int len)
++{
++    return read (tt->fd, buf, len);
++}
++#endif        /* NETBSD_MULTI_AF */
++
++#elif defined(TARGET_FREEBSD)
++
++static inline int
++freebsd_modify_read_write_return (int len)
++{
++  if (len > 0)
++    return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0;
++  else
++    return len;
++}
++
++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, true, true, tt);
++
++  if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
++    {
++      int i = 0;
++
++      i = tt->topology == TOP_SUBNET ? IFF_BROADCAST : IFF_POINTOPOINT;
++      i |= IFF_MULTICAST;
++      if (ioctl (tt->fd, TUNSIFMODE, &i) < 0) {
++      msg (M_WARN | M_ERRNO, "ioctl(TUNSIFMODE): %s", strerror(errno));
++      }
++      i = 1;
++      if (ioctl (tt->fd, TUNSIFHEAD, &i) < 0) {
++      msg (M_WARN | M_ERRNO, "ioctl(TUNSIFHEAD): %s", strerror(errno));
++      }
++    }
++}
++
++void
++close_tun (struct tuntap *tt)
++{
++  if (tt)
++    {
++      close_tun_generic (tt);
++      free (tt);
++    }
++}
++
++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 ip *iph;
++
++      iph = (struct ip *) buf;
++
++      if (tt->ipv6 && iph->ip_v == 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 freebsd_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 freebsd_modify_read_write_return (readv (tt->fd, iv, 2));
++    }
++  else
++    return read (tt->fd, buf, len);
++}
++
++#elif defined(TARGET_DRAGONFLY)
++
++static inline int
++dragonfly_modify_read_write_return (int len)
++{
++  if (len > 0)
++    return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0;
++  else
++    return len;
++}
++
++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, true, true, tt);
++
++  if (tt->fd >= 0)
++    {
++      int i = 0;
++
++      /* Disable extended modes */
++      ioctl (tt->fd, TUNSLMODE, &i);
++      i = 1;
++      ioctl (tt->fd, TUNSIFHEAD, &i);
++    }
++}
++
++void
++close_tun (struct tuntap *tt)
++{
++  if (tt)
++    {
++      close_tun_generic (tt);
++      free (tt);
++    }
++}
++
++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 ip *iph;
++
++      iph = (struct ip *) buf;
++
++      if (tt->ipv6 && iph->ip_v == 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 dragonfly_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 dragonfly_modify_read_write_return (readv (tt->fd, iv, 2));
++    }
++  else
++    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
++tun_read_queue (struct tuntap *tt, int maxsize)
++{
++  if (tt->reads.iostate == IOSTATE_INITIAL)
++    {
++      DWORD len;
++      BOOL status;
++      int err;
++
++      /* reset buf to its initial state */
++      tt->reads.buf = tt->reads.buf_init;
++
++      len = maxsize ? maxsize : BLEN (&tt->reads.buf);
++      ASSERT (len <= BLEN (&tt->reads.buf));
++
++      /* the overlapped read will signal this event on I/O completion */
++      ASSERT (ResetEvent (tt->reads.overlapped.hEvent));
++
++      status = ReadFile(
++                    tt->hand,
++                    BPTR (&tt->reads.buf),
++                    len,
++                    &tt->reads.size,
++                    &tt->reads.overlapped
++                    );
++
++      if (status) /* operation completed immediately? */
++      {
++        /* since we got an immediate return, we must signal the event object ourselves */
++        ASSERT (SetEvent (tt->reads.overlapped.hEvent));
++
++        tt->reads.iostate = IOSTATE_IMMEDIATE_RETURN;
++        tt->reads.status = 0;
++
++        dmsg (D_WIN32_IO, "WIN32 I/O: TAP Read immediate return [%d,%d]",
++             (int) len,
++             (int) tt->reads.size);          
++      }
++      else
++      {
++        err = GetLastError (); 
++        if (err == ERROR_IO_PENDING) /* operation queued? */
++          {
++            tt->reads.iostate = IOSTATE_QUEUED;
++            tt->reads.status = err;
++            dmsg (D_WIN32_IO, "WIN32 I/O: TAP Read queued [%d]",
++                 (int) len);
++          }
++        else /* error occurred */
++          {
++            struct gc_arena gc = gc_new ();
++            ASSERT (SetEvent (tt->reads.overlapped.hEvent));
++            tt->reads.iostate = IOSTATE_IMMEDIATE_RETURN;
++            tt->reads.status = err;
++            dmsg (D_WIN32_IO, "WIN32 I/O: TAP Read error [%d] : %s",
++                 (int) len,
++                 strerror_win32 (status, &gc));
++            gc_free (&gc);
++          }
++      }
++    }
++  return tt->reads.iostate;
++}
++
++int
++tun_write_queue (struct tuntap *tt, struct buffer *buf)
++{
++  if (tt->writes.iostate == IOSTATE_INITIAL)
++    {
++      BOOL status;
++      int err;
++ 
++      /* make a private copy of buf */
++      tt->writes.buf = tt->writes.buf_init;
++      tt->writes.buf.len = 0;
++      ASSERT (buf_copy (&tt->writes.buf, buf));
++
++      /* the overlapped write will signal this event on I/O completion */
++      ASSERT (ResetEvent (tt->writes.overlapped.hEvent));
++
++      status = WriteFile(
++                      tt->hand,
++                      BPTR (&tt->writes.buf),
++                      BLEN (&tt->writes.buf),
++                      &tt->writes.size,
++                      &tt->writes.overlapped
++                      );
++
++      if (status) /* operation completed immediately? */
++      {
++        tt->writes.iostate = IOSTATE_IMMEDIATE_RETURN;
++
++        /* since we got an immediate return, we must signal the event object ourselves */
++        ASSERT (SetEvent (tt->writes.overlapped.hEvent));
++
++        tt->writes.status = 0;
++
++        dmsg (D_WIN32_IO, "WIN32 I/O: TAP Write immediate return [%d,%d]",
++             BLEN (&tt->writes.buf),
++             (int) tt->writes.size);         
++      }
++      else
++      {
++        err = GetLastError (); 
++        if (err == ERROR_IO_PENDING) /* operation queued? */
++          {
++            tt->writes.iostate = IOSTATE_QUEUED;
++            tt->writes.status = err;
++            dmsg (D_WIN32_IO, "WIN32 I/O: TAP Write queued [%d]",
++                 BLEN (&tt->writes.buf));
++          }
++        else /* error occurred */
++          {
++            struct gc_arena gc = gc_new ();
++            ASSERT (SetEvent (tt->writes.overlapped.hEvent));
++            tt->writes.iostate = IOSTATE_IMMEDIATE_RETURN;
++            tt->writes.status = err;
++            dmsg (D_WIN32_IO, "WIN32 I/O: TAP Write error [%d] : %s",
++                 BLEN (&tt->writes.buf),
++                 strerror_win32 (err, &gc));
++            gc_free (&gc);
++          }
++      }
++    }
++  return tt->writes.iostate;
++}
++
++int
++tun_finalize (
++            HANDLE h,
++            struct overlapped_io *io,
++            struct buffer *buf)
++{
++  int ret = -1;
++  BOOL status;
++
++  switch (io->iostate)
++    {
++    case IOSTATE_QUEUED:
++      status = GetOverlappedResult(
++                                 h,
++                                 &io->overlapped,
++                                 &io->size,
++                                 FALSE
++                                 );
++      if (status)
++      {
++        /* successful return for a queued operation */
++        if (buf)
++          *buf = io->buf;
++        ret = io->size;
++        io->iostate = IOSTATE_INITIAL;
++        ASSERT (ResetEvent (io->overlapped.hEvent));
++        dmsg (D_WIN32_IO, "WIN32 I/O: TAP Completion success [%d]", ret);
++      }
++      else
++      {
++        /* error during a queued operation */
++        ret = -1;
++        if (GetLastError() != ERROR_IO_INCOMPLETE)
++          {
++            /* if no error (i.e. just not finished yet),
++               then DON'T execute this code */
++            io->iostate = IOSTATE_INITIAL;
++            ASSERT (ResetEvent (io->overlapped.hEvent));
++            msg (D_WIN32_IO | M_ERRNO, "WIN32 I/O: TAP Completion error");
++          }
++      }
++      break;
++
++    case IOSTATE_IMMEDIATE_RETURN:
++      io->iostate = IOSTATE_INITIAL;
++      ASSERT (ResetEvent (io->overlapped.hEvent));
++      if (io->status)
++      {
++        /* error return for a non-queued operation */
++        SetLastError (io->status);
++        ret = -1;
++        msg (D_WIN32_IO | M_ERRNO, "WIN32 I/O: TAP Completion non-queued error");
++      }
++      else
++      {
++        /* successful return for a non-queued operation */
++        if (buf)
++          *buf = io->buf;
++        ret = io->size;
++        dmsg (D_WIN32_IO, "WIN32 I/O: TAP Completion non-queued success [%d]", ret);
++      }
++      break;
++
++    case IOSTATE_INITIAL: /* were we called without proper queueing? */
++      SetLastError (ERROR_INVALID_FUNCTION);
++      ret = -1;
++      dmsg (D_WIN32_IO, "WIN32 I/O: TAP Completion BAD STATE");
++      break;
++
++    default:
++      ASSERT (0);
++    }
++
++  if (buf)
++    buf->len = ret;
++  return ret;
++}
++
++const struct tap_reg *
++get_tap_reg (struct gc_arena *gc)
++{
++  HKEY adapter_key;
++  LONG status;
++  DWORD len;
++  struct tap_reg *first = NULL;
++  struct tap_reg *last = NULL;
++  int i = 0;
++
++  status = RegOpenKeyEx(
++                      HKEY_LOCAL_MACHINE,
++                      ADAPTER_KEY,
++                      0,
++                      KEY_READ,
++                      &adapter_key);
++
++  if (status != ERROR_SUCCESS)
++    msg (M_FATAL, "Error opening registry key: %s", ADAPTER_KEY);
++
++  while (true)
++    {
++      char enum_name[256];
++      char unit_string[256];
++      HKEY unit_key;
++      char component_id_string[] = "ComponentId";
++      char component_id[256];
++      char net_cfg_instance_id_string[] = "NetCfgInstanceId";
++      char net_cfg_instance_id[256];
++      DWORD data_type;
++
++      len = sizeof (enum_name);
++      status = RegEnumKeyEx(
++                          adapter_key,
++                          i,
++                          enum_name,
++                          &len,
++                          NULL,
++                          NULL,
++                          NULL,
++                          NULL);
++      if (status == ERROR_NO_MORE_ITEMS)
++      break;
++      else if (status != ERROR_SUCCESS)
++      msg (M_FATAL, "Error enumerating registry subkeys of key: %s",
++           ADAPTER_KEY);
++
++      openvpn_snprintf (unit_string, sizeof(unit_string), "%s\\%s",
++                      ADAPTER_KEY, enum_name);
++
++      status = RegOpenKeyEx(
++                          HKEY_LOCAL_MACHINE,
++                          unit_string,
++                          0,
++                          KEY_READ,
++                          &unit_key);
++
++      if (status != ERROR_SUCCESS)
++      dmsg (D_REGISTRY, "Error opening registry key: %s", unit_string);
++      else
++      {
++        len = sizeof (component_id);
++        status = RegQueryValueEx(
++                                 unit_key,
++                                 component_id_string,
++                                 NULL,
++                                 &data_type,
++                                 component_id,
++                                 &len);
++
++        if (status != ERROR_SUCCESS || data_type != REG_SZ)
++          dmsg (D_REGISTRY, "Error opening registry key: %s\\%s",
++               unit_string, component_id_string);
++        else
++          {         
++            len = sizeof (net_cfg_instance_id);
++            status = RegQueryValueEx(
++                                     unit_key,
++                                     net_cfg_instance_id_string,
++                                     NULL,
++                                     &data_type,
++                                     net_cfg_instance_id,
++                                     &len);
++
++            if (status == ERROR_SUCCESS && data_type == REG_SZ)
++              {
++                if (!strcmp (component_id, TAP_COMPONENT_ID))
++                  {
++                    struct tap_reg *reg;
++                    ALLOC_OBJ_CLEAR_GC (reg, struct tap_reg, gc);
++                    reg->guid = string_alloc (net_cfg_instance_id, gc);
++                    
++                    /* link into return list */
++                    if (!first)
++                      first = reg;
++                    if (last)
++                      last->next = reg;
++                    last = reg;
++                  }
++              }
++          }
++        RegCloseKey (unit_key);
++      }
++      ++i;
++    }
++
++  RegCloseKey (adapter_key);
++  return first;
++}
++
++const struct panel_reg *
++get_panel_reg (struct gc_arena *gc)
++{
++  LONG status;
++  HKEY network_connections_key;
++  DWORD len;
++  struct panel_reg *first = NULL;
++  struct panel_reg *last = NULL;
++  int i = 0;
++
++  status = RegOpenKeyEx(
++                      HKEY_LOCAL_MACHINE,
++                      NETWORK_CONNECTIONS_KEY,
++                      0,
++                      KEY_READ,
++                      &network_connections_key);
++
++  if (status != ERROR_SUCCESS)
++    msg (M_FATAL, "Error opening registry key: %s", NETWORK_CONNECTIONS_KEY);
++
++  while (true)
++    {
++      char enum_name[256];
++      char connection_string[256];
++      HKEY connection_key;
++      char name_data[256];
++      DWORD name_type;
++      const char name_string[] = "Name";
++
++      len = sizeof (enum_name);
++      status = RegEnumKeyEx(
++                          network_connections_key,
++                          i,
++                          enum_name,
++                          &len,
++                          NULL,
++                          NULL,
++                          NULL,
++                          NULL);
++      if (status == ERROR_NO_MORE_ITEMS)
++      break;
++      else if (status != ERROR_SUCCESS)
++      msg (M_FATAL, "Error enumerating registry subkeys of key: %s",
++           NETWORK_CONNECTIONS_KEY);
++
++      openvpn_snprintf (connection_string, sizeof(connection_string),
++                      "%s\\%s\\Connection",
++                      NETWORK_CONNECTIONS_KEY, enum_name);
++
++      status = RegOpenKeyEx(
++                          HKEY_LOCAL_MACHINE,
++                          connection_string,
++                          0,
++                          KEY_READ,
++                          &connection_key);
++
++      if (status != ERROR_SUCCESS)
++      dmsg (D_REGISTRY, "Error opening registry key: %s", connection_string);
++      else
++      {
++        len = sizeof (name_data);
++        status = RegQueryValueEx(
++                                 connection_key,
++                                 name_string,
++                                 NULL,
++                                 &name_type,
++                                 name_data,
++                                 &len);
++
++        if (status != ERROR_SUCCESS || name_type != REG_SZ)
++          dmsg (D_REGISTRY, "Error opening registry key: %s\\%s\\%s",
++               NETWORK_CONNECTIONS_KEY, connection_string, name_string);
++        else
++          {
++            struct panel_reg *reg;
++
++            ALLOC_OBJ_CLEAR_GC (reg, struct panel_reg, gc);
++            reg->name = string_alloc (name_data, gc);
++            reg->guid = string_alloc (enum_name, gc);
++                    
++            /* link into return list */
++            if (!first)
++              first = reg;
++            if (last)
++              last->next = reg;
++            last = reg;
++          }
++        RegCloseKey (connection_key);
++      }
++      ++i;
++    }
++
++  RegCloseKey (network_connections_key);
++
++  return first;
++}
++
++/*
++ * Check that two addresses are part of the same 255.255.255.252 subnet.
++ */
++void
++verify_255_255_255_252 (in_addr_t local, in_addr_t remote)
++{
++  struct gc_arena gc = gc_new ();
++  const unsigned int mask = 3;
++  const char *err = NULL;
++
++  if (local == remote)
++    {
++      err = "must be different";
++      goto error;
++    }
++  if ((local & (~mask)) != (remote & (~mask)))
++    {
++      err = "must exist within the same 255.255.255.252 subnet.  This is a limitation of --dev tun when used with the TAP-WIN32 driver";
++      goto error;
++    }
++  if ((local & mask) == 0
++      || (local & mask) == 3
++      || (remote & mask) == 0
++      || (remote & mask) == 3)
++    {
++      err = "cannot use the first or last address within a given 255.255.255.252 subnet.  This is a limitation of --dev tun when used with the TAP-WIN32 driver";
++      goto error;
++    }
++
++  gc_free (&gc);
++  return;
++
++ error:
++  msg (M_FATAL, "There is a problem in your selection of --ifconfig endpoints [local=%s, remote=%s].  The local and remote VPN endpoints %s.  Try '" PACKAGE " --show-valid-subnets' option for more info.",
++       print_in_addr_t (local, 0, &gc),
++       print_in_addr_t (remote, 0, &gc),
++       err);
++  gc_free (&gc);
++}
++
++void show_valid_win32_tun_subnets (void)
++{
++  int i;
++  int col = 0;
++
++  printf ("On Windows, point-to-point IP support (i.e. --dev tun)\n");
++  printf ("is emulated by the TAP-Win32 driver.  The major limitation\n");
++  printf ("imposed by this approach is that the --ifconfig local and\n");
++  printf ("remote endpoints must be part of the same 255.255.255.252\n");
++  printf ("subnet.  The following list shows examples of endpoint\n");
++  printf ("pairs which satisfy this requirement.  Only the final\n");
++  printf ("component of the IP address pairs is at issue.\n\n");
++  printf ("As an example, the following option would be correct:\n");
++  printf ("    --ifconfig 10.7.0.5 10.7.0.6 (on host A)\n");
++  printf ("    --ifconfig 10.7.0.6 10.7.0.5 (on host B)\n");
++  printf ("because [5,6] is part of the below list.\n\n");
++
++  for (i = 0; i < 256; i += 4)
++    {
++      printf("[%3d,%3d] ", i+1, i+2);
++      if (++col > 4)
++      {
++        col = 0;
++        printf ("\n");
++      }
++    }
++  if (col)
++    printf ("\n");
++}
++
++void
++show_tap_win32_adapters (int msglev, int warnlev)
++{
++  struct gc_arena gc = gc_new ();
++
++  bool warn_panel_null = false;
++  bool warn_panel_dup = false;
++  bool warn_tap_dup = false;
++
++  int links;
++
++  const struct tap_reg *tr;
++  const struct tap_reg *tr1;
++  const struct panel_reg *pr;
++
++  const struct tap_reg *tap_reg = get_tap_reg (&gc);
++  const struct panel_reg *panel_reg = get_panel_reg (&gc);
++
++  msg (msglev, "Available TAP-WIN32 adapters [name, GUID]:");
++
++  /* loop through each TAP-Win32 adapter registry entry */
++  for (tr = tap_reg; tr != NULL; tr = tr->next)
++    {
++      links = 0;
++
++      /* loop through each network connections entry in the control panel */
++      for (pr = panel_reg; pr != NULL; pr = pr->next)
++      {
++        if (!strcmp (tr->guid, pr->guid))
++          {
++            msg (msglev, "'%s' %s", pr->name, tr->guid);
++            ++links;
++          }
++      }
++
++      if (links > 1)
++      {
++        warn_panel_dup = true;
++      }
++      else if (links == 0)
++      {
++        /* a TAP adapter exists without a link from the network
++           connections control panel */
++        warn_panel_null = true;
++        msg (msglev, "[NULL] %s", tr->guid);
++      }
++    }
++
++  /* check for TAP-Win32 adapter duplicated GUIDs */
++  for (tr = tap_reg; tr != NULL; tr = tr->next)
++    {
++      for (tr1 = tap_reg; tr1 != NULL; tr1 = tr1->next)
++      {
++        if (tr != tr1 && !strcmp (tr->guid, tr1->guid))
++          warn_tap_dup = true;
++      }
++    }
++
++  /* warn on registry inconsistencies */
++  if (warn_tap_dup)
++    msg (warnlev, "WARNING: Some TAP-Win32 adapters have duplicate GUIDs");
++
++  if (warn_panel_dup)
++    msg (warnlev, "WARNING: Some TAP-Win32 adapters have duplicate links from the Network Connections control panel");
++
++  if (warn_panel_null)
++    msg (warnlev, "WARNING: Some TAP-Win32 adapters have no link from the Network Connections control panel");
++
++  gc_free (&gc);
++}
++
++/*
++ * Confirm that GUID is a TAP-Win32 adapter.
++ */
++static bool
++is_tap_win32 (const char *guid, const struct tap_reg *tap_reg)
++{
++  const struct tap_reg *tr;
++
++  for (tr = tap_reg; tr != NULL; tr = tr->next)
++    {
++      if (guid && !strcmp (tr->guid, guid))
++      return true;
++    }
++
++  return false;
++}
++
++static const char *
++guid_to_name (const char *guid, const struct panel_reg *panel_reg)
++{
++  const struct panel_reg *pr;
++
++  for (pr = panel_reg; pr != NULL; pr = pr->next)
++    {
++      if (guid && !strcmp (pr->guid, guid))
++      return pr->name;
++    }
++
++  return NULL;
++}
++
++static const char *
++name_to_guid (const char *name, const struct tap_reg *tap_reg, const struct panel_reg *panel_reg)
++{
++  const struct panel_reg *pr;
++
++  for (pr = panel_reg; pr != NULL; pr = pr->next)
++    {
++      if (name && !strcmp (pr->name, name) && is_tap_win32 (pr->guid, tap_reg))
++      return pr->guid;
++    }
++
++  return NULL;
++}
++
++static void
++at_least_one_tap_win32 (const struct tap_reg *tap_reg)
++{
++  if (!tap_reg)
++    msg (M_FATAL, "There are no TAP-Win32 adapters on this system.  You should be able to create a TAP-Win32 adapter by going to Start -> All Programs -> " PACKAGE_NAME " -> Add a new TAP-Win32 virtual ethernet adapter.");
++}
++
++/*
++ * Get an adapter GUID and optional actual_name from the 
++ * registry for the TAP device # = device_number.
++ */
++static const char *
++get_unspecified_device_guid (const int device_number,
++                           char *actual_name,
++                           int actual_name_size,
++                           const struct tap_reg *tap_reg_src,
++                           const struct panel_reg *panel_reg_src,
++                           struct gc_arena *gc)
++{
++  const struct tap_reg *tap_reg = tap_reg_src;
++  struct buffer ret = clear_buf ();
++  struct buffer actual = clear_buf ();
++  int i;
++
++  ASSERT (device_number >= 0);
++
++  /* Make sure we have at least one TAP adapter */
++  if (!tap_reg)
++    return NULL;
++
++  /* The actual_name output buffer may be NULL */
++  if (actual_name)
++    {
++      ASSERT (actual_name_size > 0);
++      buf_set_write (&actual, actual_name, actual_name_size);
++    }
++
++  /* Move on to specified device number */
++  for (i = 0; i < device_number; i++)
++    {
++      tap_reg = tap_reg->next;
++      if (!tap_reg)
++      return NULL;
++    }
++
++  /* Save Network Panel name (if exists) in actual_name */
++  if (actual_name)
++    {
++      const char *act = guid_to_name (tap_reg->guid, panel_reg_src);
++      if (act)
++      buf_printf (&actual, "%s", act);
++      else
++      buf_printf (&actual, "%s", tap_reg->guid);
++    }
++
++  /* Save GUID for return value */
++  ret = alloc_buf_gc (256, gc);
++  buf_printf (&ret, "%s", tap_reg->guid);
++  return BSTR (&ret);
++}
++
++/*
++ * Lookup a --dev-node adapter name in the registry
++ * returning the GUID and optional actual_name.
++ */
++static const char *
++get_device_guid (const char *name,
++               char *actual_name,
++               int actual_name_size,
++               const struct tap_reg *tap_reg,
++               const struct panel_reg *panel_reg,
++               struct gc_arena *gc)
++{
++  struct buffer ret = alloc_buf_gc (256, gc);
++  struct buffer actual = clear_buf ();
++
++  /* Make sure we have at least one TAP adapter */
++  if (!tap_reg)
++    return NULL;
++
++  /* The actual_name output buffer may be NULL */
++  if (actual_name)
++    {
++      ASSERT (actual_name_size > 0);
++      buf_set_write (&actual, actual_name, actual_name_size);
++    }
++
++  /* Check if GUID was explicitly specified as --dev-node parameter */
++  if (is_tap_win32 (name, tap_reg))
++    {
++      const char *act = guid_to_name (name, panel_reg);
++      buf_printf (&ret, "%s", name);
++      if (act)
++      buf_printf (&actual, "%s", act);
++      else
++      buf_printf (&actual, "%s", name);
++      return BSTR (&ret);
++    }
++
++  /* Lookup TAP adapter in network connections list */
++  {
++    const char *guid = name_to_guid (name, tap_reg, panel_reg);
++    if (guid)
++      {
++      buf_printf (&actual, "%s", name);
++      buf_printf (&ret, "%s", guid);
++      return BSTR (&ret);
++      }
++  }
++
++  return NULL;
++}
++
++/*
++ * Get adapter info list
++ */
++const IP_ADAPTER_INFO *
++get_adapter_info_list (struct gc_arena *gc)
++{
++  ULONG size = 0;
++  IP_ADAPTER_INFO *pi = NULL;
++  DWORD status;
++
++  if ((status = GetAdaptersInfo (NULL, &size)) != ERROR_BUFFER_OVERFLOW)
++    {
++      msg (M_INFO, "GetAdaptersInfo #1 failed (status=%u) : %s",
++         (unsigned int)status,
++         strerror_win32 (status, gc));
++    }
++  else
++    {
++      pi = (PIP_ADAPTER_INFO) gc_malloc (size, false, gc);
++      if ((status = GetAdaptersInfo (pi, &size)) == NO_ERROR)
++      return pi;
++      else
++      {
++        msg (M_INFO, "GetAdaptersInfo #2 failed (status=%u) : %s",
++             (unsigned int)status,
++             strerror_win32 (status, gc));
++      }
++    }
++  return pi;
++}
++
++const IP_PER_ADAPTER_INFO *
++get_per_adapter_info (const DWORD index, struct gc_arena *gc)
++{
++  ULONG size = 0;
++  IP_PER_ADAPTER_INFO *pi = NULL;
++  DWORD status;
++
++  if (index != ~0)
++    {
++      if ((status = GetPerAdapterInfo (index, NULL, &size)) != ERROR_BUFFER_OVERFLOW)
++      {
++        msg (M_INFO, "GetPerAdapterInfo #1 failed (status=%u) : %s",
++             (unsigned int)status,
++             strerror_win32 (status, gc));
++      }
++      else
++      {
++        pi = (PIP_PER_ADAPTER_INFO) gc_malloc (size, false, gc);
++        if ((status = GetPerAdapterInfo ((ULONG)index, pi, &size)) == ERROR_SUCCESS)
++          return pi;
++        else
++          {
++            msg (M_INFO, "GetPerAdapterInfo #2 failed (status=%u) : %s",
++                 (unsigned int)status,
++                 strerror_win32 (status, gc));
++          }
++      }
++    }
++  return pi;
++}
++
++static const IP_INTERFACE_INFO *
++get_interface_info_list (struct gc_arena *gc)
++{
++  ULONG size = 0;
++  IP_INTERFACE_INFO *ii = NULL;
++  DWORD status;
++
++  if ((status = GetInterfaceInfo (NULL, &size)) != ERROR_INSUFFICIENT_BUFFER)
++    {
++      msg (M_INFO, "GetInterfaceInfo #1 failed (status=%u) : %s",
++         (unsigned int)status,
++         strerror_win32 (status, gc));
++    }
++  else
++    {
++      ii = (PIP_INTERFACE_INFO) gc_malloc (size, false, gc);
++      if ((status = GetInterfaceInfo (ii, &size)) == NO_ERROR)
++      return ii;
++      else
++      {
++        msg (M_INFO, "GetInterfaceInfo #2 failed (status=%u) : %s",
++             (unsigned int)status,
++             strerror_win32 (status, gc));
++      }
++    }
++  return ii;
++}
++
++static const IP_ADAPTER_INDEX_MAP *
++get_interface_info (DWORD index, struct gc_arena *gc)
++{
++  const IP_INTERFACE_INFO *list = get_interface_info_list (gc);
++  if (list)
++    {
++      int i;
++      for (i = 0; i < list->NumAdapters; ++i)
++      {
++        const IP_ADAPTER_INDEX_MAP *inter = &list->Adapter[i];
++        if (index == inter->Index)
++          return inter;
++      }
++    }
++  return NULL;
++}
++
++/*
++ * Given an adapter index, return a pointer to the
++ * IP_ADAPTER_INFO structure for that adapter.
++ */
++
++const IP_ADAPTER_INFO *
++get_adapter (const IP_ADAPTER_INFO *ai, DWORD index)
++{
++  if (ai && index != (DWORD)~0)
++    {
++      const IP_ADAPTER_INFO *a;
++
++      /* find index in the linked list */
++      for (a = ai; a != NULL; a = a->Next)
++      {
++        if (a->Index == index)
++          return a;
++      }
++    }
++  return NULL;
++}
++
++const IP_ADAPTER_INFO *
++get_adapter_info (DWORD index, struct gc_arena *gc)
++{
++  return get_adapter (get_adapter_info_list (gc), index);
++}
++
++static int
++get_adapter_n_ip_netmask (const IP_ADAPTER_INFO *ai)
++{
++  if (ai)
++    {
++      int n = 0;
++      const IP_ADDR_STRING *ip = &ai->IpAddressList;
++
++      while (ip)
++      {
++        ++n;
++        ip = ip->Next;
++      }
++      return n;
++    }
++  else
++    return 0;
++}
++
++static bool
++get_adapter_ip_netmask (const IP_ADAPTER_INFO *ai, const int n, in_addr_t *ip, in_addr_t *netmask)
++{
++  bool ret = false;
++  *ip = 0;
++  *netmask = 0;
++
++  if (ai)
++    {
++      const IP_ADDR_STRING *iplist = &ai->IpAddressList;
++      int i = 0;
++
++      while (iplist)
++      {
++        if (i == n)
++          break;
++        ++i;
++        iplist = iplist->Next;
++      }
++
++      if (iplist)
++      {
++        const unsigned int getaddr_flags = GETADDR_HOST_ORDER;
++        const char *ip_str = iplist->IpAddress.String;
++        const char *netmask_str = iplist->IpMask.String;
++        bool succeed1 = false;
++        bool succeed2 = false;
++
++        if (ip_str && netmask_str && strlen (ip_str) && strlen (netmask_str))
++          {
++            *ip = getaddr (getaddr_flags, ip_str, 0, &succeed1, NULL);
++            *netmask = getaddr (getaddr_flags, netmask_str, 0, &succeed2, NULL);
++            ret = (succeed1 == true && succeed2 == true);
++          }
++      }
++    }
++
++  return ret;
++}
++
++static bool
++test_adapter_ip_netmask (const IP_ADAPTER_INFO *ai, const in_addr_t ip, const in_addr_t netmask)
++{
++  if (ai)
++    {
++      in_addr_t ip_adapter = 0;
++      in_addr_t netmask_adapter = 0;
++      const bool status = get_adapter_ip_netmask (ai, 0, &ip_adapter, &netmask_adapter);
++      return (status && ip_adapter == ip && netmask_adapter == netmask);
++    }
++  else
++    return false;
++}
++
++const IP_ADAPTER_INFO *
++get_tun_adapter (const struct tuntap *tt, const IP_ADAPTER_INFO *list)
++{
++  if (list && tt)
++    return get_adapter (list, tt->adapter_index);
++  else
++    return NULL;
++}
++
++bool
++is_adapter_up (const struct tuntap *tt, const IP_ADAPTER_INFO *list)
++{
++  int i;
++  bool ret = false;
++
++  const IP_ADAPTER_INFO *ai = get_tun_adapter (tt, list);
++
++  if (ai)
++    {
++      const int n = get_adapter_n_ip_netmask (ai);
++
++      /* loop once for every IP/netmask assigned to adapter */
++      for (i = 0; i < n; ++i)
++      {
++        in_addr_t ip, netmask;
++        if (get_adapter_ip_netmask (ai, i, &ip, &netmask))
++          {
++            if (tt->local && tt->adapter_netmask)
++              {
++                /* wait for our --ifconfig parms to match the actual adapter parms */
++                if (tt->local == ip && tt->adapter_netmask == netmask)
++                  ret = true;
++              }
++            else
++              {
++                /* --ifconfig was not defined, maybe using a real DHCP server */
++                if (ip && netmask)
++                  ret = true;
++              }
++          }
++      }
++    }
++  else
++    ret = true; /* this can occur when TAP adapter is bridged */
++
++  return ret;
++}
++
++bool
++is_ip_in_adapter_subnet (const IP_ADAPTER_INFO *ai, const in_addr_t ip, in_addr_t *highest_netmask)
++{
++  int i;
++  bool ret = false;
++
++  if (highest_netmask)
++    *highest_netmask = 0;
++
++  if (ai)
++    {
++      const int n = get_adapter_n_ip_netmask (ai);
++      for (i = 0; i < n; ++i)
++      {
++        in_addr_t adapter_ip, adapter_netmask;
++        if (get_adapter_ip_netmask (ai, i, &adapter_ip, &adapter_netmask))
++          {
++            if (adapter_ip && adapter_netmask && (ip & adapter_netmask) == (adapter_ip & adapter_netmask))
++              {
++                if (highest_netmask && adapter_netmask > *highest_netmask)
++                  *highest_netmask = adapter_netmask;
++                ret = true;
++              }
++          }
++      }
++    }
++  return ret;
++}
++
++DWORD
++adapter_index_of_ip (const IP_ADAPTER_INFO *list,
++                   const in_addr_t ip,
++                   int *count,
++                   in_addr_t *netmask)
++{
++  struct gc_arena gc = gc_new ();
++  DWORD ret = ~0;
++  in_addr_t highest_netmask = 0;
++  bool first = true;
++
++  if (count)
++    *count = 0;
++
++  while (list)
++    {
++      in_addr_t hn;
++
++      if (is_ip_in_adapter_subnet (list, ip, &hn))
++      {
++        if (first || hn > highest_netmask)
++          {
++            highest_netmask = hn;
++            if (count)
++              *count = 1;
++            ret = list->Index;
++            first = false;
++          }
++        else if (hn == highest_netmask)
++          {
++            if (count)
++              ++*count;
++          }
++      }
++      list = list->Next;
++    }
++
++  dmsg (D_ROUTE_DEBUG, "DEBUG: IP Locate: ip=%s nm=%s index=%d count=%d",
++       print_in_addr_t (ip, 0, &gc),
++       print_in_addr_t (highest_netmask, 0, &gc),
++       (int)ret,
++       count ? *count : -1);
++
++  if (ret == ~0 && count)
++    *count = 0;
++
++  if (netmask)
++    *netmask = highest_netmask;
++
++  gc_free (&gc);
++  return ret;
++}
++
++/*
++ * Given an adapter index, return true if the adapter
++ * is DHCP disabled.
++ */
++
++#define DHCP_STATUS_UNDEF     0
++#define DHCP_STATUS_ENABLED   1
++#define DHCP_STATUS_DISABLED  2
++
++static int
++dhcp_status (DWORD index)
++{
++  struct gc_arena gc = gc_new ();
++  int ret = DHCP_STATUS_UNDEF;
++  if (index != ~0)
++    {
++      const IP_ADAPTER_INFO *ai = get_adapter_info (index, &gc);
++
++      if (ai)
++      {
++        if (ai->DhcpEnabled)
++          ret = DHCP_STATUS_ENABLED;
++        else
++          ret = DHCP_STATUS_DISABLED;
++      }
++    }
++  gc_free (&gc);
++  return ret;
++}
++
++/*
++ * Delete all temporary address/netmask pairs which were added
++ * to adapter (given by index) by previous calls to AddIPAddress.
++ */
++static void
++delete_temp_addresses (DWORD index)
++{
++  struct gc_arena gc = gc_new ();
++  const IP_ADAPTER_INFO *a = get_adapter_info (index, &gc);
++
++  if (a)
++    {
++      const IP_ADDR_STRING *ip = &a->IpAddressList;
++      while (ip)
++      {
++        DWORD status;
++        const DWORD context = ip->Context;
++
++        if ((status = DeleteIPAddress ((ULONG) context)) == NO_ERROR)
++          {
++            msg (M_INFO, "Successfully deleted previously set dynamic IP/netmask: %s/%s",
++                 ip->IpAddress.String,
++                 ip->IpMask.String);
++          }
++        else
++          {
++            const char *empty = "0.0.0.0";
++            if (strcmp (ip->IpAddress.String, empty)
++                || strcmp (ip->IpMask.String, empty))
++              msg (M_INFO, "NOTE: could not delete previously set dynamic IP/netmask: %s/%s (status=%u)",
++                   ip->IpAddress.String,
++                   ip->IpMask.String,
++                   (unsigned int)status);
++          }
++        ip = ip->Next;
++      }
++    }
++  gc_free (&gc);
++}
++
++/*
++ * Get interface index for use with IP Helper API functions.
++ */
++static DWORD
++get_adapter_index_method_1 (const char *guid)
++{
++  struct gc_arena gc = gc_new ();
++  ULONG index = ~0;
++  DWORD status;
++  wchar_t wbuf[256];
++  snwprintf (wbuf, SIZE (wbuf), L"\\DEVICE\\TCPIP_%S", guid);
++  wbuf [SIZE(wbuf) - 1] = 0;
++  if ((status = GetAdapterIndex (wbuf, &index)) != NO_ERROR)
++    index = ~0;
++  gc_free (&gc);
++  return index;
++}
++
++static DWORD
++get_adapter_index_method_2 (const char *guid)
++{
++  struct gc_arena gc = gc_new ();
++  DWORD index = ~0;
++
++  const IP_ADAPTER_INFO *list = get_adapter_info_list (&gc);
++
++  while (list)
++    {
++      if (!strcmp (guid, list->AdapterName))
++      {
++        index = list->Index;
++        break;
++      }
++      list = list->Next;
++    }
++
++  gc_free (&gc);
++  return index;
++}
++
++static DWORD
++get_adapter_index (const char *guid)
++{
++  DWORD index;
++  index = get_adapter_index_method_1 (guid);
++  if (index == ~0)
++    index = get_adapter_index_method_2 (guid);
++  if (index == ~0)
++    msg (M_INFO, "NOTE: could not get adapter index for %s", guid);
++  return index;
++}
++
++static DWORD
++get_adapter_index_flexible (const char *name) /* actual name or GUID */
++{
++  struct gc_arena gc = gc_new ();
++  DWORD index;
++  index = get_adapter_index_method_1 (name);
++  if (index == ~0)
++    index = get_adapter_index_method_2 (name);
++  if (index == ~0)
++    {
++      const struct tap_reg *tap_reg = get_tap_reg (&gc);
++      const struct panel_reg *panel_reg = get_panel_reg (&gc);
++      const char *guid = name_to_guid (name, tap_reg, panel_reg);
++      index = get_adapter_index_method_1 (guid);
++      if (index == ~0)
++      index = get_adapter_index_method_2 (guid);
++    }
++  if (index == ~0)
++    msg (M_INFO, "NOTE: could not get adapter index for name/GUID '%s'", name);
++  gc_free (&gc);
++  return index;
++}
++
++/*
++ * Return a string representing a PIP_ADDR_STRING
++ */
++static const char *
++format_ip_addr_string (const IP_ADDR_STRING *ip, struct gc_arena *gc)
++{
++  struct buffer out = alloc_buf_gc (256, gc);
++  while (ip)
++    {
++      buf_printf (&out, "%s", ip->IpAddress.String);
++      if (strlen (ip->IpMask.String))
++      {
++        buf_printf (&out, "/");
++        buf_printf (&out, "%s", ip->IpMask.String);
++      }
++      buf_printf (&out, " ");
++      ip = ip->Next;
++    }
++  return BSTR (&out);
++}
++
++/*
++ * Show info for a single adapter
++ */
++static void
++show_adapter (int msglev, const IP_ADAPTER_INFO *a, struct gc_arena *gc)
++{
++  msg (msglev, "%s", a->Description);
++  msg (msglev, "  Index = %d", (int)a->Index);
++  msg (msglev, "  GUID = %s", a->AdapterName);
++  msg (msglev, "  IP = %s", format_ip_addr_string (&a->IpAddressList, gc));
++  msg (msglev, "  MAC = %s", format_hex_ex (a->Address, a->AddressLength, 0, 1, ":", gc));
++  msg (msglev, "  GATEWAY = %s", format_ip_addr_string (&a->GatewayList, gc));
++  if (a->DhcpEnabled)
++    {
++      msg (msglev, "  DHCP SERV = %s", format_ip_addr_string (&a->DhcpServer, gc));
++      msg (msglev, "  DHCP LEASE OBTAINED = %s", time_string (a->LeaseObtained, 0, false, gc));
++      msg (msglev, "  DHCP LEASE EXPIRES  = %s", time_string (a->LeaseExpires, 0, false, gc));
++    }
++  if (a->HaveWins)
++    {
++      msg (msglev, "  PRI WINS = %s", format_ip_addr_string (&a->PrimaryWinsServer, gc));
++      msg (msglev, "  SEC WINS = %s", format_ip_addr_string (&a->SecondaryWinsServer, gc));
++    }
++
++  {
++    const IP_PER_ADAPTER_INFO *pai = get_per_adapter_info (a->Index, gc);
++    if (pai)
++      {
++      msg (msglev, "  DNS SERV = %s", format_ip_addr_string (&pai->DnsServerList, gc));
++      }
++  }
++}
++
++/*
++ * Show current adapter list
++ */
++void
++show_adapters (int msglev)
++{
++  struct gc_arena gc = gc_new ();
++  const IP_ADAPTER_INFO *ai = get_adapter_info_list (&gc);
++
++  msg (msglev, "SYSTEM ADAPTER LIST");
++  if (ai)
++    {
++      const IP_ADAPTER_INFO *a;
++
++      /* find index in the linked list */
++      for (a = ai; a != NULL; a = a->Next)
++      {
++        show_adapter (msglev, a, &gc);
++      }
++    }
++  gc_free (&gc);
++}
++
++/*
++ * Set a particular TAP-Win32 adapter (or all of them if
++ * adapter_name == NULL) to allow it to be opened from
++ * a non-admin account.  This setting will only persist
++ * for the lifetime of the device object.
++ */
++
++static void
++tap_allow_nonadmin_access_handle (const char *device_path, HANDLE hand)
++{
++  struct security_attributes sa;
++  BOOL status;
++
++  if (!init_security_attributes_allow_all (&sa))
++    msg (M_ERR, "Error: init SA failed");
++
++  status = SetKernelObjectSecurity (hand, DACL_SECURITY_INFORMATION, &sa.sd);
++  if (!status)
++    {
++      msg (M_ERRNO, "Error: SetKernelObjectSecurity failed on %s", device_path);
++    }
++  else
++    {
++      msg (M_INFO|M_NOPREFIX, "TAP-Win32 device: %s [Non-admin access allowed]", device_path);
++    }
++}
++
++void
++tap_allow_nonadmin_access (const char *dev_node)
++{
++  struct gc_arena gc = gc_new ();
++  const struct tap_reg *tap_reg = get_tap_reg (&gc);
++  const struct panel_reg *panel_reg = get_panel_reg (&gc);
++  const char *device_guid = NULL;
++  HANDLE hand;
++  char actual_buffer[256];
++  char device_path[256];
++
++  at_least_one_tap_win32 (tap_reg);
++
++  if (dev_node)
++    {
++      /* Get the device GUID for the device specified with --dev-node. */
++      device_guid = get_device_guid (dev_node, actual_buffer, sizeof (actual_buffer), tap_reg, panel_reg, &gc);
++
++      if (!device_guid)
++      msg (M_FATAL, "TAP-Win32 adapter '%s' not found", dev_node);
++
++      /* Open Windows TAP-Win32 adapter */
++      openvpn_snprintf (device_path, sizeof(device_path), "%s%s%s",
++                      USERMODEDEVICEDIR,
++                      device_guid,
++                      TAPSUFFIX);
++      
++      hand = CreateFile (
++                       device_path,
++                       MAXIMUM_ALLOWED,
++                       0, /* was: FILE_SHARE_READ */
++                       0,
++                       OPEN_EXISTING,
++                       FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
++                       0
++                       );
++
++      if (hand == INVALID_HANDLE_VALUE)
++      msg (M_ERR, "CreateFile failed on TAP device: %s", device_path);
++
++      tap_allow_nonadmin_access_handle (device_path, hand);
++      CloseHandle (hand);
++    }
++  else 
++    {
++      int device_number = 0;
++
++      /* Try opening all TAP devices */
++      while (true)
++      {
++        device_guid = get_unspecified_device_guid (device_number, 
++                                                   actual_buffer, 
++                                                   sizeof (actual_buffer),
++                                                   tap_reg,
++                                                   panel_reg,
++                                                   &gc);
++
++        if (!device_guid)
++          break;
++
++        /* Open Windows TAP-Win32 adapter */
++        openvpn_snprintf (device_path, sizeof(device_path), "%s%s%s",
++                          USERMODEDEVICEDIR,
++                          device_guid,
++                          TAPSUFFIX);
++
++        hand = CreateFile (
++                           device_path,
++                           MAXIMUM_ALLOWED,
++                           0, /* was: FILE_SHARE_READ */
++                           0,
++                           OPEN_EXISTING,
++                           FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
++                           0
++                           );
++
++        if (hand == INVALID_HANDLE_VALUE)
++          msg (M_WARN, "CreateFile failed on TAP device: %s", device_path);
++        else
++          {
++            tap_allow_nonadmin_access_handle (device_path, hand);
++            CloseHandle (hand);
++          }
++  
++        device_number++;
++      }
++    }
++  gc_free (&gc);
++}
++
++/*
++ * DHCP release/renewal
++ */
++bool
++dhcp_release_by_adapter_index(const DWORD adapter_index)
++{
++  struct gc_arena gc = gc_new ();
++  bool ret = false;
++  const IP_ADAPTER_INDEX_MAP *inter = get_interface_info (adapter_index, &gc);
++
++  if (inter)
++    {
++      DWORD status = IpReleaseAddress ((IP_ADAPTER_INDEX_MAP *)inter);
++      if (status == NO_ERROR)
++      {
++        msg (D_TUNTAP_INFO, "TAP: DHCP address released");
++        ret = true;
++      }
++      else
++      msg (M_WARN, "NOTE: Release of DHCP-assigned IP address lease on TAP-Win32 adapter failed: %s (code=%u)",
++           strerror_win32 (status, &gc),
++           (unsigned int)status);
++    }
++
++  gc_free (&gc);
++  return ret;
++}
++
++static bool
++dhcp_release (const struct tuntap *tt)
++{
++  if (tt && tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ && tt->adapter_index != ~0)
++    return dhcp_release_by_adapter_index (tt->adapter_index);
++  else
++    return false;
++}
++
++bool
++dhcp_renew_by_adapter_index (const DWORD adapter_index)
++{
++  struct gc_arena gc = gc_new ();
++  bool ret = false;
++  const IP_ADAPTER_INDEX_MAP *inter = get_interface_info (adapter_index, &gc);
++
++  if (inter)
++    {
++      DWORD status = IpRenewAddress ((IP_ADAPTER_INDEX_MAP *)inter);
++      if (status == NO_ERROR)
++      {
++        msg (D_TUNTAP_INFO, "TAP: DHCP address renewal succeeded");
++        ret = true;
++      }
++      else
++      msg (M_WARN, "WARNING: Failed to renew DHCP IP address lease on TAP-Win32 adapter: %s (code=%u)",
++           strerror_win32 (status, &gc),
++           (unsigned int)status);
++    }
++  gc_free (&gc);
++  return ret;
++}
++
++static bool
++dhcp_renew (const struct tuntap *tt)
++{
++  if (tt && tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ && tt->adapter_index != ~0)
++    return dhcp_renew_by_adapter_index (tt->adapter_index);
++  else
++    return false;
++}
++
++/*
++ * netsh functions
++ */
++
++static void
++netsh_command (const struct argv *a, int n)
++{
++  int i;
++  for (i = 0; i < n; ++i)
++    {
++      bool status;
++      openvpn_sleep (1);
++      netcmd_semaphore_lock ();
++      argv_msg_prefix (M_INFO, a, "NETSH");
++      status = openvpn_execve_check (a, NULL, 0, "ERROR: netsh command failed");
++      netcmd_semaphore_release ();
++      if (status)
++      return;
++      openvpn_sleep (4);
++    }
++  msg (M_FATAL, "NETSH: command failed");
++}
++
++void
++ipconfig_register_dns (const struct env_set *es)
++{
++  struct argv argv;
++  bool status;
++  const char err[] = "ERROR: Windows ipconfig command failed";
++
++  msg (D_TUNTAP_INFO, "Start net commands...");
++  netcmd_semaphore_lock ();
++
++  argv_init (&argv);
++
++  argv_printf (&argv, "%s%sc stop dnscache",
++             get_win_sys_path(),
++             WIN_NET_PATH_SUFFIX);
++  argv_msg (D_TUNTAP_INFO, &argv);
++  status = openvpn_execve_check (&argv, es, 0, err);
++  argv_reset(&argv);
++
++  argv_printf (&argv, "%s%sc start dnscache",
++             get_win_sys_path(),
++             WIN_NET_PATH_SUFFIX);
++  argv_msg (D_TUNTAP_INFO, &argv);
++  status = openvpn_execve_check (&argv, es, 0, err);
++  argv_reset(&argv);
++
++  argv_printf (&argv, "%s%sc /flushdns",
++             get_win_sys_path(),
++             WIN_IPCONFIG_PATH_SUFFIX);
++  argv_msg (D_TUNTAP_INFO, &argv);
++  status = openvpn_execve_check (&argv, es, 0, err);
++  argv_reset(&argv);
++
++  argv_printf (&argv, "%s%sc /registerdns",
++             get_win_sys_path(),
++             WIN_IPCONFIG_PATH_SUFFIX);
++  argv_msg (D_TUNTAP_INFO, &argv);
++  status = openvpn_execve_check (&argv, es, 0, err);
++  argv_reset(&argv);
++
++  netcmd_semaphore_release ();
++  msg (D_TUNTAP_INFO, "End net commands...");
++}
++
++void
++ip_addr_string_to_array (in_addr_t *dest, int *dest_len, const IP_ADDR_STRING *src)
++{
++  int i = 0;
++  while (src)
++    {
++      const unsigned int getaddr_flags = GETADDR_HOST_ORDER;
++      const char *ip_str = src->IpAddress.String;
++      in_addr_t ip = 0;
++      bool succeed = false;
++
++      if (i >= *dest_len)
++      break;
++      if (!ip_str || !strlen (ip_str))
++      break;
++
++      ip = getaddr (getaddr_flags, ip_str, 0, &succeed, NULL);
++      if (!succeed)
++      break;
++      dest[i++] = ip;
++
++      src = src->Next;
++    }
++  *dest_len = i;
++
++#if 0
++ {
++   struct gc_arena gc = gc_new ();
++   msg (M_INFO, "ip_addr_string_to_array [%d]", *dest_len);
++   for (i = 0; i < *dest_len; ++i)
++     {
++       msg (M_INFO, "%s", print_in_addr_t (dest[i], 0, &gc));
++     }
++   gc_free (&gc);
++ }
++#endif
++}
++
++static bool
++ip_addr_one_to_one (const in_addr_t *a1, const int a1len, const IP_ADDR_STRING *ias)
++{
++  in_addr_t a2[8];
++  int a2len = SIZE(a2);
++  int i;
++
++  ip_addr_string_to_array (a2, &a2len, ias);
++  /*msg (M_INFO, "a1len=%d a2len=%d", a1len, a2len);*/
++  if (a1len != a2len)
++    return false;
++
++  for (i = 0; i < a1len; ++i)
++    {
++      if (a1[i] != a2[i])
++      return false;
++    }
++  return true;
++}
++
++static bool
++ip_addr_member_of (const in_addr_t addr, const IP_ADDR_STRING *ias)
++{
++  in_addr_t aa[8];
++  int len = SIZE(aa);
++  int i;
++
++  ip_addr_string_to_array (aa, &len, ias);
++  for (i = 0; i < len; ++i)
++    {
++      if (addr == aa[i])
++      return true;
++    }
++  return false;
++}
++
++static void
++netsh_ifconfig_options (const char *type,
++                      const in_addr_t *addr_list,
++                      const int addr_len,
++                      const IP_ADDR_STRING *current,
++                      const char *flex_name,
++                      const bool test_first)
++{
++  struct gc_arena gc = gc_new ();
++  struct argv argv = argv_new ();
++  bool delete_first = false;
++
++  /* first check if we should delete existing DNS/WINS settings from TAP interface */
++  if (test_first)
++    {
++      if (!ip_addr_one_to_one (addr_list, addr_len, current))
++      delete_first = true;
++    }
++  else
++    delete_first = true;
++  
++  /* delete existing DNS/WINS settings from TAP interface */
++  if (delete_first)
++    {
++      argv_printf (&argv, "%s%sc interface ip delete %s %s all",
++                 get_win_sys_path(),
++                 NETSH_PATH_SUFFIX,
++                 type,
++                 flex_name);
++      netsh_command (&argv, 2);
++    }
++
++  /* add new DNS/WINS settings to TAP interface */
++  {
++    int count = 0;
++    int i;
++    for (i = 0; i < addr_len; ++i)
++      {
++      if (delete_first || !test_first || !ip_addr_member_of (addr_list[i], current))
++        {
++          const char *fmt = count ?
++              "%s%sc interface ip add %s %s %s"
++            : "%s%sc interface ip set %s %s static %s";
++
++          argv_printf (&argv, fmt,
++                       get_win_sys_path(),
++                       NETSH_PATH_SUFFIX,
++                       type,
++                       flex_name,
++                       print_in_addr_t (addr_list[i], 0, &gc));
++          netsh_command (&argv, 2);
++        
++          ++count;
++        }
++      else
++        {
++          msg (M_INFO, "NETSH: \"%s\" %s %s [already set]",
++               flex_name,
++               type,
++               print_in_addr_t (addr_list[i], 0, &gc));
++        }
++      }
++  }
++
++  argv_reset (&argv);
++  gc_free (&gc);
++}
++
++static void
++init_ip_addr_string2 (IP_ADDR_STRING *dest, const IP_ADDR_STRING *src1, const IP_ADDR_STRING *src2)
++{
++  CLEAR (dest[0]);
++  CLEAR (dest[1]);
++  if (src1)
++    {
++      dest[0] = *src1;
++      dest[0].Next = NULL;
++    }
++  if (src2)
++    {
++      dest[1] = *src2;
++      dest[0].Next = &dest[1];
++      dest[1].Next = NULL;
++    }
++}
++
++static void
++netsh_ifconfig (const struct tuntap_options *to,
++              const char *flex_name,
++              const in_addr_t ip,
++              const in_addr_t netmask,
++              const unsigned int flags)
++{
++  struct gc_arena gc = gc_new ();
++  struct argv argv = argv_new ();
++  const IP_ADAPTER_INFO *ai = NULL;
++  const IP_PER_ADAPTER_INFO *pai = NULL;
++
++  if (flags & NI_TEST_FIRST)
++    {
++      const IP_ADAPTER_INFO *list = get_adapter_info_list (&gc);
++      const int index = get_adapter_index_flexible (flex_name);
++      ai = get_adapter (list, index);
++      pai = get_per_adapter_info (index, &gc);
++    }
++
++  if (flags & NI_IP_NETMASK)
++    {
++      if (test_adapter_ip_netmask (ai, ip, netmask))
++      {
++        msg (M_INFO, "NETSH: \"%s\" %s/%s [already set]",
++             flex_name,
++             print_in_addr_t (ip, 0, &gc),
++             print_in_addr_t (netmask, 0, &gc));
++      }
++      else
++      {
++        /* example: netsh interface ip set address my-tap static 10.3.0.1 255.255.255.0 */
++        argv_printf (&argv, "%s%sc interface ip set address %s static %s %s",
++                     get_win_sys_path(),
++                     NETSH_PATH_SUFFIX,
++                     flex_name,
++                     print_in_addr_t (ip, 0, &gc),
++                     print_in_addr_t (netmask, 0, &gc));
++
++        netsh_command (&argv, 4);
++      }
++    }
++
++  /* set WINS/DNS options */
++  if (flags & NI_OPTIONS)
++    {
++      IP_ADDR_STRING wins[2];
++      CLEAR (wins[0]);
++      CLEAR (wins[1]);
++
++      netsh_ifconfig_options ("dns",
++                            to->dns,
++                            to->dns_len,
++                            pai ? &pai->DnsServerList : NULL,
++                            flex_name,
++                            BOOL_CAST (flags & NI_TEST_FIRST));
++      if (ai && ai->HaveWins)
++      init_ip_addr_string2 (wins, &ai->PrimaryWinsServer, &ai->SecondaryWinsServer);
++
++      netsh_ifconfig_options ("wins",
++                            to->wins,
++                            to->wins_len,
++                            ai ? wins : NULL,
++                            flex_name,
++                            BOOL_CAST (flags & NI_TEST_FIRST));
++    }
++  
++  argv_reset (&argv);
++  gc_free (&gc);
++}
++
++static void
++netsh_enable_dhcp (const struct tuntap_options *to,
++                 const char *actual_name)
++{
++  struct argv argv;
++  argv_init (&argv);
++
++  /* example: netsh interface ip set address my-tap dhcp */
++  argv_printf (&argv,
++            "%s%sc interface ip set address %s dhcp",
++             get_win_sys_path(),
++             NETSH_PATH_SUFFIX,
++             actual_name);
++
++  netsh_command (&argv, 4);
++
++  argv_reset (&argv);
++}
++
++/*
++ * Return a TAP name for netsh commands.
++ */
++static const char *
++netsh_get_id (const char *dev_node, struct gc_arena *gc)
++{
++  const struct tap_reg *tap_reg = get_tap_reg (gc);
++  const struct panel_reg *panel_reg = get_panel_reg (gc);
++  struct buffer actual = alloc_buf_gc (256, gc);
++  const char *guid;
++
++  at_least_one_tap_win32 (tap_reg);
++
++  if (dev_node)
++    {
++      guid = get_device_guid (dev_node, BPTR (&actual), BCAP (&actual), tap_reg, panel_reg, gc);
++    }
++  else
++    {
++      guid = get_unspecified_device_guid (0, BPTR (&actual), BCAP (&actual), tap_reg, panel_reg, gc);
++
++      if (get_unspecified_device_guid (1, NULL, 0, tap_reg, panel_reg, gc)) /* ambiguous if more than one TAP-Win32 adapter */
++      guid = NULL;
++    }
++
++  if (!guid)
++    return "NULL";         /* not found */
++  else if (strcmp (BPTR (&actual), "NULL"))
++    return BPTR (&actual); /* control panel name */
++  else
++    return guid;           /* no control panel name, return GUID instead */
++}
++
++/*
++ * Called iteratively on TAP-Win32 wait-for-initialization polling loop
++ */
++void
++tun_standby_init (struct tuntap *tt)
++{
++  tt->standby_iter = 0;
++}
++
++bool
++tun_standby (struct tuntap *tt)
++{
++  bool ret = true;
++  ++tt->standby_iter;
++  if (tt->options.ip_win32_type == IPW32_SET_ADAPTIVE)
++    {
++      if (tt->standby_iter == IPW32_SET_ADAPTIVE_TRY_NETSH)
++      {
++        msg (M_INFO, "NOTE: now trying netsh (this may take some time)");
++        netsh_ifconfig (&tt->options,
++                        tt->actual_name,
++                        tt->local,
++                        tt->adapter_netmask,
++                        NI_TEST_FIRST|NI_IP_NETMASK|NI_OPTIONS);
++      }
++      else if (tt->standby_iter >= IPW32_SET_ADAPTIVE_TRY_NETSH*2)
++      {
++        ret = false;
++      }
++    }
++  return ret;
++}
++
++/*
++ * Convert DHCP options from the command line / config file
++ * into a raw DHCP-format options string.
++ */
++
++static void
++write_dhcp_u8 (struct buffer *buf, const int type, const int data, bool *error)
++{
++  if (!buf_safe (buf, 3))
++    {
++      *error = true;
++      msg (M_WARN, "write_dhcp_u8: buffer overflow building DHCP options");
++      return;
++    }
++  buf_write_u8 (buf, type);
++  buf_write_u8 (buf, 1);
++  buf_write_u8 (buf, data);
++}
++
++static void
++write_dhcp_u32_array (struct buffer *buf, const int type, const uint32_t *data, const unsigned int len, bool *error)
++{
++  if (len > 0)
++    {
++      int i;
++      const int size = len * sizeof (uint32_t);
++
++      if (!buf_safe (buf, 2 + size))
++      {
++        *error = true;
++        msg (M_WARN, "write_dhcp_u32_array: buffer overflow building DHCP options");
++        return;
++      }
++      if (size < 1 || size > 255)
++      {
++        *error = true;
++        msg (M_WARN, "write_dhcp_u32_array: size (%d) must be > 0 and <= 255", size);
++        return;
++      }
++      buf_write_u8 (buf, type);
++      buf_write_u8 (buf, size);
++      for (i = 0; i < len; ++i)
++      buf_write_u32 (buf, data[i]);
++    }
++}
++
++static void
++write_dhcp_str (struct buffer *buf, const int type, const char *str, bool *error)
++{
++  const int len = strlen (str);
++  if (!buf_safe (buf, 2 + len))
++    {
++      *error = true;
++      msg (M_WARN, "write_dhcp_str: buffer overflow building DHCP options");
++      return;
++    }
++  if (len < 1 || len > 255)
++    {
++      *error = true;
++      msg (M_WARN, "write_dhcp_str: string '%s' must be > 0 bytes and <= 255 bytes", str);
++      return;
++    }
++  buf_write_u8 (buf, type);
++  buf_write_u8 (buf, len);
++  buf_write (buf, str, len);
++}
++
++static bool
++build_dhcp_options_string (struct buffer *buf, const struct tuntap_options *o)
++{
++  bool error = false;
++  if (o->domain)
++    write_dhcp_str (buf, 15, o->domain, &error);
++
++  if (o->netbios_scope)
++    write_dhcp_str (buf, 47, o->netbios_scope, &error);
++
++  if (o->netbios_node_type)
++    write_dhcp_u8 (buf, 46, o->netbios_node_type, &error);
++
++  write_dhcp_u32_array (buf, 6, (uint32_t*)o->dns, o->dns_len, &error);
++  write_dhcp_u32_array (buf, 44, (uint32_t*)o->wins, o->wins_len, &error);
++  write_dhcp_u32_array (buf, 42, (uint32_t*)o->ntp, o->ntp_len, &error);
++  write_dhcp_u32_array (buf, 45, (uint32_t*)o->nbdd, o->nbdd_len, &error);
++
++  /* the MS DHCP server option 'Disable Netbios-over-TCP/IP
++     is implemented as vendor option 001, value 002.
++     A value of 001 means 'leave NBT alone' which is the default */
++  if (o->disable_nbt)
++  {
++    if (!buf_safe (buf, 8))
++      {
++      msg (M_WARN, "build_dhcp_options_string: buffer overflow building DHCP options");
++      return false;
++      }
++    buf_write_u8 (buf,  43);
++    buf_write_u8 (buf,  6);  /* total length field */
++    buf_write_u8 (buf,  0x001);
++    buf_write_u8 (buf,  4);  /* length of the vendor specified field */
++    buf_write_u32 (buf, 0x002);
++  }
++  return !error;
++}
++
++static void
++fork_dhcp_action (struct tuntap *tt)
++{
++  if (tt->options.dhcp_pre_release || tt->options.dhcp_renew)
++    {
++      struct gc_arena gc = gc_new ();
++      struct buffer cmd = alloc_buf_gc (256, &gc);
++      const int verb = 3;
++      const int pre_sleep = 1;
++  
++      buf_printf (&cmd, "openvpn --verb %d --tap-sleep %d", verb, pre_sleep);
++      if (tt->options.dhcp_pre_release)
++      buf_printf (&cmd, " --dhcp-pre-release");
++      if (tt->options.dhcp_renew)
++      buf_printf (&cmd, " --dhcp-renew");
++      buf_printf (&cmd, " --dhcp-internal %u", (unsigned int)tt->adapter_index);
++
++      fork_to_self (BSTR (&cmd));
++      gc_free (&gc);
++    }
++}
++
++void
++fork_register_dns_action (struct tuntap *tt)
++{
++  if (tt && tt->options.register_dns)
++    {
++      struct gc_arena gc = gc_new ();
++      struct buffer cmd = alloc_buf_gc (256, &gc);
++      const int verb = 3;
++ 
++      buf_printf (&cmd, "openvpn --verb %d --register-dns --rdns-internal", verb);
++      fork_to_self (BSTR (&cmd));
++      gc_free (&gc);
++    }
++}
++
++void
++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];
++  const char *device_guid = NULL;
++  DWORD len;
++  bool dhcp_masq = false;
++  bool dhcp_masq_post = false;
++
++  /*netcmd_semaphore_lock ();*/
++
++  msg( M_INFO, "open_tun, tt->ipv6=%d", tt->ipv6 );
++
++  if (tt->type == DEV_TYPE_NULL)
++    {
++      open_null (tt);
++      gc_free (&gc);
++      return;
++    }
++  else if (tt->type == DEV_TYPE_TAP || tt->type == DEV_TYPE_TUN)
++    {
++      ;
++    }
++  else
++    {
++      msg (M_FATAL|M_NOPREFIX, "Unknown virtual device type: '%s'", dev);
++    }
++
++  /*
++   * Lookup the device name in the registry, using the --dev-node high level name.
++   */
++  {
++    const struct tap_reg *tap_reg = get_tap_reg (&gc);
++    const struct panel_reg *panel_reg = get_panel_reg (&gc);
++    char actual_buffer[256];
++
++    at_least_one_tap_win32 (tap_reg);
++
++    if (dev_node)
++      {
++        /* Get the device GUID for the device specified with --dev-node. */
++        device_guid = get_device_guid (dev_node, actual_buffer, sizeof (actual_buffer), tap_reg, panel_reg, &gc);
++
++      if (!device_guid)
++        msg (M_FATAL, "TAP-Win32 adapter '%s' not found", dev_node);
++
++        /* Open Windows TAP-Win32 adapter */
++        openvpn_snprintf (device_path, sizeof(device_path), "%s%s%s",
++                        USERMODEDEVICEDIR,
++                        device_guid,
++                        TAPSUFFIX);
++
++        tt->hand = CreateFile (
++                             device_path,
++                             GENERIC_READ | GENERIC_WRITE,
++                             0, /* was: FILE_SHARE_READ */
++                             0,
++                             OPEN_EXISTING,
++                             FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
++                             0
++                             );
++
++        if (tt->hand == INVALID_HANDLE_VALUE)
++          msg (M_ERR, "CreateFile failed on TAP device: %s", device_path);
++      }
++    else 
++      {
++        int device_number = 0;
++
++        /* Try opening all TAP devices until we find one available */
++        while (true)
++          {
++            device_guid = get_unspecified_device_guid (device_number, 
++                                                     actual_buffer, 
++                                                     sizeof (actual_buffer),
++                                                     tap_reg,
++                                                     panel_reg,
++                                                     &gc);
++
++          if (!device_guid)
++            msg (M_FATAL, "All TAP-Win32 adapters on this system are currently in use.");
++
++            /* Open Windows TAP-Win32 adapter */
++            openvpn_snprintf (device_path, sizeof(device_path), "%s%s%s",
++                                    USERMODEDEVICEDIR,
++                            device_guid,
++                            TAPSUFFIX);
++
++            tt->hand = CreateFile (
++                                 device_path,
++                                 GENERIC_READ | GENERIC_WRITE,
++                                 0, /* was: FILE_SHARE_READ */
++                                 0,
++                                 OPEN_EXISTING,
++                                 FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
++                                 0
++                                 );
++
++            if (tt->hand == INVALID_HANDLE_VALUE)
++              msg (D_TUNTAP_INFO, "CreateFile failed on TAP device: %s", device_path);
++            else
++              break;
++        
++            device_number++;
++          }
++      }
++
++    /* translate high-level device name into a device instance
++       GUID using the registry */
++    tt->actual_name = string_alloc (actual_buffer, NULL);
++  }
++
++  msg (M_INFO, "TAP-WIN32 device [%s] opened: %s", tt->actual_name, device_path);
++  tt->adapter_index = get_adapter_index (device_guid);
++
++  /* get driver version info */
++  {
++    ULONG info[3];
++    CLEAR (info);
++    if (DeviceIoControl (tt->hand, TAP_IOCTL_GET_VERSION,
++                       &info, sizeof (info),
++                       &info, sizeof (info), &len, NULL))
++      {
++      msg (D_TUNTAP_INFO, "TAP-Win32 Driver Version %d.%d %s",
++           (int) info[0],
++           (int) info[1],
++           (info[2] ? "(DEBUG)" : ""));
++
++      }
++    if (!(info[0] == TAP_WIN32_MIN_MAJOR && info[1] >= TAP_WIN32_MIN_MINOR))
++      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);
++
++    /* tap driver 9.8 (2.2.0 and 2.2.1 release) is buggy
++     */
++    if ( tt->type == DEV_TYPE_TUN &&
++       info[0] == 9 && info[1] == 8)
++      {
++      msg( M_FATAL, "ERROR:  Tap-Win32 driver version %d.%d is buggy regarding small IPv4 packets in TUN mode.  Upgrade to Tap-Win32 9.9 (2.2.2 release or later) or use TAP mode", (int) info[0], (int) info[1] );
++      }
++  }
++
++  /* get driver MTU */
++  {
++    ULONG mtu;
++    if (DeviceIoControl (tt->hand, TAP_IOCTL_GET_MTU,
++                       &mtu, sizeof (mtu),
++                       &mtu, sizeof (mtu), &len, NULL))
++      {
++      tt->post_open_mtu = (int) mtu;
++      msg (D_MTU_INFO, "TAP-Win32 MTU=%d", (int) mtu);
++      }
++  }
++
++  /*
++   * Preliminaries for setting TAP-Win32 adapter TCP/IP
++   * properties via --ip-win32 dynamic or --ip-win32 adaptive.
++   */
++  if (tt->did_ifconfig_setup)
++    {
++      if (tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ)
++      {
++        /*
++         * If adapter is set to non-DHCP, set to DHCP mode.
++         */
++        if (dhcp_status (tt->adapter_index) == DHCP_STATUS_DISABLED)
++          netsh_enable_dhcp (&tt->options, tt->actual_name);
++        dhcp_masq = true;
++        dhcp_masq_post = true;
++      }
++      else if (tt->options.ip_win32_type == IPW32_SET_ADAPTIVE)
++      {
++        /*
++         * If adapter is set to non-DHCP, use netsh right away.
++         */
++        if (dhcp_status (tt->adapter_index) != DHCP_STATUS_ENABLED)
++          {
++            netsh_ifconfig (&tt->options,
++                            tt->actual_name,
++                            tt->local,
++                            tt->adapter_netmask,
++                            NI_TEST_FIRST|NI_IP_NETMASK|NI_OPTIONS);
++          }
++        else
++          {
++            dhcp_masq = true;
++          }
++      }
++    }
++
++  /* set point-to-point mode if TUN device */
++
++  if (tt->type == DEV_TYPE_TUN)
++    {
++      if (!tt->did_ifconfig_setup)
++      {
++        msg (M_FATAL, "ERROR: --dev tun also requires --ifconfig");
++      }
++
++      if (tt->topology == TOP_SUBNET)
++      {
++        in_addr_t ep[3];
++        BOOL status;
++
++        ep[0] = htonl (tt->local);
++        ep[1] = htonl (tt->local & tt->remote_netmask);
++        ep[2] = htonl (tt->remote_netmask);
++
++        status = DeviceIoControl (tt->hand, TAP_IOCTL_CONFIG_TUN,
++                                  ep, sizeof (ep),
++                                  ep, sizeof (ep), &len, NULL);
++
++          msg (status ? M_INFO : M_FATAL, "Set TAP-Win32 TUN subnet mode network/local/netmask = %s/%s/%s [%s]",
++             print_in_addr_t (ep[1], IA_NET_ORDER, &gc),
++             print_in_addr_t (ep[0], IA_NET_ORDER, &gc),
++             print_in_addr_t (ep[2], IA_NET_ORDER, &gc),
++             status ? "SUCCEEDED" : "FAILED");
++
++      } else {
++
++        in_addr_t ep[2];
++        ep[0] = htonl (tt->local);
++        ep[1] = htonl (tt->remote_netmask);
++
++        if (!DeviceIoControl (tt->hand, TAP_IOCTL_CONFIG_POINT_TO_POINT,
++                              ep, sizeof (ep),
++                              ep, sizeof (ep), &len, NULL))
++          msg (M_FATAL, "ERROR: The TAP-Win32 driver rejected a DeviceIoControl call to set Point-to-Point mode, which is required for --dev tun");
++      }
++    }
++
++  /* should we tell the TAP-Win32 driver to masquerade as a DHCP server as a means
++     of setting the adapter address? */
++  if (dhcp_masq)
++    {
++      uint32_t ep[4];
++
++      /* We will answer DHCP requests with a reply to set IP/subnet to these values */
++      ep[0] = htonl (tt->local);
++      ep[1] = htonl (tt->adapter_netmask);
++
++      /* At what IP address should the DHCP server masquerade at? */
++      if (tt->type == DEV_TYPE_TUN)
++      {
++        if (tt->topology == TOP_SUBNET)
++          {
++            const in_addr_t netmask_inv = ~tt->remote_netmask;
++            ep[2] = netmask_inv ? htonl ((tt->local | netmask_inv) - 1) : 0;
++          }
++        else
++          ep[2] = htonl (tt->remote_netmask);
++
++        if (tt->options.dhcp_masq_custom_offset)
++          msg (M_WARN, "WARNING: because you are using '--dev tun' mode, the '--ip-win32 dynamic [offset]' option is ignoring the offset parameter");
++      }
++      else
++      {
++        in_addr_t dsa; /* DHCP server addr */
++
++        ASSERT (tt->type == DEV_TYPE_TAP);
++
++        if (tt->options.dhcp_masq_offset < 0)
++          dsa = (tt->local | (~tt->adapter_netmask)) + tt->options.dhcp_masq_offset;
++        else
++          dsa = (tt->local & tt->adapter_netmask) + tt->options.dhcp_masq_offset;
++
++        if (dsa == tt->local)
++          msg (M_FATAL, "ERROR: There is a clash between the --ifconfig local address and the internal DHCP server address -- both are set to %s -- please use the --ip-win32 dynamic option to choose a different free address from the --ifconfig subnet for the internal DHCP server", print_in_addr_t (dsa, 0, &gc));
++
++        if ((tt->local & tt->adapter_netmask) != (dsa & tt->adapter_netmask))
++          msg (M_FATAL, "ERROR: --tap-win32 dynamic [offset] : offset is outside of --ifconfig subnet");
++
++        ep[2] = htonl (dsa);
++      }
++
++      /* lease time in seconds */
++      ep[3] = (uint32_t) tt->options.dhcp_lease_time;
++
++      ASSERT (ep[3] > 0);
++
++#ifndef SIMULATE_DHCP_FAILED /* this code is disabled to simulate bad DHCP negotiation */
++      if (!DeviceIoControl (tt->hand, TAP_IOCTL_CONFIG_DHCP_MASQ,
++                          ep, sizeof (ep),
++                          ep, sizeof (ep), &len, NULL))
++      msg (M_FATAL, "ERROR: The TAP-Win32 driver rejected a DeviceIoControl call to set TAP_IOCTL_CONFIG_DHCP_MASQ mode");
++
++      msg (M_INFO, "Notified TAP-Win32 driver to set a DHCP IP/netmask of %s/%s on interface %s [DHCP-serv: %s, lease-time: %d]",
++         print_in_addr_t (tt->local, 0, &gc),
++         print_in_addr_t (tt->adapter_netmask, 0, &gc),
++         device_guid,
++         print_in_addr_t (ep[2], IA_NET_ORDER, &gc),
++         ep[3]
++         );
++
++      /* user-supplied DHCP options capability */
++      if (tt->options.dhcp_options)
++      {
++        struct buffer buf = alloc_buf (256);
++        if (build_dhcp_options_string (&buf, &tt->options))
++          {
++            msg (D_DHCP_OPT, "DHCP option string: %s", format_hex (BPTR (&buf), BLEN (&buf), 0, &gc));
++            if (!DeviceIoControl (tt->hand, TAP_IOCTL_CONFIG_DHCP_SET_OPT,
++                                  BPTR (&buf), BLEN (&buf),
++                                  BPTR (&buf), BLEN (&buf), &len, NULL))
++              msg (M_FATAL, "ERROR: The TAP-Win32 driver rejected a TAP_IOCTL_CONFIG_DHCP_SET_OPT DeviceIoControl call");
++          }
++        else
++          msg (M_WARN, "DHCP option string not set due to error");
++        free_buf (&buf);
++      }
++#endif
++    }
++
++  /* set driver media status to 'connected' */
++  {
++    ULONG status = TRUE;
++    if (!DeviceIoControl (tt->hand, TAP_IOCTL_SET_MEDIA_STATUS,
++                        &status, sizeof (status),
++                        &status, sizeof (status), &len, NULL))
++      msg (M_WARN, "WARNING: The TAP-Win32 driver rejected a TAP_IOCTL_SET_MEDIA_STATUS DeviceIoControl call.");
++  }
++
++  /* possible wait for adapter to come up */
++  {
++    int s = tt->options.tap_sleep;
++    if (s > 0)
++      {
++      msg (M_INFO, "Sleeping for %d seconds...", s);
++      openvpn_sleep (s);
++      }
++  }
++
++  /* possibly use IP Helper API to set IP address on adapter */
++  {
++    const DWORD index = tt->adapter_index;
++    
++    /* flush arp cache */
++    if (index != (DWORD)~0)
++      {
++      DWORD status;
++
++      if ((status = FlushIpNetTable (index)) == NO_ERROR)
++        msg (M_INFO, "Successful ARP Flush on interface [%u] %s",
++             (unsigned int)index,
++             device_guid);
++      else
++        msg (D_TUNTAP_INFO, "NOTE: FlushIpNetTable failed on interface [%u] %s (status=%u) : %s",
++             (unsigned int)index,
++             device_guid,
++             (unsigned int)status,
++             strerror_win32 (status, &gc));
++      }
++
++    /*
++     * If the TAP-Win32 driver is masquerading as a DHCP server
++     * make sure the TCP/IP properties for the adapter are
++     * set correctly.
++     */
++    if (dhcp_masq_post)
++      {
++      /* check dhcp enable status */
++      if (dhcp_status (index) == DHCP_STATUS_DISABLED)
++        msg (M_WARN, "WARNING: You have selected '--ip-win32 dynamic', which will not work unless the TAP-Win32 TCP/IP properties are set to 'Obtain an IP address automatically'");
++
++      /* force an explicit DHCP lease renewal on TAP adapter? */
++      if (tt->options.dhcp_pre_release)
++        dhcp_release (tt);
++      if (tt->options.dhcp_renew)
++        dhcp_renew (tt);
++      }
++    else
++      fork_dhcp_action (tt);
++
++    if (tt->did_ifconfig_setup && tt->options.ip_win32_type == IPW32_SET_IPAPI)
++      {
++      DWORD status;
++      const char *error_suffix = "I am having trouble using the Windows 'IP helper API' to automatically set the IP address -- consider using other --ip-win32 methods (not 'ipapi')";
++
++      /* couldn't get adapter index */
++      if (index == (DWORD)~0)
++        {
++          msg (M_FATAL, "ERROR: unable to get adapter index for interface %s -- %s",
++               device_guid,
++               error_suffix);
++        }
++
++      /* check dhcp enable status */
++      if (dhcp_status (index) == DHCP_STATUS_DISABLED)
++        msg (M_WARN, "NOTE: You have selected (explicitly or by default) '--ip-win32 ipapi', which has a better chance of working correctly if the TAP-Win32 TCP/IP properties are set to 'Obtain an IP address automatically'");
++
++      /* delete previously added IP addresses which were not
++         correctly deleted */
++      delete_temp_addresses (index);
++
++      /* add a new IP address */
++      if ((status = AddIPAddress (htonl(tt->local),
++                                  htonl(tt->adapter_netmask),
++                                  index,
++                                  &tt->ipapi_context,
++                                  &tt->ipapi_instance)) == NO_ERROR)
++        msg (M_INFO, "Succeeded in adding a temporary IP/netmask of %s/%s to interface %s using the Win32 IP Helper API",
++             print_in_addr_t (tt->local, 0, &gc),
++             print_in_addr_t (tt->adapter_netmask, 0, &gc),
++             device_guid
++             );
++      else
++        msg (M_FATAL, "ERROR: AddIPAddress %s/%s failed on interface %s, index=%d, status=%u (windows error: '%s') -- %s",
++             print_in_addr_t (tt->local, 0, &gc),
++             print_in_addr_t (tt->adapter_netmask, 0, &gc),
++             device_guid,
++             (int)index,
++             (unsigned int)status,
++             strerror_win32 (status, &gc),
++             error_suffix);
++      tt->ipapi_context_defined = true;
++      }
++  }
++  /*netcmd_semaphore_release ();*/
++  gc_free (&gc);
++}
++
++const char *
++tap_win32_getinfo (const struct tuntap *tt, struct gc_arena *gc)
++{
++  if (tt && tt->hand != NULL)
++    {
++      struct buffer out = alloc_buf_gc (256, gc);
++      DWORD len;
++      if (DeviceIoControl (tt->hand, TAP_IOCTL_GET_INFO,
++                         BSTR (&out), BCAP (&out),
++                         BSTR (&out), BCAP (&out),
++                         &len, NULL))
++      {
++        return BSTR (&out);
++      }
++    }
++  return NULL;
++}
++
++void
++tun_show_debug (struct tuntap *tt)
++{
++  if (tt && tt->hand != NULL)
++    {
++      struct buffer out = alloc_buf (1024);
++      DWORD len;
++      while (DeviceIoControl (tt->hand, TAP_IOCTL_GET_LOG_LINE,
++                            BSTR (&out), BCAP (&out),
++                            BSTR (&out), BCAP (&out),
++                            &len, NULL))
++      {
++        msg (D_TAP_WIN32_DEBUG, "TAP-Win32: %s", BSTR (&out));
++      }
++      free_buf (&out);
++    }
++}
++
++void
++close_tun (struct tuntap *tt)
++{
++  struct gc_arena gc = gc_new ();
++
++  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)
++      {
++        DWORD status;
++        if ((status = DeleteIPAddress (tt->ipapi_context)) != NO_ERROR)
++          {
++            msg (M_WARN, "Warning: DeleteIPAddress[%u] failed on TAP-Win32 adapter, status=%u : %s",
++                 (unsigned int)tt->ipapi_context,
++                 (unsigned int)status,
++                 strerror_win32 (status, &gc));
++          }
++      }
++#endif
++
++      if (tt->options.dhcp_release)
++      dhcp_release (tt);
++
++      if (tt->hand != NULL)
++      {
++        dmsg (D_WIN32_IO_LOW, "Attempting CancelIO on TAP-Win32 adapter");
++        if (!CancelIo (tt->hand))
++          msg (M_WARN | M_ERRNO, "Warning: CancelIO failed on TAP-Win32 adapter");
++      }
++
++      dmsg (D_WIN32_IO_LOW, "Attempting close of overlapped read event on TAP-Win32 adapter");
++      overlapped_io_close (&tt->reads);
++
++      dmsg (D_WIN32_IO_LOW, "Attempting close of overlapped write event on TAP-Win32 adapter");
++      overlapped_io_close (&tt->writes);
++
++      if (tt->hand != NULL)
++      {
++        dmsg (D_WIN32_IO_LOW, "Attempting CloseHandle on TAP-Win32 adapter");
++        if (!CloseHandle (tt->hand))
++          msg (M_WARN | M_ERRNO, "Warning: CloseHandle failed on TAP-Win32 adapter");
++      }
++
++      if (tt->actual_name)
++      free (tt->actual_name);
++
++      clear_tuntap (tt);
++      free (tt);
++    }
++  gc_free (&gc);
++}
++
++/*
++ * Convert --ip-win32 constants between index and ascii form.
++ */
++
++struct ipset_names {
++  const char *short_form;
++};
++
++/* Indexed by IPW32_SET_x */
++static const struct ipset_names ipset_names[] = {
++  {"manual"},
++  {"netsh"},
++  {"ipapi"},
++  {"dynamic"},
++  {"adaptive"}
++};
++
++int
++ascii2ipset (const char* name)
++{
++  int i;
++  ASSERT (IPW32_SET_N == SIZE (ipset_names));
++  for (i = 0; i < IPW32_SET_N; ++i)
++    if (!strcmp (name, ipset_names[i].short_form))
++      return i;
++  return -1;
++}
++
++const char *
++ipset2ascii (int index)
++{
++  ASSERT (IPW32_SET_N == SIZE (ipset_names));
++  if (index < 0 || index >= IPW32_SET_N)
++    return "[unknown --ip-win32 type]";
++  else
++    return ipset_names[index].short_form;
++}
++
++const char *
++ipset2ascii_all (struct gc_arena *gc)
++{
++  struct buffer out = alloc_buf_gc (256, gc);
++  int i;
++
++  ASSERT (IPW32_SET_N == SIZE (ipset_names));
++  for (i = 0; i < IPW32_SET_N; ++i)
++    {
++      if (i)
++      buf_printf(&out, " ");
++      buf_printf(&out, "[%s]", ipset2ascii(i));
++    }
++  return BSTR (&out);
++}
++
++#else /* generic */
++
++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)
++    {
++      close_tun_generic (tt);
++      free (tt);
++    }
++}
++
++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);
++}
++
++#endif
+diff -durN openvpn-2.2.2.orig/tun.h openvpn-2.2.2/tun.h
+--- openvpn-2.2.2.orig/tun.h   2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/tun.h        2012-06-01 10:40:28.000000000 +0200
+@@ -130,6 +130,7 @@
+   int topology; /* one of the TOP_x values */
+   bool did_ifconfig_setup;
++  bool did_ifconfig_ipv6_setup;
+   bool did_ifconfig;
+   bool ipv6;
+@@ -146,6 +147,10 @@
+   in_addr_t remote_netmask;
+   in_addr_t broadcast;
++  struct in6_addr local_ipv6;
++  struct in6_addr remote_ipv6;
++  int netbits_ipv6;
++
+ #ifdef WIN32
+   HANDLE hand;
+   struct overlapped_io reads;
+@@ -197,7 +202,7 @@
+ void clear_tuntap (struct tuntap *tuntap);
+ void open_tun (const char *dev, const char *dev_type, const char *dev_node,
+-             bool ipv6, struct tuntap *tt);
++             struct tuntap *tt);
+ void close_tun (struct tuntap *tt);
+@@ -206,7 +211,7 @@
+ int read_tun (struct tuntap* tt, uint8_t *buf, int len);
+ void tuncfg (const char *dev, const char *dev_type, const char *dev_node,
+-           bool ipv6, int persist_mode, const char *username,
++           int persist_mode, const char *username,
+            const char *groupname, const struct tuntap_options *options);
+ const char *guess_tuntap_dev (const char *dev,
+@@ -219,6 +224,8 @@
+                        int topology,          /* one of the TOP_x values */
+                        const char *ifconfig_local_parm,          /* --ifconfig parm 1 */
+                        const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */
++                       const char *ifconfig_ipv6_local_parm,     /* --ifconfig parm 1 / IPv6 */
++                       const char *ifconfig_ipv6_remote_parm,    /* --ifconfig parm 2 / IPv6 */
+                        in_addr_t local_public,
+                        in_addr_t remote_public,
+                        const bool strict_warn,
+diff -durN openvpn-2.2.2.orig/win32.c openvpn-2.2.2/win32.c
+--- openvpn-2.2.2.orig/win32.c 2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/win32.c      2012-06-01 10:40:28.000000000 +0200
+@@ -874,16 +874,21 @@
+ static char *
+ env_block (const struct env_set *es)
+ {
++  char * force_path = "PATH=C:\\Windows\\System32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem";
++
+   if (es)
+     {
+       struct env_item *e;
+       char *ret;
+       char *p;
+       size_t nchars = 1;
++      bool path_seen = false;
+       
+       for (e = es->list; e != NULL; e = e->next)
+       nchars += strlen (e->string) + 1;
++      nchars += strlen(force_path)+1;
++
+       ret = (char *) malloc (nchars);
+       check_malloc_return (ret);
+@@ -895,7 +900,18 @@
+             strcpy (p, e->string);
+             p += strlen (e->string) + 1;
+           }
++        if ( strncmp(e->string, "PATH=", 5 ) == 0 )
++          path_seen = true;
++      }
++
++      /* make sure PATH is set */
++      if ( !path_seen )
++      {
++        msg( M_INFO, "env_block: add %s", force_path );
++        strcpy( p, force_path );
++        p += strlen(force_path) + 1;
+       }
++
+       *p = '\0';
+       return ret;
+     }
+diff -durN openvpn-2.2.2.orig/win32.h openvpn-2.2.2/win32.h
+--- openvpn-2.2.2.orig/win32.h 2011-12-13 17:58:56.000000000 +0100
++++ openvpn-2.2.2/win32.h      2012-06-01 10:40:28.000000000 +0200
+@@ -269,6 +269,8 @@
+ /* call self in a subprocess */
+ void fork_to_self (const char *cmdline);
++const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
++int inet_pton(int af, const char *src, void *st);
+ /* Find temporary directory */
+ const char *win_get_tempdir();
diff --git a/openvpn-pam.patch b/openvpn-pam.patch
new file mode 100644 (file)
index 0000000..273a495
--- /dev/null
@@ -0,0 +1,11 @@
+--- x/src/plugins/auth-pam/auth-pam.c~ 2012-11-22 13:07:51.000000000 +0100
++++ x/src/plugins/auth-pam/auth-pam.c  2013-01-09 06:36:53.012563695 +0100
+@@ -696,7 +696,7 @@
+   struct user_pass up;
+   int command;
+ #ifdef USE_PAM_DLOPEN
+-  static const char pam_so[] = "libpam.so";
++  static const char pam_so[] = "libpam.so.0";
+ #endif
+   /*
diff --git a/openvpn-service-generator b/openvpn-service-generator
new file mode 100644 (file)
index 0000000..5c05da1
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+destunitdir=${1:-/tmp}
+
+# Do nothing if target is disabled
+[ -e /etc/systemd/system/multi-user.target.wants/openvpn.target ] || exit 0
+
+[ -f /etc/sysconfig/openvpn ] && . /etc/sysconfig/openvpn
+
+if [ -d $destunitdir/openvpn.target.wants ]; then
+       rm -f $destunitdir/openvpn.target.wants/openvpn@*.service
+else
+       mkdir -p $destunitdir/openvpn.target.wants
+fi
+
+for tun in $TUNNELS; do
+       [ -L $destunitdir/openvpn.target.wants/openvpn@$tun.service ] && \
+               continue
+       ln -s /lib/systemd/system/openvpn@.service \
+               $destunitdir/openvpn.target.wants/openvpn@$tun.service
+done
diff --git a/openvpn-tunnel.upstart b/openvpn-tunnel.upstart
new file mode 100644 (file)
index 0000000..381e5da
--- /dev/null
@@ -0,0 +1,13 @@
+# openvpn tunnel daemon
+#
+# This service runs specified tunnel
+
+stop on pld.network-stopped
+
+manual
+
+#console output
+
+respawn
+instance $TUNNEL
+exec /usr/sbin/openvpn --config /etc/openvpn/$TUNNEL.conf --writepid /var/run/openvpn/$TUNNEL.pid --cd /etc/openvpn
diff --git a/openvpn-update-resolv-conf b/openvpn-update-resolv-conf
new file mode 100644 (file)
index 0000000..af08467
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/bash
+# 
+# Parses DHCP options from openvpn to update resolv.conf
+# To use set as 'up' and 'down' script in your openvpn *.conf:
+# up /usr/lib/openvpn/update-resolv-conf
+# down /usr/lib/openvpn/update-resolv-conf
+#
+# Used snippets of resolvconf script by Thomas Hood <jdthood@yahoo.co.uk> 
+# and Chris Hanson
+# Licensed under the GNU GPL.
+#
+# 05/2006 chlauber@bnc.ch
+# 
+# Example envs set from openvpn:
+# foreign_option_1='dhcp-option DNS 193.43.27.132'
+# foreign_option_2='dhcp-option DNS 193.43.27.133'
+# foreign_option_3='dhcp-option DOMAIN be.bnc.ch'
+
+[ -x /sbin/resolvconf ] || exit 0
+
+case $script_type in
+
+up)
+       for optionname in ${!foreign_option_*} ; do
+               option="${!optionname}"
+               echo $option
+               part1=$(echo "$option" | cut -d " " -f 1)
+               if [ "$part1" == "dhcp-option" ] ; then
+                       part2=$(echo "$option" | cut -d " " -f 2)
+                       part3=$(echo "$option" | cut -d " " -f 3)
+                       if [ "$part2" == "DNS" ] ; then
+                               IF_DNS_NAMESERVERS="$IF_DNS_NAMESERVERS $part3"
+                       fi
+                       if [ "$part2" == "DOMAIN" ] ; then
+                               IF_DNS_SEARCH="$part3"
+                       fi
+               fi
+       done
+       R=""
+       if [ "$IF_DNS_SEARCH" ] ; then
+               R="${R}search $IF_DNS_SEARCH
+"
+       fi
+       for NS in $IF_DNS_NAMESERVERS ; do
+               R="${R}nameserver $NS
+"
+       done
+       echo -n "$R" | /sbin/resolvconf -a "${dev}.inet"
+       ;;
+down)
+       /sbin/resolvconf -d "${dev}.inet"
+       ;;
+esac
+
diff --git a/openvpn.init b/openvpn.init
new file mode 100644 (file)
index 0000000..9c47799
--- /dev/null
@@ -0,0 +1,161 @@
+#!/bin/sh
+#
+# openvpn      Start/stop the VPN daemon.
+#
+# chkconfig:   2345 11 89
+#
+# description: OpenVPN is a robust and highly configurable VPN (Virtual \
+#              Private Network) daemon
+#
+
+# Get service config
+[ -f /etc/sysconfig/openvpn ] && . /etc/sysconfig/openvpn
+
+[ -n "$2" ] && TUNNELS="$2"
+
+# no tunnels. exit silently
+if [ -z "$TUNNELS" ]; then
+       case "$1" in
+       start|stop|restart|reload|force-reload)
+               exit 0
+               ;;
+       esac
+fi
+
+# Source function library
+. /etc/rc.d/init.d/functions
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+# check if the tunnel $1 is up
+tunlup() {
+       local tun="$1"
+       local pidfile=/var/run/openvpn/$tun.pid
+       local pid=$(cat $pidfile 2>/dev/null)
+       kill -0 $pid 2>/dev/null
+       return $?
+}
+
+# check if all the configured tunnels are up
+tunlsup() {
+       ret=0
+       for tun in $TUNNELS; do
+               tunlup $tun && continue
+               ret=1
+       done
+       return $ret
+}
+
+start() {
+       # Check if the service is already running?
+       if tunlsup; then
+               msg_already_running "OpenVPN"
+               return
+       fi
+
+       msg_starting "OpenVPN"; started
+       for tun in $TUNNELS; do
+               config="/etc/openvpn/$tun.conf"
+               if [ ! -f "$config" ]; then
+                       nls "Invalid tunnel \`%s': missing config: %s" $tun "$config"
+                       fail
+                       RET=1
+               else
+                       show "Starting OpenVPN tunnel %s" "$tun"
+                       if tunlup $tun; then
+                               started
+                               continue
+                       fi
+
+                       daemon --pidfile /var/run/openvpn/$tun.pid /usr/sbin/openvpn --daemon --writepid /var/run/openvpn/$tun.pid \
+                               --config $config --cd /etc/openvpn ${OPENVPN_OPT}
+                       RET=$?
+               fi
+               [ $RETVAL -eq 0 ] && RETVAL=$RET
+       done
+       [ $RETVAL -eq 0 ] && touch /var/lock/subsys/openvpn
+}
+
+stop() {
+       if ! tunlsup; then
+               msg_not_running "OpenVPN"
+               return
+       fi
+
+       # Stop daemons.
+       msg_stopping "OpenVPN"; started
+       for tun in $TUNNELS; do
+               pidfile=/var/run/openvpn/$tun.pid
+               [ -f "$pidfile" ] || continue
+               pid=`cat "$pidfile"`
+               show "Stopping OpenVPN tunnel %s" "$tun"; busy
+               killproc --pidfile openvpn/$tun.pid || err=1
+       done
+       rm -f /var/lock/subsys/openvpn >/dev/null 2>&1
+}
+
+reload() {
+       if ! tunlsup; then
+               msg_not_running "OpenVPN"
+               RETVAL=7
+               return
+       fi
+
+       msg_reloading "OpenVPN"; started
+       for tun in $TUNNELS; do
+               show "Reloading OpenVPN tunnel %s" "$tun"
+               killproc --pidfile openvpn/$tun.pid openvpn -HUP
+               [ $? -ne 0 -a $RETVAL -eq 0 ] && RETVAL=7
+       done
+}
+
+status() {
+       nls "Configured tunnels:"
+       echo " $TUNNELS"
+       nls "Currently active tunnels:"
+       for pidfile in /var/run/openvpn/*.pid; do
+               [ -f "$pidfile" ] || continue
+               tun=${pidfile#/var/run/openvpn/}
+               tun=${tun%.pid}
+               tunlup $tun && echo -n " $tun($(cat $pidfile))"
+       done
+       echo ""
+       nm_ovpn_pid=$(ps -o pid= -C nm-openvpn-service | xargs)
+       if [ "$nm_ovpn_pid" ]; then
+               nls "NM ($nm_ovpn_pid) managed OpenVPN sessions"
+               ps -o pid,user,command --ppid=$nm_ovpn_pid
+       fi
+       tunlsup
+       RETVAL=$?
+}
+
+upstart_controlled --except status
+
+RETVAL=0
+# See how we were called.
+case "$1" in
+  start)
+       start
+       ;;
+  stop)
+       stop
+       ;;
+  reload|force-reload)
+       reload
+       ;;
+  restart)
+       stop
+       sleep 1
+       start
+       ;;
+  status)
+       status
+       ;;
+  *)
+       msg_usage "$0 {start|stop|restart|reload|force-reload|status}"
+       exit 3
+       ;;
+esac
+
+exit $RETVAL
diff --git a/openvpn.spec b/openvpn.spec
new file mode 100644 (file)
index 0000000..70eddeb
--- /dev/null
@@ -0,0 +1,158 @@
+
+# Conditional build:
+%bcond_without pkcs11          # build without PKCS#11 support
+
+Summary:       VPN Daemon
+Summary(pl.UTF-8):     Serwer VPN
+Name:          openvpn
+Version:       2.3.6
+Release:       1
+License:       GPL v2
+Group:         Networking/Daemons
+Source0:       http://swupdate.openvpn.net/community/releases/%{name}-%{version}.tar.gz
+# Source0-md5: 6ca03fe0fd093e0d01601abee808835c
+Source1:       %{name}.init
+Source2:       %{name}.sysconfig
+Source3:       %{name}.tmpfiles
+Source4:       openvpn-service-generator
+Source5:       openvpn.target
+Source6:       openvpn@.service
+Patch0:                %{name}-pam.patch
+URL:           http://www.openvpn.net/
+BuildRequires: autoconf >= 2.59
+BuildRequires: automake
+BuildRequires: libselinux-devel
+BuildRequires: lzo-devel
+BuildRequires: openssl-devel >= 0.9.7d
+BuildRequires: pam-devel
+%{?with_pkcs11:BuildRequires:  pkcs11-helper-devel}
+BuildRequires: rpmbuild(macros) >= 1.671
+BuildRequires: systemd-devel
+Requires(post,preun):  /sbin/chkconfig
+Requires(post,preun,postun):   systemd-units >= 38
+Requires:      /sbin/ip
+Requires:      rc-scripts >= 0.4.3.0
+Requires:      systemd-units >= 38
+Conflicts:     kernel < 2.4
+BuildRoot:     %{tmpdir}/%{name}-%{version}-root-%(id -u -n)
+
+%define                _localstatedir  /var
+
+%description
+OpenVPN is a robust and highly configurable VPN (Virtual Private
+Network) daemon which can be used to securely link two or more private
+networks using an encrypted tunnel over the internet.
+
+%description -l pl.UTF-8
+OpenVPN jest mocnym i silnie konfigurowalnym serwerem VPN (Wirtualne
+Sieci Prywatne), który może być użyty do bezpiecznego łączenia dwóch
+lub więcej prywatnych sieci używając zaszyfrowanego tunelu poprzez
+internet.
+
+%package devel
+Summary:       Header files for OpenVPN plugins development
+Summary(pl.UTF-8):     Pliki nagłówkowe do tworzenia wtyczek OpenVPN
+Group:         Development/Libraries
+
+%description devel
+This is the package containing the header files for OpenVPN plugins
+development.
+
+%description devel -l pl.UTF-8
+Ten pakiet zawiera pliki nagłówkowe do tworzenia wtyczek OpenVPN.
+
+%prep
+%setup -q
+%patch0 -p1
+
+sed -e 's,/''usr/lib/openvpn,%{_libdir}/%{name},' %{SOURCE3} > contrib/update-resolv-conf
+
+%build
+%{__aclocal} -I m4
+%{__autoheader}
+%{__autoconf}
+%{__automake}
+
+%configure \
+       %{!?with_pkcs11:--disable-pkcs11} \
+       --enable-password-save \
+       --enable-iproute2 \
+       --enable-selinux \
+       --enable-systemd \
+       IFCONFIG=/sbin/ifconfig \
+       IPROUTE=/sbin/ip \
+       ROUTE=/sbin/route \
+       NETSTAT=/bin/netstat
+
+%{__make}
+
+%install
+rm -rf $RPM_BUILD_ROOT
+install -d $RPM_BUILD_ROOT{%{_sysconfdir}/openvpn,%{_sbindir},%{_mandir}/man8} \
+       $RPM_BUILD_ROOT{/etc/{rc.d/init.d,sysconfig},/var/run/openvpn,%{_includedir}} \
+       $RPM_BUILD_ROOT{%{_libdir}/%{name}/plugins,%{systemdtmpfilesdir},%{systemdunitdir}} \
+       $RPM_BUILD_ROOT/lib/systemd/system-generators
+
+%{__make} install \
+       DESTDIR=$RPM_BUILD_ROOT
+
+install %{SOURCE1} $RPM_BUILD_ROOT/etc/rc.d/init.d/%{name}
+install %{SOURCE2} $RPM_BUILD_ROOT/etc/sysconfig/%{name}
+install %{SOURCE3} $RPM_BUILD_ROOT%{systemdtmpfilesdir}/%{name}.conf
+
+install -p %{SOURCE4} $RPM_BUILD_ROOT/lib/systemd/system-generators/openvpn-service-generator
+install -p %{SOURCE5} $RPM_BUILD_ROOT%{systemdunitdir}/openvpn.target
+install -p %{SOURCE6} $RPM_BUILD_ROOT%{systemdunitdir}/openvpn@.service
+ln -s /dev/null $RPM_BUILD_ROOT%{systemdunitdir}/openvpn.service
+
+%{__rm} $RPM_BUILD_ROOT%{_libdir}/%{name}/plugins/*.la
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+/sbin/chkconfig --add openvpn
+%service openvpn restart "OpenVPN"
+%systemd_post openvpn.target
+
+%preun
+if [ "$1" = "0" ]; then
+       %service openvpn stop
+       /sbin/chkconfig --del openvpn
+fi
+%systemd_preun openvpn.target
+
+%postun
+%systemd_reload
+
+%triggerpostun -- openvpn < 2.3.2-2
+[ -f /etc/sysconfig/rpm ] && . /etc/sysconfig/rpm
+[ ${RPM_ENABLE_SYSTEMD_SERVICE:-yes} = no ] && exit 0
+[ "$(echo /etc/rc.d/rc[0-6].d/S[0-9][0-9]openvpn)" = "/etc/rc.d/rc[0-6].d/S[0-9][0-9]openvpn" ] && exit 0
+export SYSTEMD_LOG_LEVEL=warning SYSTEMD_LOG_TARGET=syslog
+/bin/systemctl --quiet enable openvpn.target || :
+exit 0
+
+%files
+%defattr(644,root,root,755)
+%doc AUTHORS README* ChangeLog sample/sample-{config-files,keys,scripts} doc/management-notes.txt
+%doc *.IPv6 src/plugins/*/README.*
+%dir %{_sysconfdir}/openvpn
+%config(noreplace) %verify(not md5 mtime size) /etc/sysconfig/%{name}
+%attr(755,root,root) %{_sbindir}/openvpn
+%attr(754,root,root) /etc/rc.d/init.d/%{name}
+%attr(755,root,root) /lib/systemd/system-generators/%{name}-service-generator
+%{systemdunitdir}/%{name}.service
+%{systemdunitdir}/%{name}.target
+%{systemdunitdir}/%{name}@.service
+%dir %{_libdir}/%{name}
+%dir %{_libdir}/%{name}/plugins
+%attr(755,root,root) %{_libdir}/%{name}/plugins/*.so
+%{_mandir}/man8/openvpn.8*
+%dir /var/run/openvpn
+/usr/lib/tmpfiles.d/%{name}.conf
+
+%files devel
+%defattr(644,root,root,755)
+%doc doc/README.plugins sample/sample-plugins
+%{_includedir}/openvpn-plugin.h
diff --git a/openvpn.sysconfig b/openvpn.sysconfig
new file mode 100644 (file)
index 0000000..4fb33e6
--- /dev/null
@@ -0,0 +1,18 @@
+# Customized settings for openvpn
+
+# Nice level for sysconf
+SERVICE_RUN_NICE_LEVEL="+0"
+
+# Options for all tunnels
+#OPENVPN_OPT="--script-security 2"
+
+# Tunnels to set up. For each tunnel name, there should be config file in /etc/openvpn
+#
+# On systems running systemd after updating the list below you have to
+# restart systemd to regenerate openvpn@TUNNEL.service services
+# (systemctl --system daemon-reload), stop and start openvpn.target when
+# removing elements from list or restart openvpn.target when only adding
+# elements (systemctl stop openvpn.target / systemctl restart openvpn.target).
+#
+#TUNNELS="mytunnel"
+TUNNELS=""
diff --git a/openvpn.target b/openvpn.target
new file mode 100644 (file)
index 0000000..652626e
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=Start openvpn tunnels
+After=network.target
+Wants=network.target
+
+[Install]
+WantedBy=multi-user.target
diff --git a/openvpn.tmpfiles b/openvpn.tmpfiles
new file mode 100644 (file)
index 0000000..4a3dc4a
--- /dev/null
@@ -0,0 +1 @@
+d /var/run/openvpn 0755 root root -
diff --git a/openvpn.upstart b/openvpn.upstart
new file mode 100644 (file)
index 0000000..13abace
--- /dev/null
@@ -0,0 +1,33 @@
+description "Start the OpenVPN tunnels"
+
+start on pld.network-started
+stop on pld.network-stopped
+
+task
+console output
+
+script
+       [ -f /etc/sysconfig/openvpn ] && . /etc/sysconfig/openvpn
+
+       for tun in $TUNNELS; do
+               if [ ! -f "/etc/openvpn/$tun.conf" ]; then
+                       # skip invalid tunnels
+                       continue
+               fi
+
+               initctl start openvpn-tunnel TUNNEL=$tun >/dev/null || :
+       done
+end script
+
+pre-stop script
+       [ -f /etc/sysconfig/openvpn ] && . /etc/sysconfig/openvpn
+
+       for tun in $TUNNELS; do
+               if [ ! -f "/etc/openvpn/$tun.conf" ]; then
+                       # skip invalid tunnels
+                       continue
+               fi
+
+               initctl stop openvpn-tunnel TUNNEL=$tun >/dev/null || :
+       done
+end script
diff --git a/openvpn@.service b/openvpn@.service
new file mode 100644 (file)
index 0000000..4a2db14
--- /dev/null
@@ -0,0 +1,16 @@
+[Unit]
+Description=OpenVPN tunnel on %I
+Requires=openvpn.target
+After=openvpn.target
+PropagateReloadFrom=openvpn.target
+
+[Service]
+Type=forking
+PrivateTmp=true
+EnvironmentFile=-/etc/sysconfig/openvpn
+PIDFile=/var/run/openvpn/%i.pid
+ExecStart=/usr/sbin/openvpn --daemon --writepid /var/run/openvpn/%i.pid --config /etc/openvpn/%i.conf --cd /etc/openvpn $OPENVPN_OPT
+ExecReload=/bin/kill -HUP $MAINPID
+
+[Install]
+WantedBy=openvpn.target