diff -rauN openvpn-2.2.0/ChangeLog.IPv6 openvpn-2.2.0-debian/ChangeLog.IPv6 --- openvpn-2.2.0/ChangeLog.IPv6 1970-01-01 01:00:00.000000000 +0100 +++ openvpn-2.2.0-debian/ChangeLog.IPv6 2011-05-21 19:10:29.000000000 +0200 @@ -0,0 +1,394 @@ +Do 31. Dez 15:32:40 CET 2009 Gert Doering + + * Basic IPv6 p2mp functionality implemented + + * new options: + - server-ipv6 + - ifconfig-ipv6 + - ifconfig-ipv6-pool + - route-ipv6 + - iroute-ipv6 + + * modules touched: + - init.c: init & setup IPv6 route list & add/delete IPv6 routes + - tun.c: add "ifconfig" and "route" handling for IPv6 + - multi.c: IPv6 ifconfig-pool assignments + put to route-hash table + push to client + - pool.c: extend pools to handle IPv4+IPv6, and also return IPv6 address + IPv6 address saved to file if ifconfig-pool-persist is set + (but ignored on read due to the way pools work) + - mroute.c: handle reading src/dst addresses from IPv6 packets + (so multi.c can check against route-hash table) + handle printing of IPv6 mroute_addr structure + - helper.c: implement "server-ipv6" macro (->ifconfig-ipv6, pool, ...) + - options.c: implement all the new options + add helper functions for IPv6 address handling + - forward.c: tell do_route() about IPv6 routes + - route.c: handle IPv6 route lists + route option lists + extend add_routes() to do IPv4 + IPv6 route lists + extend delete_routes() to do IPv4 + IPv6 route lists + implement add_route_ipv6(), delete_route_ipv6() to call + system-dependend external program to do the work + - push.c: handle pushing of "ifconfig-ipv6" option + - socket.c: helper function to check & print IPv6 address strings + + * known issues: + - operating system support on all but Linux (ifconfig, route) + - route-ipv6 gateway handling + - iroute-ipv6 not implemented + - TAP support: ifconfig, routing (route needs gateway!) + + * release as patch 20091231-1 + +Thu Dec 31 17:02:08 CET 2009 + + * NetBSD port (NetBSD 3.1 on Sparc64) + + * mroute.c, socket.c: make byte/word access to in6_addr more portable + + * tun.c: fix IPv6 ifconfig arguments on NetBSD + + still doesn't work on NetBSD 3.1, "ifconfig tun0 inet6..." errors with + + ifconfig: SIOCAIFADDR: Address family not supported by protocol family + + (sys/net/if_tun.c, needs to be revision 1.80 or later, NetBSD PR 32944, + included in NetBSD 4.0 and up) + + +Fri Jan 1 14:07:15 CET 2010 + + * FreeBSD port (FreeBSD 6.3-p12 on i386) + + * tun.c: implement IPv6 ifconfig setting for FreeBSD + + * route.c: fix %s/%s argument to IPv6 route add/delete command for *BSD + + * TEST SUCCESS: FreeBSD 6.3-p12, server-ipv6, route-ipv6, ccd/iroute-ipv6 + + * multi.c: implement setting and deleting of iroute-ipv6 + (multi_add_iroutes(), multi_del_iroutes()) + * mroute.c: add mroute_helper_add_iroute6(), mroute_helper_del_iroute6() + * mroute.h: add prototypes, increase MR_HELPER_NET_LEN to 129 (/0.../128) + * multi.c: zeroize host part of IPv6 iroutes in multi_learn_in6_addr() + * mroute.c: implement mroute_addr_mask_host_bits() for IPv6 + + * TEST SUCCESS: Linux 2.6.30 (Gentoo)/iproute2, server-ipv6, ccd/iroute-ipv6 + + * TEST SUCCESS: Linux 2.6.30 (Gentoo)/ifconfig, client-ipv6 + + * TEST FAIL: NetBSD 5.0, IPv6 client + - "ifconfig tun0 .../64" does not create a "connected" route + - adding routes fails + + --> more work to do here. + + * release as patch 20100101-1 + + * TEST FAIL: + FreeBSD 6.3-p12 server "--topology subnet" + Linux/ifconfig client + - BSD sends ICMP6 neighbor solicitations, which are ignored by Linux + - server tun interface is not in p2p mode, client tun interface *is* + + * TEST SUCCESS: non-ipv6 enabled client -> "--server-ipv6" server + (warnings in the log file, but no malfunctions) + + +Sat Jan 2 19:48:35 CET 2010 + + * tun.c: change "ipv6_support()", do not turn off tt->ipv6 unconditionally + if we don't know about OS IPv6 support - just log warning + + * tun.c: implement "ifconfig inet6" setting for MacOS X / Darwin + + * route.c: split *BSD system dependent part of add/delete_route_ipv6() + into FreeBSD/Dragonfly and NetBSD/Darwin/OpenBSD variants + ("2001:db8::/64" vs. "2001:db8:: --prefixlen 64"). + + * tun.c: on MacOS X, NetBSD and OpenBSD, explicitely set on-link route + + * TEST SUCCESS: MacOS X, client-ipv6 with route-ipv6 + + +Sun Jan 3 10:55:31 CET 2010 + + * route.c: NetBSD fails with "-iface tun0", needs gateway address + (assume that the same syntax is needed for OpenBSD) + + * route.h: introduce "remote_endpoint_ipv6" into "struct route_ipv6_list" + + * init.c: pass "ifconfig_ipv6_remote" as gateway to init_route_ipv6_list() + + * route.c: + - init_route_ipv6(): use "remote_endpoint_ipv6" as IPv6 gateway address + if no gateway was specified explicitely + + - init_route_ipv6_list(): fill in "remote_endpoint_ipv6", if parseable + + - get rid of "GATEWAY-LESS ROUTE6" warning + + * route.c, add_route_ipv6() + - explicitely clear host bits of base address, to be able to more + easily set up "connected" /64 routes on NetBSD+Darwin + + - split system-dependent part between Darwin and NetBSD/OpenBSD + (Darwin can use "-iface tun0", NetBSD/OpenBSD get gateway address) + + - change Solaris comments from "known-broken" to "unknown" + + * tun.c: rework NetBSD tunnel initialization and tun_read() / tun_write() + to work the same way OpenBSD and NetBSD do - tunnel is put into + "multi-af" mode, and all packet read/write activity is prepended by + a 32 bit value specifying the address family. + + * TEST SUCCESS: NetBSD 5.0/Sparc64: client-ipv6 with route-ipv6 + + * TEST SUCCESS: MacOS X 10.5: client-ipv6 with route-ipv6 + + * (RE-)TEST SUCCESS: Linux/iproute2: server-ipv6 + Linux/ifconfig: client-ipv6 + FreeBSD 6.3: server-ipv6 + + * release as patch 20100103-1 + + * options.c: document all new options in "--help" + + * tun.c: fix typo in Solaris-specific section + + * socket.h, socket.c: change u_int32_t to uint32_t + (Solaris - and all the rest of the code uses "uintNN" anyway) + +Mon Jan 4 17:46:58 CET 2010 + + * socket.c: rework add_in6_addr() to use 32-bit access to struct in6_addr + (Solaris has no 16-bit values in union, but this is more elegant as well) + + * tun.c: fix "ifconfig inet6" command for Solaris + + * tun.c: make sure "tun0 inet6" is unplumbed first, cleanup leftovers + + * route.c: add routes with "metric 0" on solaris, otherwise they just + don't work (someone who understands Solaris might want to fix this). + + * Solaris "sort of" works now - ifconfig works, route add does not give + errors, "netstat -rn" looks right, but packets are discarded unless + the routes are installed with "metric 0". So we just use "metric 0"... + + * CAVEAT: Solaris "ifconfig ... preferred" interferes with source address + selection. So if there are any active IPv6 interfaces configured with + "preferred", packets leaving out the tunnel will use the wrong source + IPv6 address. Not fixable from within OpenVPN. + + * CAVEAT2: Solaris insists on doing DHCPv6 on tun0 interfaces by default, + so DHCPv6 solicitation packets will be seen. Since the server end has + no idea what to do with them, they are a harmless nuisance. Fixable + on the Solaris side via "ndpd.conf" (see ``man ifconfig''). + + * release as patch 20100104-1 + +Fri Jan 8 10:00:50 CET 2010 + + * import into git repository + + * options.c: add sanity checks for most typical error cases + (--ifconfig-ipv6-pool configured with no --ifconfig-ipv6, etc) + + * options.c: modify get_ipv6_addr() to be more flexible about netbits + (optional now, default to /64) and to return the address-without-netbits + string now (-> for options that want the IPv6 address in printable + form, but without /nn) + + * options.c: modify --ifconfig-ipv6 to optionally accept /netbits, + you can do now "ifconfig-ipv6 2001:df8::1/64 2001:df8::2" or just + "ifconfig-ipv6 2001:df8::5 2001:df8::7", defaulting to /64 + + * options.h: add necessary structure elements for --ifconfig-ipv6-push + + * options.c: implement "parse options" side of --ifconfig-ipv6-push + +Tue Jan 12 22:42:09 CET 2010 + + * tun.c: in TARGET_NETBSD #ifdef, distinguish between "old" code + (IPv4 only, but unmodified read/write) and "new" code (multi-af, + extra 32 bit AF on read/write of the tun interface) - pre-4.0 + NetBSD systems don't have TUNSIFHEAD, no way to have common code. + + * TEST SUCCESS: NetBSD 5.0/Sparc64: client-ipv6 with route-ipv6 (v4+v6) + + * TEST SUCCESS: NetBSD 3.1/Sparc64: client-ipv6 with route-ipv6 (v4-only) + +Thu Jan 14 15:41:50 CET 2010 + + * multi.c: if "--ifconfig-push" is used together with "--ifconfig-ipv6-pool" + and no "--ifconfig-ipv6-push" is seen, issue warning - the current + implementation of pools has IPv6 tied to IPv4, so if v4 does not use + the pool, it breaks for IPv6. Not a *big* problem (since there is + enough v6, just give those users a static v6 address as well), but needs + to be pointed out clearly. + + * release as patch 20100114-1 + +Tue Feb 16 14:43:28 CET 2010 + + * options.c: print "IPv6 payload patch" release date in "--version" + + * tun.c: undo change to init_tun() (moving "bool tun" and call to + "is_tun_p2p()" further up) - it wasn't needed and breaks "make check" + + * git stuff: rebase on David Sommerseth's openvpn-testing git tree + + * release as patch 20100216-1 + +Fri Feb 26 19:59:01 CET 2010 + + * init.c: initialize tuntap->ipv6 in do_init_tun() (to make sure it's + always initialized early-enough, independent of the sequence of + do_ifconfig()/open_tun() [see ifconfig_order() in tun.h]) + + * tun.c, init.c: remove "bool ipv6" argument to tuncfg(), open_tun() + and open_tun_generic() - obsoleted by previous change + + * tun.c: remove ipv6_support() - original purpose was unclear, and all + current platforms (except linux-very-old) fully support IPv6 now :-) + + * tun.c: initial implementation of "netsh" IPv6-ifconfig for Win32 + + * RE-TEST SUCCESS: Linux/i386/ifconfig, client-tun/net30, v4+v6 + +Sun Feb 28 17:05:57 CET 2010 + + * tun.c: NetBSD dependent part: correct destroying/re-creation of tun dev + + * tun.c: move adding of "connected" IPv6 prefix to new helper function, + add_route_connected_v6_net() + + * RE-TEST SUCCESS: NetBSD 5.0/Sparc64, client-tun/net30, v4+v6 + + * RE-TEST SUCCESS: NetBSD 3.1/Sparc64: client-tun/net30, v4-only + + * RE-TEST SUCCESS: Linux/i386/iproute2: server-tun/net30, v4+v6 + + * tun.c: add #ifdef TARGET_DARWIN block for *_tun() functions, to + be able to modify close_tun() for unconfiguring IPv6 + + * tun.c: on close_tun() on MacOS X, need to de-configure "lo0" route for + configured IPv6 address + + * RE-TEST SUCCESS: MacOS X (10.5)/i386: client-tun/net30, v4+v6 + + * route.c: implement ipv6 route adding / deletion via "netsh" for WIN32 + + * TEST FAIL: Windows XP fails, because the tun/tap driver does not + forward IPv6 frames kernel->userland if in "tun" mode + + * options.c: set IPv6 version to 20100228-1 + + * release as patch 20100228-1 + +Sun Mar 7 19:17:33 CET 2010 + + * options.c: set IPv6 version to 20100307-1 + + * TODO.IPv6: add note about OpenBSD TODO (#16) + + * route.c: set (and remove) "magic next hop" fe80::8 for IPv6 routes on + Win32 + + * install-win32/settings.in: bump TAP driver version from 9.6 to 9.7 + and TAP_RELDATE to "07/03/2010" + + * tap-win32/proto.h: add data types and definitions needed for IPv6 + + * tap-win32/types.h: add m_UserToTap_IPv6 ethernet header for IPv6 packets + + * tap-win32/tapdrvr.c: implement support for IPv6 in TUN mode: + - IPv6 packets User->OS need correct ether type + - IPv6 packets OS->User get correctly forwarded + - IPv6 neighbour discovery packets for "fe80::8" (magic address + installed as route-nexthop by OpenVPN.exe) get answered locally + + * TEST SUCCESS: WindowsXP/32bit: client-tun/net30, v4+v6 + + * tun.c: if IPv6 requested in TUN mode, and TUN/TAP driver version + is older than 9.7, log warning and disable IPv6 (won't work anyway). + + * release as patch 20100307-1 + +Sat Jul 10 14:37:52 CEST 2010 + + * TEST SUCCESS: point-to-point tun mode with --ifconfig-ipv6 between + Solaris10/sparc and Linux (Michal Ludvig) + (using the whiteboard tun driver on Solaris, otherwise "no IPv6") + +Sun Aug 8 12:30:44 CEST 2010 + + * route.c: split NetBSD and OpenBSD parts of add_route_ipv6() and + delete_route_ipv6(), implement OpenBSD variant + (needs "-prefixlen nn" while NetBSD uses "/nn") + + * tun.c: implement IPv6 ifconfig for OpenBSD + + * tun.c: destroy tunX interface at tun_close() on OpenBSD (cleanup) + + * TEST SUCCESS: OpenBSD 4.7: client-tun/net30, v4+v6 + +Thu Sep 2 21:18:32 CEST 2010 + + * tun.c: the TAP binary in 2.2-beta3 has the IPv6 related changes, but + the version number is 9.8 now -> check for 9.8, not 9.7 + +Wed Sep 22 22:20:37 CEST 2010 + + * tun.c: bugfix for Linux/iproute2/"topology subnet". Works :-) + + * TEST SUCCESS: Linux/ifconfig: client-tun/net30+subnet, v4+v6 + + * TEST SUCCESS: Linux/iproute2: client-tun/net30+subnet, v4+v6 + + * options.c: tag as 20100922-1 so "allmerged" users can see IPv6 change + +Fri Sep 24 17:57:41 CEST 2010 + + * TEST SUCCESS: Linux/: client-tap, v4+v6, ping6 on connected addr + + * TEST FAIL: Linux/: client-tap, v6, route6 (gateway missing) + +Do 21. Okt 19:36:49 CEST 2010 + + * t_client.sh.in: cherrypick commit f25fe91a40aa3f and 6f1e61b41be52 + (proper exit codes to signal "SKIP" if we do not want to run) + +So 16. Jan 17:25:23 CET 2011 + + * tun.c, route.c: cherrypick 121755c2cb4891f and f0eac1a5979096c67 + (TAP driver and "topology subnet" support for Solaris) + + * tun.c: add IPv6 configuration for TAP interfaces (:1 inet6) + + * tun.c: on close_tun on Solaris, unplumb IPv6 TUN or TAP interfaces + + * TEST SUCCESS: OpenSolaris: client-tun, v4+v6 + TEST SUCCESS: OpenSolaris: client-tap, v4+v6, ping6 on connected addr + TEST FAIL: OpenSolaris: client-tap, v6, route6 (gateway missing) + +So 24. Apr 16:51:45 CEST 2011 + + * rebase to "beta2.2" branch (at 2.2RC2 tag) + + * mroute.c: remove mroute_helper_lock/_unlock() calls for IPv6 + * socket.c: remove locking with L_INET_NTOA mutex + (all the threading stuff got removed by David Sommerseth for 2.2) + + * mroute.c: remove duplicate mroute_helper_add_iroute6() and + mroute_helper_del_iroute6() - "git rebase" artefact + + * ChangeLog.IPv6 and TODO.IPv6: add to commit + + * options.c: tag as 20110424-2 (2.2RC2) + + * TEST SUCCESS: Linux/ifconfig: client-tun/net30+subnet, v4+v6 + + * TEST SUCCESS: Linux/iproute2: client-tun/net30+subnet, v4+v6 + diff -rauN openvpn-2.2.0/README-payload.IPv6 openvpn-2.2.0-debian/README-payload.IPv6 --- openvpn-2.2.0/README-payload.IPv6 1970-01-01 01:00:00.000000000 +0100 +++ openvpn-2.2.0-debian/README-payload.IPv6 2011-05-21 19:10:29.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 -rauN openvpn-2.2.0/README.IPv6 openvpn-2.2.0-debian/README.IPv6 --- openvpn-2.2.0/README.IPv6 1970-01-01 01:00:00.000000000 +0100 +++ openvpn-2.2.0-debian/README.IPv6 2011-05-21 19:07:50.000000000 +0200 @@ -0,0 +1,81 @@ +[ Last updated: 25-Mar-2011. ] + +OpenVPN-2.1 over UDP6/TCP6 README for ipv6-0.4.x patch releases: +( --udp6 and --tcp6-{client,server} ) + +* Availability + Source code under GPLv2 from http://github.com/jjo/openvpn-ipv6 + + Distro ready repos/packages: + o Debian sid official repo, by Alberto Gonzalez Iniesta, + starting from openvpn_2.1~rc20-2 + o Gentoo official portage tree, by Marcel Pennewiss: + - https://bugs.gentoo.org/show_bug.cgi?id=287896 + o Ubuntu package, by Bernhard Schmidt: + - https://launchpad.net/~berni/+archive/ipv6/+packages + o Freetz.org, milestone freetz-1.2 + - http://trac.freetz.org/milestone/freetz-1.2 + +* Status: + o OK: + - upd6,tcp6: GNU/Linux, win32, openbsd-4.7, freebsd-8.1 + - udp4->upd6,tcp4->tcp6 (ipv4/6 mapped): GNU/Linux + (gives a warning on local!=remote proto matching) + o NOT: + - win32: tcp4->tcp6 (ipv4/6 mapped) fails w/connection refused + o NOT tested: + - mgmt console + +* Build setup: + ./configure --enable-ipv6 (by default) + +* Usage: + For IPv6 just specify "-p upd6" an proper IPv6 hostnames, adapting the example + from man page ... + + On may: + openvpn --proto udp6 --remote --dev tun1 \ + --ifconfig 10.4.0.1 10.4.0.2 --verb 5 --secret key + + On june: + openvpn --proto udp6 --remote --dev tun1 \ + --ifconfig 10.4.0.2 10.4.0.1 --verb 5 --secret key + + Same for --proto tcp6-client, tcp6-server. + +* Main code changes summary: + - socket.h: New struct openvpn_sockaddr type that holds sockaddrs and pktinfo, + (here I omitted #ifdef USE_PF_xxxx, see socket.h ) + + struct openvpn_sockaddr { + union { + struct sockaddr sa; + struct sockaddr_in in; + struct sockaddr_in6 in6; + } addr; + }; + + struct link_socket_addr + { + struct openvpn_sockaddr local; + struct openvpn_sockaddr remote; + struct openvpn_sockaddr actual; + }; + + PRO: allows simple type overloading: local.addr.sa, local.addr.in, local.addr.in6 ... etc + (also local.pi.in and local.pi.in6) + + - several function prototypes moved from sockaddr_in to openvpn_sockaddr + - several new sockaddr functions needed to "generalize" AF_xxxx operations: + addr_copy(), addr_zero(), ...etc + proto_is_udp(), proto_is_dgram(), proto_is_net() + +* TODO: See TODO.IPv6 + +-- +JuanJo Ciarlante jjo () google () com ............................ +: : +. Linux IP Aliasing author . +. Modular algo (AES et all) support for FreeSWAN/OpenSWAN author . +. OpenVPN over IPv6 support . +:...... plus other scattered free software bits in the wild ...: diff -rauN openvpn-2.2.0/TODO-payload.IPv6 openvpn-2.2.0-debian/TODO-payload.IPv6 --- openvpn-2.2.0/TODO-payload.IPv6 1970-01-01 01:00:00.000000000 +0100 +++ openvpn-2.2.0-debian/TODO-payload.IPv6 2011-05-21 19:10:29.000000000 +0200 @@ -0,0 +1,149 @@ +known issues for IPv6 payload support in OpenVPN +----------------------------------------------- + +1.) "--topology subnet" doesn't work together with IPv6 payload on FreeBSD + (verified for FreeBSD server, Linux/ifconfig client, problems + with ICMP6 neighbor solicitations from BSD not being answered by Linux) + +2.) NetBSD IPv6 support doesn't work + ("connected" route is not auto-created, "route-ipv6" adding fails) + + * fixed, 3.1.10 * + +3.) route deletion for IPv6 routes is not yet done + + * fixed for configured routes, 3.1.10 * + * missing for manual-ifconfig-connected (NetBSD, Darwin, Win32) + +4.) do "ifconfig tun0 inet6 unplumb" or "ifconfig tun0 destroy" for + Solaris, *BSD, ... at program termination time, to clean up leftovers + (unless tunnel persistance is desired). + + For Solaris, only the "ipv6 tun0" is affected, for the *BSDs all tun0 + stay around. + +4a.) deconfigure IPv6 on tun interface on session termination, otherwise + one could end up with something like this (on NetBSD): + +tun0: flags=8051 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) diff -rauN openvpn-2.2.0/TODO.IPv6 openvpn-2.2.0-debian/TODO.IPv6 --- openvpn-2.2.0/TODO.IPv6 1970-01-01 01:00:00.000000000 +0100 +++ openvpn-2.2.0-debian/TODO.IPv6 2011-05-21 19:07:50.000000000 +0200 @@ -0,0 +1,30 @@ +[ Last updated: 11-Nov-2009. ] + +* All platforms: + o mgmt console: as currently passes straight in_addr_t bits around + + o make possible to get AF from getaddrinfo() answer, ie allow openvpn to + use ipv4/6 if DNS returns A/AAAA without specifying protocol. + Hard: requires deep changes in initialization/calling logic + + o use AI_PASSIVE + + o the getaddr()/getaddr6() interface is not prepared for handling socktype + "tagging", currently I abuse the sockflags bits for getting the ai_socktype + downstream. + + o implement comparison for mapped addesses: server in dual stack + listening IPv6 must permit incoming streams from allowed IPv4 peer, + currently you need to pass eg: --remote ffff::1.2.3.4 + + o do something with multi mode learn routes, for now just ignoring + ipv6 addresses seems the most sensible thing to do, because there's + no support for intra-tunnel ipv6 stuff. + +* win32: + o find out about mapped addresses, as I can't make it work + with bound at ::1 and connect to 127.0.0.1 + +* N/A: + o this is ipv6 *endpoint* support, so don't expect "ifconfig6"-like + support in this patch diff -rauN openvpn-2.2.0/acinclude.m4 openvpn-2.2.0-debian/acinclude.m4 --- openvpn-2.2.0/acinclude.m4 2011-04-06 18:05:52.000000000 +0200 +++ openvpn-2.2.0-debian/acinclude.m4 2011-05-21 19:07:50.000000000 +0200 @@ -123,5 +123,9 @@ AC_DEFINE_UNQUOTED(socklen_t, $curl_cv_socklen_t_equiv, [type to use in place of socklen_t if not defined])], [#include -#include ]) +#ifdef WIN32 +#include +#else +#include +#endif]) ]) diff -rauN openvpn-2.2.0/aclocal.m4 openvpn-2.2.0-debian/aclocal.m4 --- openvpn-2.2.0/aclocal.m4 2011-04-21 21:29:33.000000000 +0200 +++ openvpn-2.2.0-debian/aclocal.m4 2011-05-21 19:07:50.000000000 +0200 @@ -13,8 +13,8 @@ m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl -m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.66],, -[m4_warning([this file was generated for autoconf 2.66. +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.65],, +[m4_warning([this file was generated for autoconf 2.65. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically `autoreconf'.])]) diff -rauN openvpn-2.2.0/buffer.c openvpn-2.2.0-debian/buffer.c --- openvpn-2.2.0/buffer.c 2011-04-21 21:13:34.000000000 +0200 +++ openvpn-2.2.0-debian/buffer.c 2011-05-21 19:07:50.000000000 +0200 @@ -214,6 +214,23 @@ return ret; } +bool +buf_puts(struct buffer *buf, const char *str) +{ + int ret = false; + uint8_t *ptr = BEND (buf); + int cap = buf_forward_capacity (buf); + if (cap > 0) + { + strncpynt ((char *)ptr,str, cap); + *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */ + buf->len += (int) strlen ((char *)ptr); + ret = true; + } + return ret; +} + + /* * This is necessary due to certain buggy implementations of snprintf, * that don't guarantee null termination for size > 0. diff -rauN openvpn-2.2.0/buffer.h openvpn-2.2.0-debian/buffer.h --- openvpn-2.2.0/buffer.h 2011-04-06 18:05:52.000000000 +0200 +++ openvpn-2.2.0-debian/buffer.h 2011-05-21 19:07:50.000000000 +0200 @@ -277,6 +277,11 @@ ; /* + * puts append to a buffer with overflow check + */ +bool buf_puts (struct buffer *buf, const char *str); + +/* * Like snprintf but guarantees null termination for size > 0 */ int openvpn_snprintf(char *str, size_t size, const char *format, ...) diff -rauN openvpn-2.2.0/config.h.in openvpn-2.2.0-debian/config.h.in --- openvpn-2.2.0/config.h.in 2011-04-21 21:29:34.000000000 +0200 +++ openvpn-2.2.0-debian/config.h.in 2011-05-21 19:07:50.000000000 +0200 @@ -531,6 +531,9 @@ /* Use LZO compression library */ #undef USE_LZO +/* struct sockaddr_in6 is needed for IPv6 peer support */ +#undef USE_PF_INET6 + /* Enable PKCS11 capability */ #undef USE_PKCS11 diff -rauN openvpn-2.2.0/configure openvpn-2.2.0-debian/configure --- openvpn-2.2.0/configure 2011-04-21 21:29:34.000000000 +0200 +++ openvpn-2.2.0-debian/configure 2011-05-21 19:07:50.000000000 +0200 @@ -1,13 +1,13 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.66 for OpenVPN 2.2.0. +# Generated by GNU Autoconf 2.65 for OpenVPN 2.2.0. # # Report bugs to . # # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software -# Foundation, Inc. +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. # # # This configure script is free software; the Free Software Foundation @@ -319,7 +319,7 @@ test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" } # as_fn_mkdir_p @@ -359,19 +359,19 @@ fi # as_fn_arith -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- +# as_fn_error ERROR [LINENO LOG_FD] +# --------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. +# script with status $?, using 1 if that was 0. as_fn_error () { - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + as_status=$?; test $as_status -eq 0 && as_status=1 + if test "$3"; then + as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 fi - $as_echo "$as_me: error: $2" >&2 + $as_echo "$as_me: error: $1" >&2 as_fn_exit $as_status } # as_fn_error @@ -533,7 +533,7 @@ exec 6>&1 # Name of the host. -# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` @@ -715,6 +715,7 @@ enable_http enable_fragment enable_multihome +enable_ipv6 enable_port_share enable_debug enable_small @@ -858,7 +859,7 @@ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" + as_fn_error "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -884,7 +885,7 @@ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" + as_fn_error "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1088,7 +1089,7 @@ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" + as_fn_error "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1104,7 +1105,7 @@ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" + as_fn_error "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1134,8 +1135,8 @@ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; - -*) as_fn_error $? "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information" + -*) as_fn_error "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information." ;; *=*) @@ -1143,7 +1144,7 @@ # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + as_fn_error "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; @@ -1161,13 +1162,13 @@ if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` - as_fn_error $? "missing argument to $ac_option" + as_fn_error "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; - fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + fatal) as_fn_error "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi @@ -1190,7 +1191,7 @@ [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac - as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" + as_fn_error "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' @@ -1204,8 +1205,8 @@ if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe - $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. - If a cross compiler is detected then cross compile mode will be used" >&2 + $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi @@ -1220,9 +1221,9 @@ ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - as_fn_error $? "working directory cannot be determined" + as_fn_error "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - as_fn_error $? "pwd does not report name of working directory" + as_fn_error "pwd does not report name of working directory" # Find the source files, if location was not specified. @@ -1261,11 +1262,11 @@ fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" + as_fn_error "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then @@ -1305,7 +1306,7 @@ --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking ...' messages + -q, --quiet, --silent do not print \`checking...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files @@ -1383,6 +1384,7 @@ --disable-http Disable HTTP proxy support --disable-fragment Disable internal fragmentation support (--fragment) --disable-multihome Disable multi-homed UDP server support (--multihome) + --disable-ipv6 Disable UDP/IPv6 support --disable-port-share Disable TCP server port-share support (--port-share) --disable-debug Disable debugging support (disable gremlin and verb 7+ messages) --enable-small Enable smaller executable size (disable OCC, usage message, and verb 4 parm list) @@ -1492,9 +1494,9 @@ if $ac_init_version; then cat <<\_ACEOF OpenVPN configure 2.2.0 -generated by GNU Autoconf 2.66 +generated by GNU Autoconf 2.65 -Copyright (C) 2010 Free Software Foundation, Inc. +Copyright (C) 2009 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF @@ -1588,10 +1590,10 @@ ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if eval "test \"\${$3+set}\"" = set; then : + if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 @@ -1650,15 +1652,17 @@ $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} -( $as_echo "## -------------------------------------------------- ## +( cat <<\_ASBOX +## -------------------------------------------------- ## ## Report this to openvpn-users@lists.sourceforge.net ## -## -------------------------------------------------- ##" +## -------------------------------------------------- ## +_ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" @@ -1722,7 +1726,7 @@ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -1753,7 +1757,7 @@ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else eval "$3=no" @@ -2030,7 +2034,7 @@ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -2093,7 +2097,7 @@ running configure, to aid debugging if configure makes a mistake. It was created by OpenVPN $as_me 2.2.0, which was -generated by GNU Autoconf 2.66. Invocation command line was +generated by GNU Autoconf 2.65. Invocation command line was $ $0 $@ @@ -2203,9 +2207,11 @@ { echo - $as_echo "## ---------------- ## + cat <<\_ASBOX +## ---------------- ## ## Cache variables. ## -## ---------------- ##" +## ---------------- ## +_ASBOX echo # The following way of writing the cache mishandles newlines in values, ( @@ -2239,9 +2245,11 @@ ) echo - $as_echo "## ----------------- ## + cat <<\_ASBOX +## ----------------- ## ## Output variables. ## -## ----------------- ##" +## ----------------- ## +_ASBOX echo for ac_var in $ac_subst_vars do @@ -2254,9 +2262,11 @@ echo if test -n "$ac_subst_files"; then - $as_echo "## ------------------- ## + cat <<\_ASBOX +## ------------------- ## ## File substitutions. ## -## ------------------- ##" +## ------------------- ## +_ASBOX echo for ac_var in $ac_subst_files do @@ -2270,9 +2280,11 @@ fi if test -s confdefs.h; then - $as_echo "## ----------- ## + cat <<\_ASBOX +## ----------- ## ## confdefs.h. ## -## ----------- ##" +## ----------- ## +_ASBOX echo cat confdefs.h echo @@ -2327,12 +2339,7 @@ ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then - # We do not want a PATH search for config.site. - case $CONFIG_SITE in #(( - -*) ac_site_file1=./$CONFIG_SITE;; - */*) ac_site_file1=$CONFIG_SITE;; - *) ac_site_file1=./$CONFIG_SITE;; - esac + ac_site_file1=$CONFIG_SITE elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site @@ -2347,11 +2354,7 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" \ - || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5; } + . "$ac_site_file" fi done @@ -2427,7 +2430,7 @@ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 + as_fn_error "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## @@ -2446,22 +2449,16 @@ ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do - if test -f "$ac_dir/install-sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f "$ac_dir/install.sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f "$ac_dir/shtool"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi + for ac_t in install-sh install.sh shtool; do + if test -f "$ac_dir/$ac_t"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/$ac_t -c" + break 2 + fi + done done if test -z "$ac_aux_dir"; then - as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 + as_fn_error "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 fi # These three variables are undocumented and unsupported, @@ -2475,7 +2472,7 @@ # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || - as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + as_fn_error "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } @@ -2486,16 +2483,16 @@ test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && - as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 + as_fn_error "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + as_fn_error "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; -*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +*) as_fn_error "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' @@ -2520,7 +2517,7 @@ ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 + as_fn_error "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi @@ -2528,7 +2525,7 @@ $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; -*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +*) as_fn_error "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' @@ -2650,11 +2647,11 @@ ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) - as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; + as_fn_error "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) - as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;; + as_fn_error "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;; esac # Do `set' in a subshell so we don't clobber the current shell's @@ -2676,7 +2673,7 @@ # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". - as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + as_fn_error "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi @@ -2686,7 +2683,7 @@ # Ok. : else - as_fn_error $? "newly created file is older than distributed files! + as_fn_error "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 @@ -2924,7 +2921,7 @@ $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` -if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\"" = set; then : +if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF @@ -2932,7 +2929,7 @@ all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF -# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; @@ -2966,7 +2963,7 @@ am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then - as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + as_fn_error "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi @@ -3184,6 +3181,15 @@ fi +# Check whether --enable-ipv6 was given. +if test "${enable_ipv6+set}" = set; then : + enableval=$enable_ipv6; PF_INET6="$enableval" +else + PF_INET6="yes" + +fi + + # Check whether --enable-port-share was given. if test "${enable_port_share+set}" = set; then : enableval=$enable_port_share; PORT_SHARE="$enableval" @@ -3954,8 +3960,8 @@ test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } +as_fn_error "no acceptable C compiler found in \$PATH +See \`config.log' for more details." "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 @@ -4069,8 +4075,9 @@ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "C compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5; } +{ as_fn_set_status 77 +as_fn_error "C compiler cannot create executables +See \`config.log' for more details." "$LINENO" 5; }; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } @@ -4112,8 +4119,8 @@ else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5; } +as_fn_error "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 @@ -4170,9 +4177,9 @@ else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run C compiled programs. +as_fn_error "cannot run C compiled programs. If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5; } +See \`config.log' for more details." "$LINENO" 5; } fi fi fi @@ -4223,8 +4230,8 @@ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5; } +as_fn_error "cannot compute suffix of object files: cannot compile +See \`config.log' for more details." "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi @@ -4762,8 +4769,8 @@ else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } +as_fn_error "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." "$LINENO" 5; } fi ac_ext=c @@ -4824,7 +4831,7 @@ done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then - as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + as_fn_error "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP @@ -4890,7 +4897,7 @@ done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + as_fn_error "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP @@ -5064,7 +5071,8 @@ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF @@ -5181,7 +5189,7 @@ test -n "$MAN2HTML" && break done - test -z "${MAN2HTML}" && as_fn_error $? "man2html is required for win32" "$LINENO" 5 + test -z "${MAN2HTML}" && as_fn_error "man2html is required for win32" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 @@ -5518,7 +5526,11 @@ ac_fn_c_check_type "$LINENO" "socklen_t" "ac_cv_type_socklen_t" "#include +#ifdef WIN32 +#include +#else #include +#endif " if test "x$ac_cv_type_socklen_t" = x""yes; then : @@ -5570,7 +5582,7 @@ esac if test "x$curl_cv_socklen_t_equiv" = x; then - as_fn_error $? "Cannot find a type to use in place of socklen_t" "$LINENO" 5 + as_fn_error "Cannot find a type to use in place of socklen_t" "$LINENO" 5 fi fi @@ -5711,7 +5723,7 @@ else - as_fn_error $? "C compiler is unable to creaty empty arrays" "$LINENO" 5 + as_fn_error "C compiler is unable to creaty empty arrays" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext @@ -5725,7 +5737,8 @@ do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF @@ -5781,7 +5794,8 @@ do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF @@ -6077,8 +6091,9 @@ if test "$ac_cv_type_unsigned_int" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (unsigned int) -See \`config.log' for more details" "$LINENO" 5; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (unsigned int) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_unsigned_int=0 fi @@ -6110,8 +6125,9 @@ if test "$ac_cv_type_unsigned_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (unsigned long) -See \`config.log' for more details" "$LINENO" 5; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (unsigned long) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_unsigned_long=0 fi @@ -6208,13 +6224,14 @@ do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF else - as_fn_error $? "Required library function not found" "$LINENO" 5 + as_fn_error "Required library function not found" "$LINENO" 5 fi done @@ -6222,7 +6239,8 @@ do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -6513,7 +6531,8 @@ do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -6727,13 +6746,14 @@ do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF else - as_fn_error $? "Required library function not found" "$LINENO" 5 + as_fn_error "Required library function not found" "$LINENO" 5 fi done @@ -6741,7 +6761,8 @@ do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -6861,6 +6882,19 @@ LDFLAGS="$OLDLDFLAGS" +if test "$PF_INET6" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct sockaddr_in6 for IPv6 support..." >&5 +$as_echo "$as_me: checking for struct sockaddr_in6 for IPv6 support..." >&6;} + ac_fn_c_check_type "$LINENO" "struct sockaddr_in6" "ac_cv_type_struct_sockaddr_in6" "#include \"syshead.h\" +" +if test "x$ac_cv_type_struct_sockaddr_in6" = x""yes; then : + +$as_echo "#define USE_PF_INET6 1" >>confdefs.h + +fi + +fi + if test "$MEMCHECK" = "valgrind"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for valgrind tool and Header files..." >&5 @@ -6874,7 +6908,7 @@ CFLAGS="-g -fno-inline" else - as_fn_error $? "valgrind headers not found." "$LINENO" 5 + as_fn_error "valgrind headers not found." "$LINENO" 5 fi @@ -6933,12 +6967,12 @@ else - as_fn_error $? "dmalloc library not found." "$LINENO" 5 + as_fn_error "dmalloc library not found." "$LINENO" 5 fi else - as_fn_error $? "dmalloc headers not found." "$LINENO" 5 + as_fn_error "dmalloc headers not found." "$LINENO" 5 fi @@ -7093,7 +7127,7 @@ as_ac_Lib=`$as_echo "ac_cv_lib_$i''_lzo1x_1_15_compress" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lzo1x_1_15_compress in -l$i" >&5 $as_echo_n "checking for lzo1x_1_15_compress in -l$i... " >&6; } -if eval "test \"\${$as_ac_Lib+set}\"" = set; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -7128,7 +7162,8 @@ eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : LIBS="-l$i $LIBS" @@ -7148,14 +7183,14 @@ done if test $havelzolib = 0 ; then - as_fn_error $? "LZO headers were found but LZO library was not found" "$LINENO" 5 + as_fn_error "LZO headers were found but LZO library was not found" "$LINENO" 5 fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: LZO headers were not found" >&5 $as_echo "LZO headers were not found" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: LZO library available from http://www.oberhumer.com/opensource/lzo/" >&5 $as_echo "LZO library available from http://www.oberhumer.com/opensource/lzo/" >&6; } - as_fn_error $? "Or try ./configure --disable-lzo" "$LINENO" 5 + as_fn_error "Or try ./configure --disable-lzo" "$LINENO" 5 fi fi @@ -7167,7 +7202,7 @@ if test "x$ac_cv_header_openssl_evp_h" = x""yes; then : else - as_fn_error $? "OpenSSL Crypto headers not found." "$LINENO" 5 + as_fn_error "OpenSSL Crypto headers not found." "$LINENO" 5 fi @@ -7176,7 +7211,7 @@ as_ac_Lib=`$as_echo "ac_cv_lib_$lib''_EVP_CIPHER_CTX_init" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for EVP_CIPHER_CTX_init in -l$lib" >&5 $as_echo_n "checking for EVP_CIPHER_CTX_init in -l$lib... " >&6; } -if eval "test \"\${$as_ac_Lib+set}\"" = set; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -7211,7 +7246,8 @@ eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : cryptofound=1 @@ -7223,7 +7259,7 @@ done - test -n "$cryptofound" || as_fn_error $? "OpenSSL Crypto library not found." "$LINENO" 5 + test -n "$cryptofound" || as_fn_error "OpenSSL Crypto library not found." "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking that OpenSSL Library is at least version 0.9.6" >&5 $as_echo_n "checking that OpenSSL Library is at least version 0.9.6... " >&6; } @@ -7303,7 +7339,7 @@ else - as_fn_error $? "OpenSSL crypto Library is too old." "$LINENO" 5 + as_fn_error "OpenSSL crypto Library is too old." "$LINENO" 5 fi rm -f conftest* @@ -7317,7 +7353,7 @@ if test "x$ac_cv_header_openssl_ssl_h" = x""yes; then : else - as_fn_error $? "OpenSSL SSL headers not found." "$LINENO" 5 + as_fn_error "OpenSSL SSL headers not found." "$LINENO" 5 fi @@ -7327,7 +7363,7 @@ as_ac_Lib=`$as_echo "ac_cv_lib_$lib''_SSL_CTX_new" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_CTX_new in -l$lib" >&5 $as_echo_n "checking for SSL_CTX_new in -l$lib... " >&6; } -if eval "test \"\${$as_ac_Lib+set}\"" = set; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -7362,7 +7398,8 @@ eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : sslfound=1 @@ -7374,7 +7411,7 @@ done - test -n "${sslfound}" || as_fn_error $? "OpenSSL SSL library not found." "$LINENO" 5 + test -n "${sslfound}" || as_fn_error "OpenSSL SSL library not found." "$LINENO" 5 if test "$MEMCHECK" = "ssl"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Memory Debugging Capabilities in OpenSSL Library..." >&5 @@ -7424,7 +7461,7 @@ $as_echo "NOTE: OpenSSL library must be compiled with CRYPTO_MDEBUG" >&6; } else - as_fn_error $? "Memory Debugging function in OpenSSL library not found." "$LINENO" 5 + as_fn_error "Memory Debugging function in OpenSSL library not found." "$LINENO" 5 fi @@ -7799,7 +7836,6 @@ ac_libobjs= ac_ltlibobjs= -U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' @@ -7823,15 +7859,15 @@ fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then - as_fn_error $? "conditional \"AMDEP\" was never defined. + as_fn_error "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then - as_fn_error $? "conditional \"am__fastdepCC\" was never defined. + as_fn_error "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WIN32_TRUE}" && test -z "${WIN32_FALSE}"; then - as_fn_error $? "conditional \"WIN32\" was never defined. + as_fn_error "conditional \"WIN32\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi @@ -7981,19 +8017,19 @@ (unset CDPATH) >/dev/null 2>&1 && unset CDPATH -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- +# as_fn_error ERROR [LINENO LOG_FD] +# --------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. +# script with status $?, using 1 if that was 0. as_fn_error () { - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + as_status=$?; test $as_status -eq 0 && as_status=1 + if test "$3"; then + as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 fi - $as_echo "$as_me: error: $2" >&2 + $as_echo "$as_me: error: $1" >&2 as_fn_exit $as_status } # as_fn_error @@ -8189,7 +8225,7 @@ test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" } # as_fn_mkdir_p @@ -8243,7 +8279,7 @@ # values after options handling. ac_log=" This file was extended by OpenVPN $as_me 2.2.0, which was -generated by GNU Autoconf 2.66. Invocation command line was +generated by GNU Autoconf 2.65. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -8309,10 +8345,10 @@ ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ OpenVPN config.status 2.2.0 -configured by $0, generated by GNU Autoconf 2.66, +configured by $0, generated by GNU Autoconf 2.65, with options \\"\$ac_cs_config\\" -Copyright (C) 2010 Free Software Foundation, Inc. +Copyright (C) 2009 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -8368,7 +8404,7 @@ ac_need_defaults=false;; --he | --h) # Conflict between --help and --header - as_fn_error $? "ambiguous option: \`$1' + as_fn_error "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; @@ -8377,7 +8413,7 @@ ac_cs_silent=: ;; # This is an error. - -*) as_fn_error $? "unrecognized option: \`$1' + -*) as_fn_error "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" @@ -8441,7 +8477,7 @@ "install-win32/Makefile") CONFIG_FILES="$CONFIG_FILES install-win32/Makefile" ;; "install-win32/settings") CONFIG_FILES="$CONFIG_FILES install-win32/settings" ;; - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done @@ -8479,7 +8515,7 @@ { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") -} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +} || as_fn_error "cannot create a temporary directory in ." "$LINENO" 5 # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. @@ -8496,7 +8532,7 @@ fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then - ac_cs_awk_cr='\\r' + ac_cs_awk_cr='\r' else ac_cs_awk_cr=$ac_cr fi @@ -8510,18 +8546,18 @@ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 -ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi @@ -8610,28 +8646,20 @@ else cat fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ - || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 + || as_fn_error "could not setup config files machinery" "$LINENO" 5 _ACEOF -# VPATH may cause trouble with some makes, so we remove sole $(srcdir), -# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ -h -s/// -s/^/:/ -s/[ ]*$/:/ -s/:\$(srcdir):/:/g -s/:\${srcdir}:/:/g -s/:@srcdir@:/:/g -s/^:*// + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ s/:*$// -x -s/\(=[ ]*\).*/\1/ -G -s/\n// s/^[^=]*=[ ]*$// }' fi @@ -8659,7 +8687,7 @@ if test -z "$ac_t"; then break elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + as_fn_error "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi @@ -8744,7 +8772,7 @@ _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 + as_fn_error "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" @@ -8757,7 +8785,7 @@ esac case $ac_mode$ac_tag in :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :L* | :C*:*) as_fn_error "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac @@ -8785,7 +8813,7 @@ [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + as_fn_error "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" @@ -8812,7 +8840,7 @@ case $ac_tag in *:-:* | *:-) cat >"$tmp/stdin" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + || as_fn_error "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac @@ -8949,22 +8977,22 @@ $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + || as_fn_error "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&5 +which seems to be undefined. Please make sure it is defined." >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&2;} +which seems to be undefined. Please make sure it is defined." >&2;} rm -f "$tmp/stdin" case $ac_file in -) cat "$tmp/out" && rm -f "$tmp/out";; *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; esac \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + || as_fn_error "could not create $ac_file" "$LINENO" 5 ;; :H) # @@ -8975,19 +9003,19 @@ $as_echo "/* $configure_input */" \ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" } >"$tmp/config.h" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + || as_fn_error "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$tmp/config.h" "$ac_file" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + || as_fn_error "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ - || as_fn_error $? "could not create -" "$LINENO" 5 + || as_fn_error "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. _am_arg="$ac_file" @@ -9138,7 +9166,7 @@ ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || - as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + as_fn_error "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. @@ -9159,7 +9187,7 @@ exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. - $ac_cs_success || as_fn_exit 1 + $ac_cs_success || as_fn_exit $? fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 diff -rauN openvpn-2.2.0/configure.ac openvpn-2.2.0-debian/configure.ac --- openvpn-2.2.0/configure.ac 2011-04-14 23:30:08.000000000 +0200 +++ openvpn-2.2.0-debian/configure.ac 2011-05-21 19:07:50.000000000 +0200 @@ -146,6 +146,12 @@ [MULTIHOME="yes"] ) +AC_ARG_ENABLE(ipv6, + [ --disable-ipv6 Disable UDP/IPv6 support], + [PF_INET6="$enableval"], + [PF_INET6="yes"] +) + AC_ARG_ENABLE(port-share, [ --disable-port-share Disable TCP server port-share support (--port-share)], [PORT_SHARE="$enableval"], @@ -566,6 +572,16 @@ AC_CHECK_FUNC(epoll_create, AC_DEFINE(HAVE_EPOLL_CREATE, 1, [epoll_create function is defined])) LDFLAGS="$OLDLDFLAGS" +dnl ipv6 support +if test "$PF_INET6" = "yes"; then + AC_CHECKING([for struct sockaddr_in6 for IPv6 support]) + AC_CHECK_TYPE( + [struct sockaddr_in6], + [AC_DEFINE(USE_PF_INET6, 1, [struct sockaddr_in6 is needed for IPv6 peer support])], + [], + [#include "syshead.h"]) +fi + dnl dnl check for valgrind tool dnl diff -rauN openvpn-2.2.0/forward.c openvpn-2.2.0-debian/forward.c --- openvpn-2.2.0/forward.c 2011-04-21 21:13:34.000000000 +0200 +++ openvpn-2.2.0-debian/forward.c 2011-05-21 19:10:29.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 -rauN openvpn-2.2.0/helper.c openvpn-2.2.0-debian/helper.c --- openvpn-2.2.0/helper.c 2011-04-06 18:04:54.000000000 +0200 +++ openvpn-2.2.0-debian/helper.c 2011-05-21 19:10:29.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 -rauN openvpn-2.2.0/init.c openvpn-2.2.0-debian/init.c --- openvpn-2.2.0/init.c 2011-04-21 21:13:34.000000000 +0200 +++ openvpn-2.2.0-debian/init.c 2011-05-21 19:10:29.000000000 +0200 @@ -96,7 +96,7 @@ */ if (options->pull && options->ping_rec_timeout_action == PING_UNDEF - && options->ce.proto == PROTO_UDPv4) + && proto_is_dgram(options->ce.proto)) { options->ping_rec_timeout = PRE_PULL_INITIAL_PING_RESTART; options->ping_rec_timeout_action = PING_RESTART; @@ -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. */ @@ -1150,7 +1191,12 @@ const char *detail = "SUCCESS"; if (c->c1.tuntap) tun_local = c->c1.tuntap->local; - tun_remote = htonl (c->c1.link_socket_addr.actual.dest.sa.sin_addr.s_addr); + /* TODO(jjo): for ipv6 this will convert some 32bits in the ipv6 addr + * to a meaningless ipv4 address. + * In any case, is somewhat inconsistent to send local tunnel + * addr with remote _endpoint_ addr (?) + */ + tun_remote = htonl (c->c1.link_socket_addr.actual.dest.addr.in4.sin_addr.s_addr); if (flags & ISC_ERRORS) detail = "ERROR"; management_set_state (management, @@ -1172,12 +1218,13 @@ void do_route (const struct options *options, struct route_list *route_list, + struct route_ipv6_list *route_ipv6_list, const struct tuntap *tt, const struct plugin_list *plugins, struct env_set *es) { - if (!options->route_noexec && route_list) - add_routes (route_list, tt, ROUTE_OPTION_FLAGS (options), es); + if (!options->route_noexec && ( route_list || route_ipv6_list ) ) + add_routes (route_list, route_ipv6_list, tt, ROUTE_OPTION_FLAGS (options), es); if (plugin_defined (plugins, OPENVPN_PLUGIN_ROUTE_UP)) { @@ -1234,11 +1281,16 @@ c->options.topology, c->options.ifconfig_local, c->options.ifconfig_remote_netmask, + c->options.ifconfig_ipv6_local, + c->options.ifconfig_ipv6_remote, addr_host (&c->c1.link_socket_addr.local), addr_host (&c->c1.link_socket_addr.remote), !c->options.ifconfig_nowarn, c->c2.es); + /* flag tunnel for IPv6 config if --tun-ipv6 is set */ + c->c1.tuntap->ipv6 = c->options.tun_ipv6; + init_tun_post (c->c1.tuntap, &c->c2.frame, &c->options.tuntap_options); @@ -1270,6 +1322,8 @@ /* parse and resolve the route option list */ if (c->options.routes && c->c1.route_list && c->c2.link_socket) do_init_route_list (&c->options, c->c1.route_list, &c->c2.link_socket->info, false, c->c2.es); + if (c->options.routes_ipv6 && c->c1.route_ipv6_list ) + do_init_route_ipv6_list (&c->options, c->c1.route_ipv6_list, false, c->c2.es); /* do ifconfig */ if (!c->options.ifconfig_noexec @@ -1286,7 +1340,7 @@ /* open the tun device */ open_tun (c->options.dev, c->options.dev_type, c->options.dev_node, - c->options.tun_ipv6, c->c1.tuntap); + c->c1.tuntap); /* set the hardware address */ if (c->options.lladdr) @@ -1315,7 +1369,8 @@ /* possibly add routes */ if (!c->options.route_delay_defined) - do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->plugins, c->c2.es); + do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list, + c->c1.tuntap, c->plugins, c->c2.es); /* * Did tun/tap driver give us an MTU? @@ -1389,8 +1444,9 @@ #endif /* delete any routes we added */ - if (c->c1.route_list) - delete_routes (c->c1.route_list, c->c1.tuntap, ROUTE_OPTION_FLAGS (&c->options), c->c2.es); + if (c->c1.route_list || c->c1.route_ipv6_list ) + delete_routes (c->c1.route_list, c->c1.route_ipv6_list, + c->c1.tuntap, ROUTE_OPTION_FLAGS (&c->options), c->c2.es); /* actually close tun/tap device based on --down-pre flag */ if (!c->options.down_pre) @@ -1566,7 +1622,7 @@ #ifdef ENABLE_OCC if (found & OPT_P_EXPLICIT_NOTIFY) { - if (c->options.ce.proto != PROTO_UDPv4 && c->options.explicit_exit_notification) + if (!proto_is_udp(c->options.ce.proto) && c->options.explicit_exit_notification) { msg (D_PUSH, "OPTIONS IMPORT: --explicit-exit-notify can only be used with --proto udp"); c->options.explicit_exit_notification = 0; @@ -1661,13 +1717,22 @@ switch (c->options.ce.proto) { case PROTO_UDPv4: +#ifdef USE_PF_INET6 + case PROTO_UDPv6: +#endif if (proxy) sec = c->options.ce.connect_retry_seconds; break; case PROTO_TCPv4_SERVER: +#ifdef USE_PF_INET6 + case PROTO_TCPv6_SERVER: +#endif sec = 1; break; case PROTO_TCPv4_CLIENT: +#ifdef USE_PF_INET6 + case PROTO_TCPv6_CLIENT: +#endif sec = c->options.ce.connect_retry_seconds; break; } @@ -2807,7 +2872,7 @@ #ifdef WIN32 msg (M_INFO, "NOTE: --fast-io is disabled since we are running on Windows"); #else - if (c->options.ce.proto != PROTO_UDPv4) + if (!proto_is_udp(c->options.ce.proto)) msg (M_INFO, "NOTE: --fast-io is disabled since we are not using UDP"); else { @@ -3083,7 +3148,11 @@ /* link_socket_mode allows CM_CHILD_TCP instances to inherit acceptable fds from a top-level parent */ - if (c->options.ce.proto == PROTO_TCPv4_SERVER) + if (c->options.ce.proto == PROTO_TCPv4_SERVER +#ifdef USE_PF_INET6 + || c->options.ce.proto == PROTO_TCPv6_SERVER +#endif + ) { if (c->mode == CM_TOP) link_socket_mode = LS_MODE_TCP_LISTEN; @@ -3358,17 +3427,8 @@ { CLEAR (*dest); - switch (src->options.ce.proto) - { - case PROTO_UDPv4: - dest->mode = CM_CHILD_UDP; - break; - case PROTO_TCPv4_SERVER: - dest->mode = CM_CHILD_TCP; - break; - default: - ASSERT (0); - } + /* proto_is_dgram will ASSERT(0) if proto is invalid */ + dest->mode = proto_is_dgram(src->options.ce.proto)? CM_CHILD_UDP : CM_CHILD_TCP; dest->gc = gc_new (); @@ -3474,7 +3534,7 @@ dest->c2.es_owned = false; dest->c2.event_set = NULL; - if (src->options.ce.proto == PROTO_UDPv4) + if (proto_is_dgram(src->options.ce.proto)) do_event_set_init (dest, false); } diff -rauN openvpn-2.2.0/init.h openvpn-2.2.0-debian/init.h --- openvpn-2.2.0/init.h 2011-04-06 18:04:54.000000000 +0200 +++ openvpn-2.2.0-debian/init.h 2011-05-21 19:10:29.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 -rauN openvpn-2.2.0/manage.c openvpn-2.2.0-debian/manage.c --- openvpn-2.2.0/manage.c 2011-04-21 21:13:34.000000000 +0200 +++ openvpn-2.2.0-debian/manage.c 2011-05-21 19:07:50.000000000 +0200 @@ -1957,9 +1957,9 @@ /* * Initialize socket address */ - ms->local.sa.sin_family = AF_INET; - ms->local.sa.sin_addr.s_addr = 0; - ms->local.sa.sin_port = htons (port); + ms->local.addr.in4.sin_family = AF_INET; + ms->local.addr.in4.sin_addr.s_addr = 0; + ms->local.addr.in4.sin_port = htons (port); /* * Run management over tunnel, or @@ -1971,7 +1971,7 @@ } else { - ms->local.sa.sin_addr.s_addr = getaddr + ms->local.addr.in4.sin_addr.s_addr = getaddr (GETADDR_RESOLVE|GETADDR_WARN_ON_SIGNAL|GETADDR_FATAL, addr, 0, NULL, NULL); } } @@ -2427,7 +2427,7 @@ && man->connection.state == MS_INITIAL) { /* listen on our local TUN/TAP IP address */ - man->settings.local.sa.sin_addr.s_addr = htonl (tun_local_ip); + man->settings.local.addr.in4.sin_addr.s_addr = htonl (tun_local_ip); man_connection_init (man); } diff -rauN openvpn-2.2.0/misc.c openvpn-2.2.0-debian/misc.c --- openvpn-2.2.0/misc.c 2011-04-21 21:13:34.000000000 +0200 +++ openvpn-2.2.0-debian/misc.c 2011-05-21 19:10:29.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 -rauN openvpn-2.2.0/mroute.c openvpn-2.2.0-debian/mroute.c --- openvpn-2.2.0/mroute.c 2011-04-06 18:05:52.000000000 +0200 +++ openvpn-2.2.0-debian/mroute.c 2011-05-21 19:10:29.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 @@ -155,10 +176,29 @@ } break; case 6: - { - msg (M_WARN, "Need IPv6 code in mroute_extract_addr_from_packet"); - break; - } + if (BLEN (buf) >= (int) sizeof (struct openvpn_ipv6hdr)) + { + const struct openvpn_ipv6hdr *ipv6 = (const struct openvpn_ipv6hdr *) BPTR (buf); +#if 0 /* very basic debug */ + struct gc_arena gc = gc_new (); + msg( M_INFO, "IPv6 packet! src=%s, dst=%s", + print_in6_addr( ipv6->saddr, 0, &gc ), + print_in6_addr( ipv6->daddr, 0, &gc )); + gc_free (&gc); +#endif + + mroute_get_in6_addr (src, ipv6->saddr, 0); + mroute_get_in6_addr (dest, ipv6->daddr, 0); + + if (mroute_is_mcast_ipv6 (ipv6->daddr)) + ret |= MROUTE_EXTRACT_MCAST; + + ret |= MROUTE_EXTRACT_SUCCEEDED; + } + break; + default: + msg (M_WARN, "IP packet with unknown IP version=%d seen", + OPENVPN_IPH_GET_VER (*BPTR(buf))); } } return ret; @@ -226,25 +266,47 @@ const struct openvpn_sockaddr *osaddr, bool use_port) { - if (osaddr->sa.sin_family == AF_INET) + switch (osaddr->addr.sa.sa_family) + { + case AF_INET: { if (use_port) { addr->type = MR_ADDR_IPV4 | MR_WITH_PORT; addr->netbits = 0; addr->len = 6; - memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4); - memcpy (addr->addr + 4, &osaddr->sa.sin_port, 2); + memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4); + memcpy (addr->addr + 4, &osaddr->addr.in4.sin_port, 2); } else { addr->type = MR_ADDR_IPV4; addr->netbits = 0; addr->len = 4; - memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4); + memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4); } return true; } +#ifdef USE_PF_INET6 + case AF_INET6: + if (use_port) + { + addr->type = MR_ADDR_IPV6 | MR_WITH_PORT; + addr->netbits = 0; + addr->len = 18; + memcpy (addr->addr, &osaddr->addr.in6.sin6_addr, 16); + memcpy (addr->addr + 16, &osaddr->addr.in6.sin6_port, 2); + } + else + { + addr->type = MR_ADDR_IPV6; + addr->netbits = 0; + addr->len = 16; + memcpy (addr->addr, &osaddr->addr.in6.sin6_addr, 16); + } + return true; +#endif + } return false; } @@ -252,14 +314,36 @@ * Zero off the host bits in an address, leaving * only the network bits, using the netbits member of * struct mroute_addr as the controlling parameter. + * + * TODO: this is called for route-lookup for every yet-unhashed + * destination address, so for lots of active net-iroutes, this + * might benefit from some "zeroize 32 bit at a time" improvements */ void mroute_addr_mask_host_bits (struct mroute_addr *ma) { in_addr_t addr = ntohl(*(in_addr_t*)ma->addr); - ASSERT ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4); - addr &= netbits_to_netmask (ma->netbits); - *(in_addr_t*)ma->addr = htonl (addr); + if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4) + { + addr &= netbits_to_netmask (ma->netbits); + *(in_addr_t*)ma->addr = htonl (addr); + } + else if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV6) + { + int byte = ma->len-1; /* rightmost byte in address */ + int bits_to_clear = 128 - ma->netbits; + + while( byte >= 0 && bits_to_clear > 0 ) + { + if ( bits_to_clear >= 8 ) + { ma->addr[byte--] = 0; bits_to_clear -= 8; } + else + { ma->addr[byte--] &= (~0 << bits_to_clear); bits_to_clear = 0; } + } + ASSERT( bits_to_clear == 0 ); + } + else + ASSERT(0); } /* @@ -337,17 +421,24 @@ } break; case MR_ADDR_IPV6: - buf_printf (&out, "IPV6"); - break; - default: - buf_printf (&out, "UNKNOWN"); - break; - } - return BSTR (&out); - } - else - return "[NULL]"; -} + { + buf_printf (&out, "%s", + print_in6_addr( *(struct in6_addr*)&maddr.addr, 0, gc)); + if (maddr.type & MR_WITH_NETBITS) + { + buf_printf (&out, "/%d", maddr.netbits); + } + } + break; + default: + buf_printf (&out, "UNKNOWN"); + break; + } + return BSTR (&out); + } + else + return "[NULL]"; + } /* * mroute_helper's main job is keeping track of @@ -418,6 +509,40 @@ } } +/* 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 -rauN openvpn-2.2.0/mroute.h openvpn-2.2.0-debian/mroute.h --- openvpn-2.2.0/mroute.h 2011-04-06 18:05:52.000000000 +0200 +++ openvpn-2.2.0-debian/mroute.h 2011-05-21 19:10:29.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 -rauN openvpn-2.2.0/mtcp.c openvpn-2.2.0-debian/mtcp.c --- openvpn-2.2.0/mtcp.c 2011-04-06 18:05:52.000000000 +0200 +++ openvpn-2.2.0-debian/mtcp.c 2011-05-21 19:07:50.000000000 +0200 @@ -150,6 +150,11 @@ ASSERT (mi->context.c2.link_socket); ASSERT (mi->context.c2.link_socket->info.lsa); ASSERT (mi->context.c2.link_socket->mode == LS_MODE_TCP_ACCEPT_FROM); + ASSERT (mi->context.c2.link_socket->info.lsa->actual.dest.addr.sa.sa_family == AF_INET +#ifdef USE_PF_INET6 + || mi->context.c2.link_socket->info.lsa->actual.dest.addr.sa.sa_family == AF_INET6 +#endif + ); if (!mroute_extract_openvpn_sockaddr (&mi->real, &mi->context.c2.link_socket->info.lsa->actual.dest, true)) { msg (D_MULTI_ERRORS, "MULTI TCP: TCP client address is undefined"); diff -rauN openvpn-2.2.0/multi.c openvpn-2.2.0-debian/multi.c --- openvpn-2.2.0/multi.c 2011-04-21 21:13:34.000000000 +0200 +++ openvpn-2.2.0-debian/multi.c 2011-05-21 19:10:29.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); } } @@ -1058,8 +1055,8 @@ struct mroute_addr addr; CLEAR (remote_si); - remote_si.sa.sin_family = AF_INET; - remote_si.sa.sin_addr.s_addr = htonl (a); + remote_si.addr.in4.sin_family = AF_INET; + remote_si.addr.in4.sin_addr.s_addr = htonl (a); ASSERT (mroute_extract_openvpn_sockaddr (&addr, &remote_si, false)); if (netbits >= 0) @@ -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); @@ -2496,9 +2605,9 @@ int count = 0; CLEAR (saddr); - saddr.sa.sin_family = AF_INET; - saddr.sa.sin_addr.s_addr = htonl (addr); - saddr.sa.sin_port = htons (port); + saddr.addr.in4.sin_family = AF_INET; + saddr.addr.in4.sin_addr.s_addr = htonl (addr); + saddr.addr.in4.sin_port = htons (port); if (mroute_extract_openvpn_sockaddr (&maddr, &saddr, true)) { hash_iterator_init (m->iter, &hi); @@ -2675,16 +2784,24 @@ { ASSERT (top->options.mode == MODE_SERVER); - switch (top->options.ce.proto) { - case PROTO_UDPv4: - tunnel_server_udp (top); - break; - case PROTO_TCPv4_SERVER: - tunnel_server_tcp (top); - break; - default: - ASSERT (0); - } +#ifdef USE_PF_INET6 + if (proto_is_dgram(top->options.ce.proto)) + tunnel_server_udp(top); + else + tunnel_server_tcp(top); +#else + switch (top->options.ce.proto) + { + case PROTO_UDPv4: + tunnel_server_udp (top); + break; + case PROTO_TCPv4_SERVER: + tunnel_server_tcp (top); + break; + default: + ASSERT (0); + } +#endif } #else diff -rauN openvpn-2.2.0/occ.c openvpn-2.2.0-debian/occ.c --- openvpn-2.2.0/occ.c 2011-04-06 18:04:54.000000000 +0200 +++ openvpn-2.2.0-debian/occ.c 2011-05-21 19:07:50.000000000 +0200 @@ -369,7 +369,7 @@ c->c2.max_send_size_remote, c->c2.max_recv_size_local); if (!c->options.fragment - && c->options.ce.proto == PROTO_UDPv4 + && (proto_is_dgram(c->options.ce.proto)) && c->c2.max_send_size_local > TUN_MTU_MIN && (c->c2.max_recv_size_remote < c->c2.max_send_size_local || c->c2.max_recv_size_local < c->c2.max_send_size_remote)) diff -rauN openvpn-2.2.0/openvpn.8 openvpn-2.2.0-debian/openvpn.8 --- openvpn-2.2.0/openvpn.8 2011-04-21 21:13:51.000000000 +0200 +++ openvpn-2.2.0-debian/openvpn.8 2011-05-21 19:10:29.000000000 +0200 @@ -789,6 +789,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 @@ -4936,6 +4938,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. @@ -5450,13 +5503,16 @@ script execution. .\"********************************************************* .TP -.B trusted_ip +.B trusted_ip (or trusted_ip6) Actual IP address of connecting client or peer which has been authenticated. Set prior to execution of .B \-\-ipchange, \-\-client-connect, and .B \-\-client-disconnect scripts. +If using ipv6 endpoints (udp6, tcp6), +.B trusted_ip6 +will be set instead. .\"********************************************************* .TP .B trusted_port @@ -5468,7 +5524,7 @@ scripts. .\"********************************************************* .TP -.B untrusted_ip +.B untrusted_ip (or untrusted_ip6) Actual IP address of connecting client or peer which has not been authenticated yet. Sometimes used to .B nmap @@ -5480,6 +5536,9 @@ and .B \-\-auth-user-pass-verify scripts. +If using ipv6 endpoints (udp6, tcp6), +.B untrusted_ip6 +will be set instead. .\"********************************************************* .TP .B untrusted_port diff -rauN openvpn-2.2.0/openvpn.h openvpn-2.2.0-debian/openvpn.h --- openvpn-2.2.0/openvpn.h 2011-04-06 18:05:52.000000000 +0200 +++ openvpn-2.2.0-debian/openvpn.h 2011-05-21 19:10:29.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 -rauN openvpn-2.2.0/options.c openvpn-2.2.0-debian/options.c --- openvpn-2.2.0/options.c 2011-04-21 21:13:34.000000000 +0200 +++ openvpn-2.2.0-debian/options.c 2011-05-21 19:10:29.000000000 +0200 @@ -79,6 +79,13 @@ #ifdef ENABLE_EUREPHIA " [eurephia]" #endif +#if ENABLE_IP_PKTINFO + " [MH]" +#endif +#ifdef USE_PF_INET6 + " [PF_INET6]" +#endif + " [IPv6 payload 20110424-2 (2.2RC2)]" " built on " __DATE__ ; @@ -101,6 +108,9 @@ "--proto p : Use protocol p for communicating with peer.\n" " p = udp (default), tcp-server, or tcp-client\n" "--proto-force p : only consider protocol p in list of connection profiles.\n" +#ifdef USE_PF_INET6 + " p = udp6, tcp6-server, or tcp6-client (ipv6)\n" +#endif "--connect-retry n : For --proto tcp-client, number of seconds to wait\n" " between connection retries (default=%d).\n" "--connect-timeout n : For --proto tcp-client, connection timeout (in seconds).\n" @@ -171,6 +181,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" @@ -181,6 +193,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" @@ -369,6 +385,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" @@ -382,10 +399,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" @@ -870,6 +893,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) { @@ -988,6 +1083,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)); @@ -1008,6 +1105,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); @@ -1021,6 +1121,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); @@ -1075,6 +1178,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 */ @@ -1112,6 +1234,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) @@ -1202,6 +1331,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); @@ -1706,11 +1838,27 @@ * Sanity check on TCP mode options */ - if (ce->connect_retry_defined && ce->proto != PROTO_TCPv4_CLIENT) - msg (M_USAGE, "--connect-retry doesn't make sense unless also used with --proto tcp-client"); - - if (ce->connect_timeout_defined && ce->proto != PROTO_TCPv4_CLIENT) - msg (M_USAGE, "--connect-timeout doesn't make sense unless also used with --proto tcp-client"); + if (ce->connect_retry_defined && ce->proto != PROTO_TCPv4_CLIENT +#ifdef USE_PF_INET6 + && ce->proto != PROTO_TCPv6_CLIENT +#endif + ) + msg (M_USAGE, "--connect-retry doesn't make sense unless also used with --proto tcp-client" +#ifdef USE_PF_INET6 + " or tcp6-client" +#endif + ); + + if (ce->connect_timeout_defined && ce->proto != PROTO_TCPv4_CLIENT +#ifdef USE_PF_INET6 + && ce->proto != PROTO_TCPv6_CLIENT +#endif + ) + msg (M_USAGE, "--connect-timeout doesn't make sense unless also used with --proto tcp-client" +#ifdef USE_PF_INET6 + " or tcp6-client" +#endif + ); /* * Sanity check on MTU parameters @@ -1719,7 +1867,7 @@ msg (M_USAGE, "only one of --tun-mtu or --link-mtu may be defined (note that --ifconfig implies --link-mtu %d)", LINK_MTU_DEFAULT); #ifdef ENABLE_OCC - if (ce->proto != PROTO_UDPv4 && options->mtu_test) + if (!proto_is_udp(ce->proto) && options->mtu_test) msg (M_USAGE, "--mtu-test only makes sense with --proto udp"); #endif @@ -1732,7 +1880,8 @@ * Sanity check on --local, --remote, and --ifconfig */ - if (string_defined_equal (ce->local, ce->remote) + if (proto_is_net(ce->proto) + && string_defined_equal (ce->local, ce->remote) && ce->local_port == ce->remote_port) msg (M_USAGE, "--remote and --local addresses are the same"); @@ -1797,16 +1946,20 @@ */ #ifdef ENABLE_FRAGMENT - if (ce->proto != PROTO_UDPv4 && options->fragment) + if (!proto_is_udp(ce->proto) && options->fragment) msg (M_USAGE, "--fragment can only be used with --proto udp"); #endif #ifdef ENABLE_OCC - if (ce->proto != PROTO_UDPv4 && options->explicit_exit_notification) + if (!proto_is_udp(ce->proto) && options->explicit_exit_notification) msg (M_USAGE, "--explicit-exit-notify can only be used with --proto udp"); #endif - if (!ce->remote && ce->proto == PROTO_TCPv4_CLIENT) + if (!ce->remote && (ce->proto == PROTO_TCPv4_CLIENT +#ifdef USE_PF_INET6 + || ce->proto == PROTO_TCPv6_CLIENT +#endif + )) msg (M_USAGE, "--remote MUST be used in TCP Client mode"); #ifdef ENABLE_HTTP_PROXY @@ -1824,7 +1977,12 @@ msg (M_USAGE, "--socks-proxy can not be used in TCP Server mode"); #endif - if (ce->proto == PROTO_TCPv4_SERVER && connection_list_defined (options)) + if ((ce->proto == PROTO_TCPv4_SERVER +#ifdef USE_PF_INET6 + || ce->proto == PROTO_TCPv6_SERVER +#endif + ) + && connection_list_defined (options)) msg (M_USAGE, "TCP server mode allows at most one --remote address"); #if P2MP_SERVER @@ -1838,11 +1996,28 @@ msg (M_USAGE, "--mode server only works with --dev tun or --dev tap"); if (options->pull) msg (M_USAGE, "--pull cannot be used with --mode server"); - if (!(ce->proto == PROTO_UDPv4 || ce->proto == PROTO_TCPv4_SERVER)) - msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server"); + if (!(proto_is_udp(ce->proto) || ce->proto == PROTO_TCPv4_SERVER +#ifdef USE_PF_INET6 + || ce->proto == PROTO_TCPv6_SERVER +#endif + )) + msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server" +#ifdef USE_PF_INET6 + " or proto tcp6-server" +#endif + ); #if PORT_SHARE - if ((options->port_share_host || options->port_share_port) && ce->proto != PROTO_TCPv4_SERVER) - msg (M_USAGE, "--port-share only works in TCP server mode (--proto tcp-server)"); + if ((options->port_share_host || options->port_share_port) && + (ce->proto != PROTO_TCPv4_SERVER +#ifdef USE_PF_INET6 + && ce->proto != PROTO_TCPv6_SERVER +#endif + )) + msg (M_USAGE, "--port-share only works in TCP server mode (--proto tcp-server" +#ifdef USE_PF_INET6 + " or tcp6-server" +#endif + ")"); #endif if (!options->tls_server) msg (M_USAGE, "--mode server requires --tls-server"); @@ -1862,17 +2037,27 @@ if (options->connection_list) msg (M_USAGE, " cannot be used with --mode server"); #endif +#if 0 if (options->tun_ipv6) msg (M_USAGE, "--tun-ipv6 cannot be used with --mode server"); +#endif if (options->shaper) msg (M_USAGE, "--shaper cannot be used with --mode server"); if (options->inetd) msg (M_USAGE, "--inetd cannot be used with --mode server"); if (options->ipchange) msg (M_USAGE, "--ipchange cannot be used with --mode server (use --client-connect instead)"); - if (!(ce->proto == PROTO_UDPv4 || ce->proto == PROTO_TCPv4_SERVER)) - msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server"); - if (ce->proto != PROTO_UDPv4 && (options->cf_max || options->cf_per)) + if (!(proto_is_dgram(ce->proto) || ce->proto == PROTO_TCPv4_SERVER +#ifdef USE_PF_INET6 + || ce->proto == PROTO_TCPv6_SERVER +#endif + )) + msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server" +#ifdef USE_PF_INET6 + " or --proto tcp6-server" +#endif + ); + if (!proto_is_udp(ce->proto) && (options->cf_max || options->cf_per)) msg (M_USAGE, "--connect-freq only works with --mode server --proto udp. Try --max-clients instead."); if (!(dev == DEV_TYPE_TAP || (dev == DEV_TYPE_TUN && options->topology == TOP_SUBNET)) && options->ifconfig_pool_netmask) msg (M_USAGE, "The third parameter to --ifconfig-pool (netmask) is only valid in --dev tap mode"); @@ -1888,6 +2073,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) @@ -1919,6 +2109,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"); @@ -1963,7 +2155,7 @@ /* * Check consistency of replay options */ - if ((ce->proto != PROTO_UDPv4) + if ((!proto_is_udp(ce->proto)) && (options->replay_window != defaults.replay_window || options->replay_time != defaults.replay_time)) msg (M_USAGE, "--replay-window only makes sense with --proto udp"); @@ -2136,6 +2328,10 @@ { if (ce->proto == PROTO_TCPv4) ce->proto = PROTO_TCPv4_CLIENT; +#ifdef USE_PF_INET6 + else if (ce->proto == PROTO_TCPv6) + ce->proto = PROTO_TCPv6_CLIENT; +#endif } #endif @@ -2460,6 +2656,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, @@ -3785,6 +3983,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); @@ -4585,6 +4807,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; @@ -4796,6 +5038,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; @@ -4880,6 +5149,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; @@ -5075,6 +5366,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; @@ -5113,6 +5409,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 -rauN openvpn-2.2.0/options.h openvpn-2.2.0-debian/options.h --- openvpn-2.2.0/options.h 2011-04-06 18:05:52.000000000 +0200 +++ openvpn-2.2.0-debian/options.h 2011-05-21 19:10:29.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 */ @@ -361,6 +365,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) @@ -382,6 +389,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; @@ -394,12 +406,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; @@ -722,6 +739,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 -rauN openvpn-2.2.0/pool.c openvpn-2.2.0-debian/pool.c --- openvpn-2.2.0/pool.c 2011-04-06 18:04:54.000000000 +0200 +++ openvpn-2.2.0-debian/pool.c 2011-05-21 19:10:29.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 -rauN openvpn-2.2.0/pool.h openvpn-2.2.0-debian/pool.h --- openvpn-2.2.0/pool.h 2011-04-06 18:05:52.000000000 +0200 +++ openvpn-2.2.0-debian/pool.h 2011-05-21 19:10:29.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 -rauN openvpn-2.2.0/proto.h openvpn-2.2.0-debian/proto.h --- openvpn-2.2.0/proto.h 2011-04-06 18:05:52.000000000 +0200 +++ openvpn-2.2.0-debian/proto.h 2011-05-21 19:10:29.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 -rauN openvpn-2.2.0/ps.c openvpn-2.2.0-debian/ps.c --- openvpn-2.2.0/ps.c 2011-04-14 17:01:43.000000000 +0200 +++ openvpn-2.2.0-debian/ps.c 2011-05-21 19:07:50.000000000 +0200 @@ -320,9 +320,9 @@ const int port) { CLEAR (*osaddr); - osaddr->sa.sin_family = AF_INET; - osaddr->sa.sin_addr.s_addr = htonl (addr); - osaddr->sa.sin_port = htons (port); + osaddr->addr.in4.sin_family = AF_INET; + osaddr->addr.in4.sin_addr.s_addr = htonl (addr); + osaddr->addr.in4.sin_port = htons (port); } static inline void diff -rauN openvpn-2.2.0/push.c openvpn-2.2.0-debian/push.c --- openvpn-2.2.0/push.c 2011-04-06 18:05:52.000000000 +0200 +++ openvpn-2.2.0-debian/push.c 2011-05-21 19:10:29.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 -rauN openvpn-2.2.0/route.c openvpn-2.2.0-debian/route.c --- openvpn-2.2.0/route.c 2011-04-06 18:05:52.000000000 +0200 +++ openvpn-2.2.0-debian/route.c 2011-05-21 19:10:29.000000000 +0200 @@ -35,10 +35,12 @@ #include "socket.h" #include "manage.h" #include "win32.h" +#include "options.h" #include "memdbg.h" static void delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es); +static void delete_route_ipv6 (const struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es); static void get_bypass_addresses (struct route_bypass *rb, const unsigned int flags); #ifdef ENABLE_DEBUG @@ -68,6 +70,15 @@ return ret; } +struct route_ipv6_option_list * +new_route_ipv6_option_list (const int max_routes, struct gc_arena *a) +{ + struct route_ipv6_option_list *ret; + ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_option_list, struct route_ipv6_option, max_routes, a); + ret->capacity = max_routes; + return ret; +} + struct route_option_list * clone_route_option_list (const struct route_option_list *src, struct gc_arena *a) { @@ -95,6 +106,15 @@ return ret; } +struct route_ipv6_list * +new_route_ipv6_list (const int max_routes, struct gc_arena *a) +{ + struct route_ipv6_list *ret; + ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_list, struct route_ipv6, max_routes, a); + ret->capacity = max_routes; + return ret; +} + static const char * route_string (const struct route *r, struct gc_arena *gc) { @@ -311,6 +331,68 @@ return false; } +static bool +init_route_ipv6 (struct route_ipv6 *r6, + const struct route_ipv6_option *r6o, + const struct route_ipv6_list *rl6 ) +{ + r6->option = r6o; + r6->defined = false; + + if ( !get_ipv6_addr( r6o->prefix, &r6->network, &r6->netbits, NULL, M_WARN )) + goto fail; + + /* gateway */ + if (is_route_parm_defined (r6o->gateway)) + { + if ( inet_pton( AF_INET6, r6o->gateway, &r6->gateway ) != 1 ) + { + msg( M_WARN, PACKAGE_NAME "ROUTE6: cannot parse gateway spec '%s'", r6o->gateway ); + } + } + else if (rl6->remote_endpoint_defined) + { + r6->gateway = rl6->remote_endpoint_ipv6; + } + else + { + msg (M_WARN, PACKAGE_NAME " ROUTE6: " PACKAGE_NAME " needs a gateway parameter for a --route-ipv6 option and no default was specified by either --route-ipv6-gateway or --ifconfig-ipv6 options"); + goto fail; + } + + /* metric */ + + r6->metric_defined = false; + r6->metric = 0; + if (is_route_parm_defined (r6o->metric)) + { + r6->metric = atoi (r6o->metric); + if (r6->metric < 0) + { + msg (M_WARN, PACKAGE_NAME " ROUTE: route metric for network %s (%s) must be >= 0", + r6o->prefix, + r6o->metric); + goto fail; + } + r6->metric_defined = true; + } + else if (rl6->default_metric_defined) + { + r6->metric = rl6->default_metric; + r6->metric_defined = true; + } + + r6->defined = true; + + return true; + + fail: + msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve route for host/network: %s", + r6o->prefix); + r6->defined = false; + return false; +} + void add_route_to_option_list (struct route_option_list *l, const char *network, @@ -331,6 +413,23 @@ } void +add_route_ipv6_to_option_list (struct route_ipv6_option_list *l, + const char *prefix, + const char *gateway, + const char *metric) +{ + struct route_ipv6_option *ro; + if (l->n >= l->capacity) + msg (M_FATAL, PACKAGE_NAME " ROUTE: cannot add more than %d IPv6 routes -- please increase the max-routes option in the client configuration file", + l->capacity); + ro = &l->routes_ipv6[l->n]; + ro->prefix = prefix; + ro->gateway = gateway; + ro->metric = metric; + ++l->n; +} + +void clear_route_list (struct route_list *rl) { const int capacity = rl->capacity; @@ -340,6 +439,15 @@ } void +clear_route_ipv6_list (struct route_ipv6_list *rl6) +{ + const int capacity = rl6->capacity; + const size_t rl6_size = array_mult_safe (sizeof(struct route_ipv6), capacity, sizeof(struct route_ipv6_list)); + memset(rl6, 0, rl6_size); + rl6->capacity = capacity; +} + +void route_list_add_default_gateway (struct route_list *rl, struct env_set *es, const in_addr_t addr) @@ -432,6 +540,8 @@ CLEAR(netlist); /* init_route() will not always init this */ + CLEAR(netlist); /* init_route() will not always init this */ + if (!init_route (&r, &netlist, &opt->routes[i], @@ -469,6 +579,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, @@ -581,13 +757,23 @@ if (!local) { /* route remote host to original default gateway */ - add_route3 (rl->spec.remote_host, - ~0, - rl->spec.net_gateway, - tt, - flags, - es); - rl->did_local = true; +#ifdef USE_PF_INET6 + /* if remote_host is not ipv4 (ie: ipv6), just skip + * adding this special /32 route */ + if (rl->spec.remote_host != IPV4_INVALID_ADDR) { +#endif + add_route3 (rl->spec.remote_host, + ~0, + rl->spec.net_gateway, + tt, + flags, + es); + rl->did_local = true; +#ifdef USE_PF_INET6 + } else { + dmsg (D_ROUTE, "ROUTE remote_host protocol differs from tunneled"); + } +#endif } /* route DHCP/DNS server traffic through original default gateway */ @@ -704,10 +890,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 +921,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 +951,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 +1055,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 +1276,176 @@ gc_free (&gc); } +void +add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es) +{ + struct gc_arena gc; + struct argv argv; + + const char *network; + const char *gateway; + bool status = false; + const char *device = tt->actual_name; + int byte, bits_to_clear; + struct in6_addr network_copy = r6->network; + + if (!r6->defined) + return; + + gc_init (&gc); + argv_init (&argv); + + /* clear host bit parts of route + * (needed if routes are specified improperly, or if we need to + * explicitely setup the "connected" network routes on some OSes) + */ + byte = 15; + bits_to_clear = 128 - r6->netbits; + + while( byte >= 0 && bits_to_clear > 0 ) + { + if ( bits_to_clear >= 8 ) + { network_copy.s6_addr[byte--] = 0; bits_to_clear -= 8; } + else + { network_copy.s6_addr[byte--] &= (~0 << bits_to_clear); bits_to_clear = 0; } + } + + network = print_in6_addr( network_copy, 0, &gc); + gateway = print_in6_addr( r6->gateway, 0, &gc); + + if ( !tt->ipv6 ) + { + msg( M_INFO, "add_route_ipv6(): not adding %s/%d, no IPv6 on if %s", + network, r6->netbits, device ); + return; + } + + msg( M_INFO, "add_route_ipv6(%s/%d -> %s metric %d) dev %s", + network, r6->netbits, gateway, r6->metric, device ); + + /* + * Filter out routes which are essentially no-ops + * (not currently done for IPv6) + */ + +#if defined(TARGET_LINUX) +#ifdef CONFIG_FEATURE_IPROUTE + argv_printf (&argv, "%s -6 route add %s/%d dev %s", + iproute_path, + network, + r6->netbits, + device); + if (r6->metric_defined) + argv_printf_cat (&argv, " metric %d", r6->metric); + +#else + argv_printf (&argv, "%s -A inet6 add %s/%d dev %s", + ROUTE_PATH, + network, + r6->netbits, + device); + if (r6->metric_defined) + argv_printf_cat (&argv, " metric %d", r6->metric); +#endif /*CONFIG_FEATURE_IPROUTE*/ + argv_msg (D_ROUTE, &argv); + status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 add command failed"); + +#elif defined (WIN32) + + /* netsh interface ipv6 add route 2001:db8::/32 MyTunDevice */ + argv_printf (&argv, "%s%sc interface ipv6 add route %s/%d %s", + get_win_sys_path(), + NETSH_PATH_SUFFIX, + network, + r6->netbits, + device); + + /* next-hop depends on TUN or TAP mode: + * - in TAP mode, we use the "real" next-hop + * - in TUN mode we use a special-case link-local address that the tapdrvr + * knows about and will answer ND (neighbor discovery) packets for + */ + if ( tt->type == DEV_TYPE_TUN ) + argv_printf_cat( &argv, " %s", "fe80::8" ); + else + argv_printf_cat( &argv, " %s", gateway ); + +#if 0 + if (r->metric_defined) + argv_printf_cat (&argv, " METRIC %d", r->metric); +#endif + + argv_msg (D_ROUTE, &argv); + + netcmd_semaphore_lock (); + status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add ipv6 command failed"); + netcmd_semaphore_release (); + +#elif defined (TARGET_SOLARIS) + + /* example: route add -inet6 2001:db8::/32 somegateway 0 */ + + /* for some weird reason, this does not work for me unless I set + * "metric 0" - otherwise, the routes will be nicely installed, but + * packets will just disappear somewhere. So we use "0" now... + */ + + argv_printf (&argv, "%s add -inet6 %s/%d %s 0", + ROUTE_PATH, + network, + r6->netbits, + gateway ); + + argv_msg (D_ROUTE, &argv); + status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add -inet6 command failed"); + +#elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) + + argv_printf (&argv, "%s add -inet6 %s/%d -iface %s", + ROUTE_PATH, + network, + r6->netbits, + device ); + + argv_msg (D_ROUTE, &argv); + status = openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route add -inet6 command failed"); + +#elif defined(TARGET_DARWIN) + + argv_printf (&argv, "%s add -inet6 %s -prefixlen %d -iface %s", + ROUTE_PATH, + network, r6->netbits, device ); + + argv_msg (D_ROUTE, &argv); + status = openvpn_execve_check (&argv, es, 0, "ERROR: MacOS X route add -inet6 command failed"); + +#elif defined(TARGET_OPENBSD) + + argv_printf (&argv, "%s add -inet6 %s -prefixlen %d %s", + ROUTE_PATH, + network, r6->netbits, gateway ); + + argv_msg (D_ROUTE, &argv); + status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD route add -inet6 command failed"); + +#elif defined(TARGET_NETBSD) + + argv_printf (&argv, "%s add -inet6 %s/%d %s", + ROUTE_PATH, + network, r6->netbits, gateway ); + + argv_msg (D_ROUTE, &argv); + status = openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD route add -inet6 command failed"); + +#else + msg (M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system. Try putting your routes in a --route-up script"); +#endif + + r6->defined = status; + argv_reset (&argv); + gc_free (&gc); +} + static void delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es) { @@ -1164,6 +1585,142 @@ gc_free (&gc); } +static void +delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es) +{ + struct gc_arena gc; + struct argv argv; + const char *network; + const char *gateway; + const char *device = tt->actual_name; + + if (!r6->defined) + return; + + gc_init (&gc); + argv_init (&argv); + + network = print_in6_addr( r6->network, 0, &gc); + gateway = print_in6_addr( r6->gateway, 0, &gc); + + if ( !tt->ipv6 ) + { + msg( M_INFO, "delete_route_ipv6(): not deleting %s/%d, no IPv6 on if %s", + network, r6->netbits, device ); + return; + } + + msg( M_INFO, "delete_route_ipv6(%s/%d)", network, r6->netbits ); + +#if defined(TARGET_LINUX) +#ifdef CONFIG_FEATURE_IPROUTE + argv_printf (&argv, "%s -6 route del %s/%d dev %s", + iproute_path, + network, + r6->netbits, + device); +#else + argv_printf (&argv, "%s -A inet6 del %s/%d dev %s", + ROUTE_PATH, + network, + r6->netbits, + device); +#endif /*CONFIG_FEATURE_IPROUTE*/ + argv_msg (D_ROUTE, &argv); + openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 del command failed"); + +#elif defined (WIN32) + + /* netsh interface ipv6 delete route 2001:db8::/32 MyTunDevice */ + argv_printf (&argv, "%s%sc interface ipv6 delete route %s/%d %s", + get_win_sys_path(), + NETSH_PATH_SUFFIX, + network, + r6->netbits, + device); + + /* next-hop depends on TUN or TAP mode: + * - in TAP mode, we use the "real" next-hop + * - in TUN mode we use a special-case link-local address that the tapdrvr + * knows about and will answer ND (neighbor discovery) packets for + * (and "route deletion without specifying next-hop" does not work...) + */ + if ( tt->type == DEV_TYPE_TUN ) + argv_printf_cat( &argv, " %s", "fe80::8" ); + else + argv_printf_cat( &argv, " %s", gateway ); + +#if 0 + if (r->metric_defined) + argv_printf_cat (&argv, "METRIC %d", r->metric); +#endif + + argv_msg (D_ROUTE, &argv); + + netcmd_semaphore_lock (); + openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add ipv6 command failed"); + netcmd_semaphore_release (); + +#elif defined (TARGET_SOLARIS) + + /* example: route delete -inet6 2001:db8::/32 somegateway */ + /* GERT-TODO: this is untested, but should work */ + + argv_printf (&argv, "%s delete -inet6 %s/%d %s", + ROUTE_PATH, + network, + r6->netbits, + gateway ); + + argv_msg (D_ROUTE, &argv); + openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route delete -inet6 command failed"); + +#elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) + + argv_printf (&argv, "%s delete -inet6 %s/%d -iface %s", + ROUTE_PATH, + network, + r6->netbits, + device ); + + argv_msg (D_ROUTE, &argv); + openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route delete -inet6 command failed"); + +#elif defined(TARGET_DARWIN) + + argv_printf (&argv, "%s delete -inet6 %s -prefixlen %d -iface %s", + ROUTE_PATH, + network, r6->netbits, device ); + + argv_msg (D_ROUTE, &argv); + openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route delete -inet6 command failed"); + +#elif defined(TARGET_OPENBSD) + + argv_printf (&argv, "%s delete -inet6 %s -prefixlen %d %s", + ROUTE_PATH, + network, r6->netbits, gateway ); + + argv_msg (D_ROUTE, &argv); + openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD route delete -inet6 command failed"); + +#elif defined(TARGET_NETBSD) + + argv_printf (&argv, "%s delete -inet6 %s/%d %s", + ROUTE_PATH, + network, r6->netbits, gateway ); + + argv_msg (D_ROUTE, &argv); + openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD route delete -inet6 command failed"); + +#else + msg (M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system. Try putting your routes in a --route-down script"); +#endif + + argv_reset (&argv); + gc_free (&gc); +} + /* * The --redirect-gateway option requires OS-specific code below * to get the current default gateway. diff -rauN openvpn-2.2.0/route.h openvpn-2.2.0-debian/route.h --- openvpn-2.2.0/route.h 2011-04-06 18:04:54.000000000 +0200 +++ openvpn-2.2.0-debian/route.h 2011-05-21 19:10:29.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,24 @@ int netbits; struct iroute *next; }; + +struct iroute_ipv6 { + struct in6_addr network; + unsigned int netbits; + struct iroute_ipv6 *next; +}; #endif struct route_option_list *new_route_option_list (const int max_routes, struct gc_arena *a); +struct route_ipv6_option_list *new_route_ipv6_option_list (const int max_routes, struct gc_arena *a); struct route_option_list *clone_route_option_list (const struct route_option_list *src, struct gc_arena *a); void copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src); struct route_list *new_route_list (const int max_routes, struct gc_arena *a); +struct route_ipv6_list *new_route_ipv6_list (const int max_routes, struct gc_arena *a); void add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es); +void add_route_ipv6 (struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es); void add_route_to_option_list (struct route_option_list *l, const char *network, @@ -136,6 +183,11 @@ const char *gateway, const char *metric); +void add_route_ipv6_to_option_list (struct route_ipv6_option_list *l, + const char *prefix, + const char *gateway, + const char *metric); + bool init_route_list (struct route_list *rl, const struct route_option_list *opt, const char *remote_endpoint, @@ -143,21 +195,30 @@ in_addr_t remote_host, struct env_set *es); +bool init_route_ipv6_list (struct route_ipv6_list *rl6, + const struct route_ipv6_option_list *opt6, + const char *remote_endpoint, + int default_metric, + struct env_set *es); + void route_list_add_default_gateway (struct route_list *rl, struct env_set *es, const in_addr_t addr); void add_routes (struct route_list *rl, + struct route_ipv6_list *rl6, const struct tuntap *tt, unsigned int flags, const struct env_set *es); void delete_routes (struct route_list *rl, + struct route_ipv6_list *rl6, const struct tuntap *tt, unsigned int flags, const struct env_set *es); void setenv_routes (struct env_set *es, const struct route_list *rl); +void setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6); bool is_special_addr (const char *addr_str); diff -rauN openvpn-2.2.0/socket.c openvpn-2.2.0-debian/socket.c --- openvpn-2.2.0/socket.c 2011-04-21 21:13:34.000000000 +0200 +++ openvpn-2.2.0-debian/socket.c 2011-05-21 19:20:23.000000000 +0200 @@ -36,10 +36,16 @@ #include "memdbg.h" const int proto_overhead[] = { /* indexed by PROTO_x */ - IPv4_UDP_HEADER_SIZE, + 0, + IPv4_UDP_HEADER_SIZE, /* IPv4 */ IPv4_TCP_HEADER_SIZE, IPv4_TCP_HEADER_SIZE, - IPv4_TCP_HEADER_SIZE +#ifdef USE_PF_INET6 + IPv6_UDP_HEADER_SIZE, /* IPv6 */ + IPv6_TCP_HEADER_SIZE, + IPv6_TCP_HEADER_SIZE, + IPv6_TCP_HEADER_SIZE, +#endif }; /* @@ -276,6 +282,201 @@ return (flags & GETADDR_HOST_ORDER) ? ntohl (ia.s_addr) : ia.s_addr; } +#ifdef USE_PF_INET6 +/* + * Translate IPv6 addr or hostname into struct addrinfo + * If resolve error, try again for + * resolve_retry_seconds seconds. + */ +bool +getaddr6 (unsigned int flags, + const char *hostname, + int resolve_retry_seconds, + volatile int *signal_received, + int *gai_err, + struct sockaddr_in6 *in6) +{ + bool success; + struct addrinfo hints, *ai; + int status; + int sigrec = 0; + int msglevel = (flags & GETADDR_FATAL) ? M_FATAL : D_RESOLVE_ERRORS; + struct gc_arena gc = gc_new (); + + ASSERT(in6); + + if (!hostname) + hostname = "::"; + + if (flags & GETADDR_RANDOMIZE) + hostname = hostname_randomize(hostname, &gc); + + if (flags & GETADDR_MSG_VIRT_OUT) + msglevel |= M_MSG_VIRT_OUT; + + CLEAR (ai); + success = false; + + if ((flags & (GETADDR_FATAL_ON_SIGNAL|GETADDR_WARN_ON_SIGNAL)) + && !signal_received) + signal_received = &sigrec; + + /* try numeric ipv6 addr first */ + CLEAR(hints); + hints.ai_family = AF_INET6; + hints.ai_flags = AI_NUMERICHOST; + if ((status = getaddrinfo(hostname, NULL, &hints, &ai))==0) + { + *in6 = *((struct sockaddr_in6 *)(ai->ai_addr)); + freeaddrinfo(ai); + ai = NULL; + } + if (gai_err) + *gai_err = status; + + + if (status != 0) /* parse as IPv6 address failed? */ + { + const int fail_wait_interval = 5; /* seconds */ + int resolve_retries = (flags & GETADDR_TRY_ONCE) ? 1 : (resolve_retry_seconds / fail_wait_interval); + const char *fmt; + int level = 0; + int err; + + ai = NULL; + + fmt = "RESOLVE: Cannot resolve host address: %s: %s"; + if ((flags & GETADDR_MENTION_RESOLVE_RETRY) + && !resolve_retry_seconds) + fmt = "RESOLVE: Cannot resolve host address: %s: %s (I would have retried this name query if you had specified the --resolv-retry option.)"; + + if (!(flags & GETADDR_RESOLVE) || status == EAI_FAIL) + { + msg (msglevel, "RESOLVE: Cannot parse IPv6 address: %s", hostname); + goto done; + } + +#ifdef ENABLE_MANAGEMENT + if (flags & GETADDR_UPDATE_MANAGEMENT_STATE) + { + if (management) + management_set_state (management, + OPENVPN_STATE_RESOLVE, + NULL, + (in_addr_t)0, + (in_addr_t)0); + } +#endif + + /* + * Resolve hostname + */ + while (true) + { + /* try hostname lookup */ + hints.ai_flags = 0; + hints.ai_socktype = dnsflags_to_socktype(flags); + dmsg (D_SOCKET_DEBUG, "GETADDR6 flags=0x%04x ai_family=%d ai_socktype=%d", + flags, hints.ai_family, hints.ai_socktype); + err = getaddrinfo(hostname, NULL, &hints, &ai); + + if (gai_err) + *gai_err = err; + + if (signal_received) + { + get_signal (signal_received); + if (*signal_received) /* were we interrupted by a signal? */ + { + if (0 == err) { + ASSERT(ai); + freeaddrinfo(ai); + ai = NULL; + } + if (*signal_received == SIGUSR1) /* ignore SIGUSR1 */ + { + msg (level, "RESOLVE: Ignored SIGUSR1 signal received during DNS resolution attempt"); + *signal_received = 0; + } + else + goto done; + } + } + + /* success? */ + if (0 == err) + break; + + /* resolve lookup failed, should we + continue or fail? */ + + level = msglevel; + if (resolve_retries > 0) + level = D_RESOLVE_ERRORS; + + msg (level, + fmt, + hostname, + gai_strerror(err)); + + if (--resolve_retries <= 0) + goto done; + + openvpn_sleep (fail_wait_interval); + } + + ASSERT(ai); + + if (!ai->ai_next) + *in6 = *((struct sockaddr_in6*)(ai->ai_addr)); + else + /* more than one address returned */ + { + struct addrinfo *ai_cursor; + int n = 0; + /* count address list */ + for (ai_cursor = ai; ai_cursor; ai_cursor = ai_cursor->ai_next) n++; + ASSERT (n >= 2); + + msg (D_RESOLVE_ERRORS, "RESOLVE: NOTE: %s resolves to %d ipv6 addresses, choosing one by random", + hostname, + n); + + /* choose address randomly, for basic load-balancing capability */ + n--; + n %= get_random(); + for (ai_cursor = ai; n; ai_cursor = ai_cursor->ai_next) n--; + *in6 = *((struct sockaddr_in6*)(ai_cursor->ai_addr)); + } + + freeaddrinfo(ai); + ai = NULL; + + /* hostname resolve succeeded */ + success = true; + } + else + { + /* IP address parse succeeded */ + success = true; + } + + done: + if (signal_received && *signal_received) + { + int level = 0; + if (flags & GETADDR_FATAL_ON_SIGNAL) + level = M_FATAL; + else if (flags & GETADDR_WARN_ON_SIGNAL) + level = M_WARN; + msg (level, "RESOLVE: signal received during DNS resolution attempt"); + } + + gc_free (&gc); + return success; +} +#endif /* USE_PF_INET6 */ + /* * We do our own inet_aton because the glibc function * isn't very good about error checking. @@ -342,6 +543,24 @@ } } +bool +ipv6_addr_safe (const char *ipv6_text_addr) +{ + /* verify non-NULL */ + if (!ipv6_text_addr) + return false; + + /* verify length is within limits */ + if (strlen (ipv6_text_addr) > INET6_ADDRSTRLEN ) + return false; + + /* verify that string will convert to IPv6 address */ + { + struct in6_addr a6; + return inet_pton( AF_INET6, ipv6_text_addr, &a6 ) == 1; + } +} + static bool dns_addr_safe (const char *addr) { @@ -410,20 +629,53 @@ bool *changed, const unsigned int sockflags) { - if (host && addr) + switch(addr->addr.sa.sa_family) { - const in_addr_t new_addr = getaddr ( - sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sockflags), - host, - 1, - NULL, - NULL); - if (new_addr && addr->sa.sin_addr.s_addr != new_addr) + case AF_INET: + if (host && addr) { - addr->sa.sin_addr.s_addr = new_addr; - *changed = true; + const in_addr_t new_addr = getaddr ( + sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sockflags), + host, + 1, + NULL, + NULL); + if (new_addr && addr->addr.in4.sin_addr.s_addr != new_addr) + { + addr->addr.in4.sin_addr.s_addr = new_addr; + *changed = true; + } } - } + break; +#ifdef USE_PF_INET6 + case AF_INET6: + if (host && addr) + { + struct sockaddr_in6 sin6; + CLEAR(sin6); + int success = getaddr6 ( + sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sockflags), + host, + 1, + NULL, + NULL, + &sin6); + if ( success ) + { + if (!IN6_ARE_ADDR_EQUAL(&sin6.sin6_addr, &addr->addr.in6.sin6_addr)) + { + int port = addr->addr.in6.sin6_port; + /* ipv6 requires also eg. sin6_scope_id => easier to fully copy and override port */ + addr->addr.in6 = sin6; + addr->addr.in6.sin6_port = port; + } + } + } + break; +#endif + default: + ASSERT(0); + } } static int @@ -610,12 +862,68 @@ else if (flags & SF_USE_IP_PKTINFO) { int pad = 1; - setsockopt (sd, SOL_IP, IP_PKTINFO, (void*)&pad, sizeof(pad)); +#ifdef IP_PKTINFO + if (setsockopt (sd, SOL_IP, IP_PKTINFO, + (void*)&pad, sizeof(pad)) < 0) + msg(M_SOCKERR, "UDP: failed setsockopt for IP_PKTINFO"); +#elif defined(IP_RECVDSTADDR) + if (setsockopt (sd, IPPROTO_IP, IP_RECVDSTADDR, + (void*)&pad, sizeof(pad)) < 0) + msg(M_SOCKERR, "UDP: failed setsockopt for IP_RECVDSTADDR"); +#else +#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) +#endif } #endif return sd; } +#ifdef USE_PF_INET6 +static socket_descriptor_t +create_socket_udp6 (const unsigned int flags) +{ + socket_descriptor_t sd; + + if ((sd = socket (PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) + msg (M_SOCKERR, "UDP: Cannot create UDP6 socket"); +#if ENABLE_IP_PKTINFO + else if (flags & SF_USE_IP_PKTINFO) + { + int pad = 1; +#ifdef IPV6_RECVPKTINFO + if (setsockopt (sd, IPPROTO_IPV6, IPV6_RECVPKTINFO, + (void*)&pad, sizeof(pad)) < 0) + msg(M_SOCKERR, "UDP: failed setsockopt for IPV6_RECVPKTINFO"); +#else + if (setsockopt (sd, IPPROTO_IPV6, IPV6_PKTINFO, + (void*)&pad, sizeof(pad)) < 0) + msg(M_SOCKERR, "UDP: failed setsockopt for IPV6_PKTINFO"); +#endif + } +#endif + return sd; +} + +static socket_descriptor_t +create_socket_tcp6 (void) +{ + socket_descriptor_t sd; + + if ((sd = socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP)) < 0) + msg (M_SOCKERR, "Cannot create TCP6 socket"); + + /* set SO_REUSEADDR on socket */ + { + int on = 1; + if (setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, + (void *) &on, sizeof (on)) < 0) + msg (M_SOCKERR, "TCP: Cannot setsockopt SO_REUSEADDR on TCP6 socket"); + } + + return sd; +} + +#endif static void create_socket (struct link_socket *sock) { @@ -623,6 +931,7 @@ if (sock->info.proto == PROTO_UDPv4) { sock->sd = create_socket_udp (sock->sockflags); + sock->sockflags |= SF_GETADDRINFO_DGRAM; #ifdef ENABLE_SOCKS if (sock->socks_proxy) @@ -634,6 +943,18 @@ { sock->sd = create_socket_tcp (); } +#ifdef USE_PF_INET6 + else if (sock->info.proto == PROTO_TCPv6_SERVER + || sock->info.proto == PROTO_TCPv6_CLIENT) + { + sock->sd = create_socket_tcp6 (); + } + else if (sock->info.proto == PROTO_UDPv6) + { + sock->sd = create_socket_udp6 (sock->sockflags); + sock->sockflags |= SF_GETADDRINFO_DGRAM; + } +#endif else { ASSERT (0); @@ -671,7 +992,12 @@ struct link_socket_actual *act, const bool nowait) { - socklen_t remote_len = sizeof (act->dest.sa); + /* af_addr_size WILL return 0 in this case if AFs other than AF_INET + * are compiled because act is empty here. + * could use getsockname() to support later remote_len check + */ + socklen_t remote_len_af = af_addr_size(act->dest.addr.sa.sa_family); + socklen_t remote_len = sizeof(act->dest.addr); socket_descriptor_t new_sd = SOCKET_UNDEFINED; CLEAR (*act); @@ -679,7 +1005,7 @@ #ifdef HAVE_GETPEERNAME if (nowait) { - new_sd = getpeername (sd, (struct sockaddr *) &act->dest.sa, &remote_len); + new_sd = getpeername (sd, &act->dest.addr.sa, &remote_len); if (!socket_defined (new_sd)) msg (D_LINK_ERRORS | M_ERRNO_SOCK, "TCP: getpeername() failed"); @@ -692,7 +1018,7 @@ #endif else { - new_sd = accept (sd, (struct sockaddr *) &act->dest.sa, &remote_len); + new_sd = accept (sd, &act->dest.addr.sa, &remote_len); } #if 0 /* For debugging only, test the effect of accept() failures */ @@ -708,7 +1034,8 @@ { msg (D_LINK_ERRORS | M_ERRNO_SOCK, "TCP: accept(%d) failed", sd); } - else if (remote_len != sizeof (act->dest.sa)) + /* only valid if we have remote_len_af!=0 */ + else if (remote_len_af && remote_len != remote_len_af) { msg (D_LINK_ERRORS, "TCP: Received strange incoming connection with unknown address length=%d", remote_len); openvpn_close_socket (new_sd); @@ -809,7 +1136,7 @@ { struct gc_arena gc = gc_new (); - if (bind (sd, (struct sockaddr *) &local->sa, sizeof (local->sa))) + if (bind (sd, &local->addr.sa, af_addr_size(local->addr.sa.sa_family))) { const int errnum = openvpn_errno_socket (); msg (M_FATAL, "%s: Socket bind failed on local address %s: %s", @@ -830,7 +1157,7 @@ #ifdef CONNECT_NONBLOCK set_nonblock (sd); - status = connect (sd, (struct sockaddr *) &remote->sa, sizeof (remote->sa)); + status = connect (sd, &remote->addr.sa, af_addr_size(remote->addr.sa.sa_family)); if (status) status = openvpn_errno_socket (); if (status == EINPROGRESS) @@ -888,7 +1215,7 @@ } } #else - status = connect (sd, (struct sockaddr *) &remote->sa, sizeof (remote->sa)); + status = connect (sd, &remote->addr.sa, af_addr_size(remote->addr.sa.sa_family)); if (status) status = openvpn_errno_socket (); #endif @@ -966,7 +1293,20 @@ if (*signal_received) goto done; - *sd = create_socket_tcp (); +#ifdef USE_PF_INET6 + switch(local->addr.sa.sa_family) + { + case PF_INET6: + *sd = create_socket_tcp6 (); + break; + case PF_INET: +#endif + *sd = create_socket_tcp (); +#ifdef USE_PF_INET6 + break; + } +#endif + if (bind_local) socket_bind (*sd, local, "TCP Client"); update_remote (remote_dynamic, remote, remote_changed, sockflags); @@ -1031,15 +1371,54 @@ /* resolve local address if undefined */ if (!addr_defined (&sock->info.lsa->local)) { - sock->info.lsa->local.sa.sin_family = AF_INET; - sock->info.lsa->local.sa.sin_addr.s_addr = - (sock->local_host ? getaddr (GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL, +#ifdef USE_PF_INET6 + /* may return AF_{INET|INET6} guessed from local_host */ + switch(addr_guess_family(sock->info.proto, sock->local_host)) + { + case AF_INET: +#endif + sock->info.lsa->local.addr.in4.sin_family = AF_INET; + sock->info.lsa->local.addr.in4.sin_addr.s_addr = + (sock->local_host ? getaddr (GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL, + sock->local_host, + 0, + NULL, + NULL) + : htonl (INADDR_ANY)); + sock->info.lsa->local.addr.in4.sin_port = htons (sock->local_port); +#ifdef USE_PF_INET6 + break; + case AF_INET6: + { + int success; + int err; + CLEAR(sock->info.lsa->local.addr.in6); + if (sock->local_host) + { + success = getaddr6(GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL, sock->local_host, 0, NULL, - NULL) - : htonl (INADDR_ANY)); - sock->info.lsa->local.sa.sin_port = htons (sock->local_port); + &err, + &sock->info.lsa->local.addr.in6); + } + else + { + sock->info.lsa->local.addr.in6.sin6_family = AF_INET6; + sock->info.lsa->local.addr.in6.sin6_addr = in6addr_any; + success = true; + } + if (!success) + { + msg (M_FATAL, "getaddr6() failed for local \"%s\": %s", + sock->local_host, + gai_strerror(err)); + } + sock->info.lsa->local.addr.in6.sin6_port = htons (sock->local_port); + } + break; + } +#endif /* USE_PF_INET6 */ } /* bind to local address/port */ @@ -1062,14 +1441,32 @@ volatile int *signal_received) { struct gc_arena gc = gc_new (); +#ifdef USE_PF_INET6 + int af; +#endif if (!sock->did_resolve_remote) { /* resolve remote address if undefined */ if (!addr_defined (&sock->info.lsa->remote)) { - sock->info.lsa->remote.sa.sin_family = AF_INET; - sock->info.lsa->remote.sa.sin_addr.s_addr = 0; +#ifdef USE_PF_INET6 + af = addr_guess_family(sock->info.proto, sock->remote_host); + switch(af) + { + case AF_INET: +#endif + sock->info.lsa->remote.addr.in4.sin_family = AF_INET; + sock->info.lsa->remote.addr.in4.sin_addr.s_addr = 0; +#ifdef USE_PF_INET6 + break; + case AF_INET6: + CLEAR(sock->info.lsa->remote.addr.in6); + sock->info.lsa->remote.addr.in6.sin6_family = AF_INET6; + sock->info.lsa->remote.addr.in6.sin6_addr = in6addr_any; + break; + } +#endif if (sock->remote_host) { @@ -1112,13 +1509,31 @@ ASSERT (0); } - sock->info.lsa->remote.sa.sin_addr.s_addr = getaddr ( - flags, - sock->remote_host, - retry, - &status, - signal_received); - +#ifdef USE_PF_INET6 + switch(af) + { + case AF_INET: +#endif + sock->info.lsa->remote.addr.in4.sin_addr.s_addr = getaddr ( + flags, + sock->remote_host, + retry, + &status, + signal_received); +#ifdef USE_PF_INET6 + break; + case AF_INET6: + status = getaddr6 ( + flags, + sock->remote_host, + retry, + signal_received, + NULL, + &sock->info.lsa->remote.addr.in6); + break; + } +#endif + dmsg (D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d", flags, phase, @@ -1138,8 +1553,19 @@ goto done; } } - - sock->info.lsa->remote.sa.sin_port = htons (sock->remote_port); +#ifdef USE_PF_INET6 + switch(af) + { + case AF_INET: +#endif + sock->info.lsa->remote.addr.in4.sin_port = htons (sock->remote_port); +#ifdef USE_PF_INET6 + break; + case AF_INET6: + sock->info.lsa->remote.addr.in6.sin6_port = htons (sock->remote_port); + break; + } +#endif } /* should we re-use previous active remote address? */ @@ -1256,7 +1682,11 @@ if (mode == LS_MODE_TCP_ACCEPT_FROM) { ASSERT (accept_from); - ASSERT (sock->info.proto == PROTO_TCPv4_SERVER); + ASSERT (sock->info.proto == PROTO_TCPv4_SERVER +#ifdef USE_PF_INET6 + || sock->info.proto == PROTO_TCPv6_SERVER +#endif + ); ASSERT (!sock->inetd); sock->sd = accept_from->sd; } @@ -1313,7 +1743,11 @@ /* were we started by inetd or xinetd? */ if (sock->inetd) { - ASSERT (sock->info.proto != PROTO_TCPv4_CLIENT); + ASSERT (sock->info.proto != PROTO_TCPv4_CLIENT +#ifdef USE_PF_INET6 + && sock->info.proto != PROTO_TCPv6_CLIENT +#endif + ); ASSERT (socket_defined (inetd_socket_descriptor)); sock->sd = inetd_socket_descriptor; } @@ -1327,6 +1761,10 @@ resolve_bind_local (sock); resolve_remote (sock, 1, NULL, NULL); } + + /* set socket file descriptor to not pass across execs, so that + scripts don't have access to it */ + set_cloexec (sock->sd); } /* finalize socket initialization */ @@ -1362,7 +1800,34 @@ /* were we started by inetd or xinetd? */ if (sock->inetd) { - if (sock->info.proto == PROTO_TCPv4_SERVER) + if (sock->info.proto == PROTO_TCPv4_SERVER +#ifdef USE_PF_INET6 + || sock->info.proto == PROTO_TCPv6_SERVER +#endif + ) { + /* AF_INET as default (and fallback) for inetd */ + sock->info.lsa->actual.dest.addr.sa.sa_family = AF_INET; +#ifdef USE_PF_INET6 +#ifdef HAVE_GETSOCKNAME + { + /* inetd: hint family type for dest = local's */ + struct openvpn_sockaddr local_addr; + socklen_t addrlen = sizeof(local_addr); + if (getsockname (sock->sd, (struct sockaddr *)&local_addr, &addrlen) == 0) { + sock->info.lsa->actual.dest.addr.sa.sa_family = local_addr.addr.sa.sa_family; + dmsg (D_SOCKET_DEBUG, "inetd(%s): using sa_family=%d from getsockname(%d)", + proto2ascii(sock->info.proto, false), local_addr.addr.sa.sa_family, + sock->sd); + } else + msg (M_WARN, "inetd(%s): getsockname(%d) failed, using AF_INET", + proto2ascii(sock->info.proto, false), sock->sd); + } +#else + msg (M_WARN, "inetd(%s): this OS does not provide the getsockname() " + "function, using AF_INET", + proto2ascii(sock->info.proto, false)); +#endif +#endif sock->sd = socket_listen_accept (sock->sd, &sock->info.lsa->actual, @@ -1372,6 +1837,7 @@ false, sock->inetd == INETD_NOWAIT, signal_received); + } ASSERT (!remote_changed); if (*signal_received) goto done; @@ -1384,7 +1850,11 @@ goto done; /* TCP client/server */ - if (sock->info.proto == PROTO_TCPv4_SERVER) + if (sock->info.proto == PROTO_TCPv4_SERVER +#ifdef USE_PF_INET6 + ||sock->info.proto == PROTO_TCPv6_SERVER +#endif + ) { switch (sock->mode) { @@ -1419,7 +1889,11 @@ ASSERT (0); } } - else if (sock->info.proto == PROTO_TCPv4_CLIENT) + else if (sock->info.proto == PROTO_TCPv4_CLIENT +#ifdef USE_PF_INET6 + ||sock->info.proto == PROTO_TCPv6_CLIENT +#endif + ) { #ifdef GENERAL_PROXY_SUPPORT @@ -1506,8 +1980,8 @@ sock->remote_port = sock->proxy_dest_port; sock->did_resolve_remote = false; - sock->info.lsa->actual.dest.sa.sin_addr.s_addr = 0; - sock->info.lsa->remote.sa.sin_addr.s_addr = 0; + addr_zero_host(&sock->info.lsa->actual.dest); + addr_zero_host(&sock->info.lsa->remote); resolve_remote (sock, 1, NULL, signal_received); @@ -1522,7 +1996,7 @@ if (remote_changed) { msg (M_INFO, "TCP/UDP: Dynamic remote address changed during TCP connection establishment"); - sock->info.lsa->remote.sa.sin_addr.s_addr = sock->info.lsa->actual.dest.sa.sin_addr.s_addr; + addr_copy_host(&sock->info.lsa->remote, &sock->info.lsa->actual.dest); } } @@ -1532,10 +2006,6 @@ /* set socket to non-blocking mode */ set_nonblock (sock->sd); - /* set socket file descriptor to not pass across execs, so that - scripts don't have access to it */ - set_cloexec (sock->sd); - #ifdef ENABLE_SOCKS if (socket_defined (sock->ctrl_sd)) set_cloexec (sock->ctrl_sd); @@ -1708,13 +2178,20 @@ { struct gc_arena gc = gc_new (); - msg (D_LINK_ERRORS, - "TCP/UDP: Incoming packet rejected from %s[%d], expected peer address: %s (allow this incoming source address/port by removing --remote or adding --float)", - print_link_socket_actual (from_addr, &gc), - (int)from_addr->dest.sa.sin_family, - print_sockaddr (&info->lsa->remote, &gc)); + switch(from_addr->dest.addr.sa.sa_family) + { + case AF_INET: +#ifdef USE_PF_INET6 + case AF_INET6: +#endif + msg (D_LINK_ERRORS, + "TCP/UDP: Incoming packet rejected from %s[%d], expected peer address: %s (allow this incoming source address/port by removing --remote or adding --float)", + print_link_socket_actual (from_addr, &gc), + (int)from_addr->dest.addr.sa.sa_family, + print_sockaddr (&info->lsa->remote, &gc)); + break; + } buf->len = 0; - gc_free (&gc); } @@ -1729,10 +2206,25 @@ { const struct link_socket_addr *lsa = info->lsa; +/* + * This logic supports "redirect-gateway" semantic, which + * makes sense only for PF_INET routes over PF_INET endpoints + * + * Maybe in the future consider PF_INET6 endpoints also ... + * by now just ignore it + * + */ +#ifdef USE_PF_INET6 + if (lsa->actual.dest.addr.sa.sa_family != AF_INET) + return IPV4_INVALID_ADDR; +#else + ASSERT (lsa->actual.dest.addr.sa.sa_family == AF_INET); +#endif + if (link_socket_actual_defined (&lsa->actual)) - return ntohl (lsa->actual.dest.sa.sin_addr.s_addr); + return ntohl (lsa->actual.dest.addr.in4.sin_addr.s_addr); else if (addr_defined (&lsa->remote)) - return ntohl (lsa->remote.sa.sin_addr.s_addr); + return ntohl (lsa->remote.addr.in4.sin_addr.s_addr); else return 0; } @@ -1959,26 +2451,61 @@ const unsigned int flags, struct gc_arena *gc) { - if (addr) + struct buffer out = alloc_buf_gc (128, gc); + bool addr_is_defined; + addr_is_defined = addr_defined (addr); + if (!addr_is_defined) { + return "[undef]"; + } +#ifdef USE_PF_INET6 + switch(addr->addr.sa.sa_family) { - struct buffer out = alloc_buf_gc (64, gc); - const int port = ntohs (addr->sa.sin_port); + case AF_INET: +#endif + { + const int port= ntohs (addr->addr.in4.sin_port); + buf_puts (&out, "[AF_INET]"); + + if (!(flags & PS_DONT_SHOW_ADDR)) + buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->addr.in4.sin_addr) : "[undef]")); - if (!(flags & PS_DONT_SHOW_ADDR)) - buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]")); + if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED))) + && port) + { + if (separator) + buf_printf (&out, "%s", separator); - if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED))) - && port) + buf_printf (&out, "%d", port); + } + } +#ifdef USE_PF_INET6 + break; + case AF_INET6: { - if (separator) - buf_printf (&out, "%s", separator); + const int port= ntohs (addr->addr.in6.sin6_port); + char buf[INET6_ADDRSTRLEN] = ""; + buf_puts (&out, "[AF_INET6]"); + if (addr_is_defined) + { + getnameinfo(&addr->addr.sa, sizeof (struct sockaddr_in6), + buf, sizeof (buf), NULL, 0, NI_NUMERICHOST); + buf_puts (&out, buf); + } + if (((flags & PS_SHOW_PORT) || (addr_is_defined && (flags & PS_SHOW_PORT_IF_DEFINED))) + && port) + { + if (separator) + buf_puts (&out, separator); - buf_printf (&out, "%d", port); + buf_printf (&out, "%d", port); + } } - return BSTR (&out); + break; + default: + ASSERT(0); } - else - return "[NULL]"; +#endif + return BSTR (&out); } const char * @@ -1987,6 +2514,10 @@ return print_link_socket_actual_ex (act, ":", PS_SHOW_PORT|PS_SHOW_PKTINFO, gc); } +#ifndef IF_NAMESIZE +#define IF_NAMESIZE 16 +#endif + const char * print_link_socket_actual_ex (const struct link_socket_actual *act, const char *separator, @@ -1995,15 +2526,54 @@ { if (act) { + char ifname[IF_NAMESIZE] = "[undef]"; struct buffer out = alloc_buf_gc (128, gc); buf_printf (&out, "%s", print_sockaddr_ex (&act->dest, separator, flags, gc)); #if ENABLE_IP_PKTINFO - if ((flags & PS_SHOW_PKTINFO) && act->pi.ipi_spec_dst.s_addr) + if ((flags & PS_SHOW_PKTINFO) && addr_defined_ipi(act)) { - struct openvpn_sockaddr sa; - CLEAR (sa); - sa.sa.sin_addr = act->pi.ipi_spec_dst; - buf_printf (&out, " (via %s)", print_sockaddr_ex (&sa, separator, 0, gc)); +#ifdef USE_PF_INET6 + switch(act->dest.addr.sa.sa_family) + { + case AF_INET: +#endif + { + struct openvpn_sockaddr sa; + CLEAR (sa); + sa.addr.in4.sin_family = AF_INET; +#ifdef IP_PKTINFO + sa.addr.in4.sin_addr = act->pi.in4.ipi_spec_dst; + if_indextoname(act->pi.in4.ipi_ifindex, ifname); +#elif defined(IP_RECVDSTADDR) + sa.addr.in4.sin_addr = act->pi.in4; + ifname[0]=0; +#else +#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) +#endif + buf_printf (&out, " (via %s%%%s)", + print_sockaddr_ex (&sa, separator, 0, gc), + ifname); + } +#ifdef USE_PF_INET6 + break; + case AF_INET6: + { + struct sockaddr_in6 sin6; + char buf[INET6_ADDRSTRLEN] = "[undef]"; + CLEAR(sin6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = act->pi.in6.ipi6_addr; + if_indextoname(act->pi.in6.ipi6_ifindex, ifname); + if (getnameinfo((struct sockaddr *)&sin6, sizeof (struct sockaddr_in6), + buf, sizeof (buf), NULL, 0, NI_NUMERICHOST) == 0) + buf_printf (&out, " (via %s%%%s)", buf, ifname); + else + buf_printf (&out, " (via [getnameinfo() err]%%%s)", ifname); + } + break; + } +#endif /* USE_PF_INET6 */ + } #endif return BSTR (&out); @@ -2032,24 +2602,95 @@ 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) { char name_buf[256]; - if (flags & SA_IP_PORT) - openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip", name_prefix); - else - openvpn_snprintf (name_buf, sizeof (name_buf), "%s", name_prefix); +#ifdef USE_PF_INET6 + char buf[128]; + switch(addr->addr.sa.sa_family) + { + case AF_INET: +#endif + if (flags & SA_IP_PORT) + openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip", name_prefix); + else + openvpn_snprintf (name_buf, sizeof (name_buf), "%s", name_prefix); - setenv_str (es, name_buf, inet_ntoa (addr->sa.sin_addr)); + setenv_str (es, name_buf, inet_ntoa (addr->addr.in4.sin_addr)); - if ((flags & SA_IP_PORT) && addr->sa.sin_port) - { - openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix); - setenv_int (es, name_buf, ntohs (addr->sa.sin_port)); + if ((flags & SA_IP_PORT) && addr->addr.in4.sin_port) + { + openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix); + setenv_int (es, name_buf, ntohs (addr->addr.in4.sin_port)); + } +#ifdef USE_PF_INET6 + break; + case AF_INET6: + openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip6", name_prefix); + getnameinfo(&addr->addr.sa, sizeof (struct sockaddr_in6), + buf, sizeof(buf), NULL, 0, NI_NUMERICHOST); + setenv_str (es, name_buf, buf); + + if ((flags & SA_IP_PORT) && addr->addr.in6.sin6_port) + { + openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix); + setenv_int (es, name_buf, ntohs (addr->addr.in6.sin6_port)); + } + break; } +#endif } void @@ -2059,7 +2700,8 @@ { struct openvpn_sockaddr si; CLEAR (si); - si.sa.sin_addr.s_addr = htonl (addr); + si.addr.in4.sin_family = AF_INET; + si.addr.in4.sin_addr.s_addr = htonl (addr); setenv_sockaddr (es, name_prefix, &si, flags); } } @@ -2080,16 +2722,63 @@ struct proto_names { const char *short_form; const char *display_form; + bool is_dgram; + bool is_net; + unsigned short proto_af; }; /* Indexed by PROTO_x */ -static const struct proto_names proto_names[] = { - {"udp", "UDPv4"}, - {"tcp-server", "TCPv4_SERVER"}, - {"tcp-client", "TCPv4_CLIENT"}, - {"tcp", "TCPv4"} +static const struct proto_names proto_names[PROTO_N] = { + {"proto-uninitialized", "proto-NONE",0,0, AF_UNSPEC}, + {"udp", "UDPv4",1,1, AF_INET}, + {"tcp-server", "TCPv4_SERVER",0,1, AF_INET}, + {"tcp-client", "TCPv4_CLIENT",0,1, AF_INET}, + {"tcp", "TCPv4",0,1, AF_INET}, +#ifdef USE_PF_INET6 + {"udp6" ,"UDPv6",1,1, AF_INET6}, + {"tcp6-server","TCPv6_SERVER",0,1, AF_INET6}, + {"tcp6-client","TCPv6_CLIENT",0,1, AF_INET6}, + {"tcp6" ,"TCPv6",0,1, AF_INET6}, +#endif }; +bool +proto_is_net(int proto) +{ + if (proto < 0 || proto >= PROTO_N) + ASSERT(0); + return proto_names[proto].is_net; +} +bool +proto_is_dgram(int proto) +{ + if (proto < 0 || proto >= PROTO_N) + ASSERT(0); + return proto_names[proto].is_dgram; +} +bool +proto_is_udp(int proto) +{ + if (proto < 0 || proto >= PROTO_N) + ASSERT(0); + return proto_names[proto].is_dgram&&proto_names[proto].is_net; +} +bool +proto_is_tcp(int proto) +{ + if (proto < 0 || proto >= PROTO_N) + ASSERT(0); + return (!proto_names[proto].is_dgram)&&proto_names[proto].is_net; +} + +unsigned short +proto_sa_family(int proto) +{ + if (proto < 0 || proto >= PROTO_N) + ASSERT(0); + return proto_names[proto].proto_af; +} + int ascii2proto (const char* proto_name) { @@ -2129,6 +2818,45 @@ return BSTR (&out); } +int +addr_guess_family(int proto, const char *name) +{ +#ifdef USE_PF_INET6 + unsigned short ret; +#endif + if (proto) + { + return proto_sa_family(proto); /* already stamped */ + } +#ifdef USE_PF_INET6 + else + { + struct addrinfo hints , *ai; + int err; + CLEAR(hints); + hints.ai_flags = AI_NUMERICHOST; + err = getaddrinfo(name, NULL, &hints, &ai); + if ( 0 == err ) + { + ret=ai->ai_family; + freeaddrinfo(ai); + return ret; + } + } +#endif + return AF_INET; /* default */ +} +const char * +addr_family_name (int af) +{ + switch (af) + { + case AF_INET: return "AF_INET"; + case AF_INET6: return "AF_INET6"; + } + return "AF_UNSPEC"; +} + /* * Given a local proto, return local proto * if !remote, or compatible remote proto @@ -2143,10 +2871,15 @@ ASSERT (proto >= 0 && proto < PROTO_N); if (remote) { - if (proto == PROTO_TCPv4_SERVER) - return PROTO_TCPv4_CLIENT; - if (proto == PROTO_TCPv4_CLIENT) - return PROTO_TCPv4_SERVER; + switch (proto) + { + case PROTO_TCPv4_SERVER: return PROTO_TCPv4_CLIENT; + case PROTO_TCPv4_CLIENT: return PROTO_TCPv4_SERVER; +#ifdef USE_PF_INET6 + case PROTO_TCPv6_SERVER: return PROTO_TCPv6_CLIENT; + case PROTO_TCPv6_CLIENT: return PROTO_TCPv6_SERVER; +#endif + } } return proto; } @@ -2205,10 +2938,29 @@ #if ENABLE_IP_PKTINFO #pragma pack(1) /* needed to keep structure size consistent for 32 vs. 64-bit architectures */ -struct openvpn_pktinfo +struct openvpn_in4_pktinfo { struct cmsghdr cmsghdr; - struct in_pktinfo in_pktinfo; +#ifdef HAVE_IN_PKTINFO + struct in_pktinfo pi4; +#endif +#ifdef IP_RECVDSTADDR + struct in_addr pi4; +#endif +}; +#ifdef USE_PF_INET6 +struct openvpn_in6_pktinfo +{ + struct cmsghdr cmsghdr; + struct in6_pktinfo pi6; +}; +#endif + +union openvpn_pktinfo { + struct openvpn_in4_pktinfo msgpi4; +#ifdef USE_PF_INET6 + struct openvpn_in6_pktinfo msgpi6; +#endif }; #pragma pack() @@ -2219,18 +2971,18 @@ struct link_socket_actual *from) { struct iovec iov; - struct openvpn_pktinfo opi; + union openvpn_pktinfo opi; struct msghdr mesg; - socklen_t fromlen = sizeof (from->dest.sa); + socklen_t fromlen = sizeof (from->dest.addr); iov.iov_base = BPTR (buf); iov.iov_len = maxsize; mesg.msg_iov = &iov; mesg.msg_iovlen = 1; - mesg.msg_name = &from->dest.sa; + mesg.msg_name = &from->dest.addr; mesg.msg_namelen = fromlen; mesg.msg_control = &opi; - mesg.msg_controllen = sizeof (opi); + mesg.msg_controllen = sizeof opi; buf->len = recvmsg (sock->sd, &mesg, 0); if (buf->len >= 0) { @@ -2239,14 +2991,39 @@ cmsg = CMSG_FIRSTHDR (&mesg); if (cmsg != NULL && CMSG_NXTHDR (&mesg, cmsg) == NULL +#ifdef IP_PKTINFO && cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO - && cmsg->cmsg_len >= sizeof (opi)) +#elif defined(IP_RECVDSTADDR) + && cmsg->cmsg_level == IPPROTO_IP + && cmsg->cmsg_type == IP_RECVDSTADDR +#else +#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) +#endif + && cmsg->cmsg_len >= sizeof (struct openvpn_in4_pktinfo)) { +#ifdef IP_PKTINFO struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg); - from->pi.ipi_ifindex = pkti->ipi_ifindex; - from->pi.ipi_spec_dst = pkti->ipi_spec_dst; + from->pi.in4.ipi_ifindex = pkti->ipi_ifindex; + from->pi.in4.ipi_spec_dst = pkti->ipi_spec_dst; +#elif defined(IP_RECVDSTADDR) + from->pi.in4 = *(struct in_addr*) CMSG_DATA (cmsg); +#else +#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) +#endif + } +#ifdef USE_PF_INET6 + else if (cmsg != NULL + && CMSG_NXTHDR (&mesg, cmsg) == NULL + && cmsg->cmsg_level == IPPROTO_IPV6 + && cmsg->cmsg_type == IPV6_PKTINFO + && cmsg->cmsg_len >= sizeof (struct openvpn_in6_pktinfo)) + { + struct in6_pktinfo *pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg); + from->pi.in6.ipi6_ifindex = pkti6->ipi6_ifindex; + from->pi.in6.ipi6_addr = pkti6->ipi6_addr; } +#endif } return fromlen; } @@ -2258,18 +3035,20 @@ int maxsize, struct link_socket_actual *from) { - socklen_t fromlen = sizeof (from->dest.sa); - from->dest.sa.sin_addr.s_addr = 0; + socklen_t fromlen = sizeof (from->dest.addr); + socklen_t expectedlen = af_addr_size(proto_sa_family(sock->info.proto)); + addr_zero_host(&from->dest); ASSERT (buf_safe (buf, maxsize)); #if ENABLE_IP_PKTINFO - if (sock->sockflags & SF_USE_IP_PKTINFO) + /* Both PROTO_UDPv4 and PROTO_UDPv6 */ + if (proto_is_udp(sock->info.proto) && sock->sockflags & SF_USE_IP_PKTINFO) fromlen = link_socket_read_udp_posix_recvmsg (sock, buf, maxsize, from); else #endif buf->len = recvfrom (sock->sd, BPTR (buf), maxsize, 0, - (struct sockaddr *) &from->dest.sa, &fromlen); - if (fromlen != sizeof (from->dest.sa)) - bad_address_length (fromlen, sizeof (from->dest.sa)); + &from->dest.addr.sa, &fromlen); + if (buf->len >= 0 && expectedlen && fromlen != expectedlen) + bad_address_length (fromlen, expectedlen); return buf->len; } @@ -2306,26 +3085,64 @@ struct iovec iov; struct msghdr mesg; struct cmsghdr *cmsg; - struct in_pktinfo *pkti; - struct openvpn_pktinfo opi; iov.iov_base = BPTR (buf); iov.iov_len = BLEN (buf); mesg.msg_iov = &iov; mesg.msg_iovlen = 1; - mesg.msg_name = &to->dest.sa; - mesg.msg_namelen = sizeof (to->dest.sa); - mesg.msg_control = &opi; - mesg.msg_controllen = sizeof (opi); - mesg.msg_flags = 0; - cmsg = CMSG_FIRSTHDR (&mesg); - cmsg->cmsg_len = sizeof (opi); - cmsg->cmsg_level = SOL_IP; - cmsg->cmsg_type = IP_PKTINFO; - pkti = (struct in_pktinfo *) CMSG_DATA (cmsg); - pkti->ipi_ifindex = to->pi.ipi_ifindex; - pkti->ipi_spec_dst = to->pi.ipi_spec_dst; - pkti->ipi_addr.s_addr = 0; + switch (sock->info.lsa->remote.addr.sa.sa_family) + { + case AF_INET: + { + struct openvpn_in4_pktinfo msgpi4; + mesg.msg_name = &to->dest.addr.sa; + mesg.msg_namelen = sizeof (struct sockaddr_in); + mesg.msg_control = &msgpi4; + mesg.msg_controllen = sizeof msgpi4; + mesg.msg_flags = 0; + cmsg = CMSG_FIRSTHDR (&mesg); + cmsg->cmsg_len = sizeof (struct openvpn_in4_pktinfo); +#ifdef HAVE_IN_PKTINFO + cmsg->cmsg_level = SOL_IP; + cmsg->cmsg_type = IP_PKTINFO; + { + struct in_pktinfo *pkti; + pkti = (struct in_pktinfo *) CMSG_DATA (cmsg); + pkti->ipi_ifindex = to->pi.in4.ipi_ifindex; + pkti->ipi_spec_dst = to->pi.in4.ipi_spec_dst; + pkti->ipi_addr.s_addr = 0; + } +#elif defined(IP_RECVDSTADDR) + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_RECVDSTADDR; + *(struct in_addr *) CMSG_DATA (cmsg) = to->pi.in4; +#else +#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) +#endif + break; + } +#ifdef USE_PF_INET6 + case AF_INET6: + { + struct openvpn_in6_pktinfo msgpi6; + struct in6_pktinfo *pkti6; + mesg.msg_name = &to->dest.addr.sa; + mesg.msg_namelen = sizeof (struct sockaddr_in6); + mesg.msg_control = &msgpi6; + mesg.msg_controllen = sizeof msgpi6; + mesg.msg_flags = 0; + cmsg = CMSG_FIRSTHDR (&mesg); + cmsg->cmsg_len = sizeof (struct openvpn_in6_pktinfo); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg); + pkti6->ipi6_ifindex = to->pi.in6.ipi6_ifindex; + pkti6->ipi6_addr = to->pi.in6.ipi6_addr; + break; + } +#endif + default: ASSERT(0); + } return sendmsg (sock->sd, &mesg, 0); } @@ -2337,6 +3154,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) { @@ -2346,11 +3215,11 @@ int status; /* reset buf to its initial state */ - if (sock->info.proto == PROTO_UDPv4) + if (proto_is_udp(sock->info.proto)) { sock->reads.buf = sock->reads.buf_init; } - else if (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_TCPv4_SERVER) + else if (proto_is_tcp(sock->info.proto)) { stream_buf_get_next (&sock->stream_buf, &sock->reads.buf); } @@ -2370,10 +3239,15 @@ ASSERT (ResetEvent (sock->reads.overlapped.hEvent)); sock->reads.flags = 0; - if (sock->info.proto == PROTO_UDPv4) + if (proto_is_udp(sock->info.proto)) { sock->reads.addr_defined = true; - sock->reads.addrlen = sizeof (sock->reads.addr); +#ifdef USE_PF_INET6 + if (sock->info.proto == PROTO_UDPv6) + sock->reads.addrlen = sizeof (sock->reads.addr6); + else +#endif + sock->reads.addrlen = sizeof (sock->reads.addr); status = WSARecvFrom( sock->sd, wsabuf, @@ -2385,7 +3259,7 @@ &sock->reads.overlapped, NULL); } - else if (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_TCPv4_SERVER) + else if (proto_is_tcp(sock->info.proto)) { sock->reads.addr_defined = false; status = WSARecv( @@ -2405,8 +3279,14 @@ if (!status) /* operation completed immediately? */ { +#ifdef USE_PF_INET6 + int addrlen = af_addr_size(sock->info.lsa->local.addr.sa.sa_family); + if (sock->reads.addr_defined && sock->reads.addrlen != addrlen) + bad_address_length (sock->reads.addrlen, addrlen); +#else if (sock->reads.addr_defined && sock->reads.addrlen != sizeof (sock->reads.addr)) bad_address_length (sock->reads.addrlen, sizeof (sock->reads.addr)); +#endif sock->reads.iostate = IOSTATE_IMMEDIATE_RETURN; @@ -2465,12 +3345,22 @@ ASSERT (ResetEvent (sock->writes.overlapped.hEvent)); sock->writes.flags = 0; - if (sock->info.proto == PROTO_UDPv4) + if (proto_is_udp(sock->info.proto)) { /* set destination address for UDP writes */ sock->writes.addr_defined = true; - sock->writes.addr = to->dest.sa; - sock->writes.addrlen = sizeof (sock->writes.addr); +#ifdef USE_PF_INET6 + if (sock->info.proto == PROTO_UDPv6) + { + sock->writes.addr6 = to->dest.addr.in6; + sock->writes.addrlen = sizeof (sock->writes.addr6); + } + else +#endif + { + sock->writes.addr = to->dest.addr.in4; + sock->writes.addrlen = sizeof (sock->writes.addr); + } status = WSASendTo( sock->sd, @@ -2483,7 +3373,7 @@ &sock->writes.overlapped, NULL); } - else if (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_TCPv4_SERVER) + else if (proto_is_tcp(sock->info.proto)) { /* destination address for TCP writes was established on connection initiation */ sock->writes.addr_defined = false; @@ -2622,13 +3512,44 @@ if (from) { if (ret >= 0 && io->addr_defined) +#ifdef USE_PF_INET6 + { + /* TODO(jjo): streamline this mess */ + /* in this func we dont have relevant info about the PF_ of this + * endpoint, as link_socket_actual will be zero for the 1st received packet + * + * Test for inets PF_ possible sizes + */ + switch (io->addrlen) + { + case sizeof(struct sockaddr_in): + case sizeof(struct sockaddr_in6): + /* TODO(jjo): for some reason (?) I'm getting 24,28 for AF_INET6 */ + case sizeof(struct sockaddr_in6)-4: + break; + default: + bad_address_length (io->addrlen, af_addr_size(io->addr.sin_family)); + } + + switch (io->addr.sin_family) + { + case AF_INET: + from->dest.addr.in4 = io->addr; + break; + case AF_INET6: + from->dest.addr.in6 = io->addr6; + break; + } + } +#else { if (io->addrlen != sizeof (io->addr)) bad_address_length (io->addrlen, sizeof (io->addr)); - from->dest.sa = io->addr; + from->dest.addr.in4 = io->addr; } +#endif else - CLEAR (from->dest.sa); + CLEAR (from->dest.addr); } if (buf) diff -rauN openvpn-2.2.0/socket.h openvpn-2.2.0-debian/socket.h --- openvpn-2.2.0/socket.h 2011-04-06 18:04:54.000000000 +0200 +++ openvpn-2.2.0-debian/socket.h 2011-05-21 19:10:29.000000000 +0200 @@ -70,7 +70,13 @@ struct openvpn_sockaddr { /*int dummy;*/ /* add offset to force a bug if sa not explicitly dereferenced */ - struct sockaddr_in sa; + union { + struct sockaddr sa; + struct sockaddr_in in4; +#ifdef USE_PF_INET6 + struct sockaddr_in6 in6; +#endif + } addr; }; /* actual address of remote, based on source address of received packets */ @@ -79,7 +85,17 @@ /*int dummy;*/ /* add offset to force a bug if dest not explicitly dereferenced */ struct openvpn_sockaddr dest; #if ENABLE_IP_PKTINFO - struct in_pktinfo pi; + union { +#ifdef HAVE_IN_PKTINFO + struct in_pktinfo in4; +#endif +#ifdef IP_RECVDSTADDR + struct in_addr in4; +#endif +#ifdef USE_PF_INET6 + struct in6_pktinfo in6; +#endif + } pi; #endif }; @@ -199,6 +215,7 @@ # define SF_TCP_NODELAY (1<<1) # define SF_PORT_SHARE (1<<2) # define SF_HOST_RANDOMIZE (1<<3) +# define SF_GETADDRINFO_DGRAM (1<<4) unsigned int sockflags; /* for stream sockets */ @@ -351,6 +368,8 @@ #define IA_EMPTY_IF_UNDEF (1<<0) #define IA_NET_ORDER (1<<1) const char *print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc); +const char *print_in6_addr (struct in6_addr addr6, unsigned int flags, struct gc_arena *gc); +struct in6_addr add_in6_addr( struct in6_addr base, uint32_t add ); #define SA_IP_PORT (1<<0) #define SA_SET_IF_NONZERO (1<<1) @@ -371,6 +390,12 @@ void bad_address_length (int actual, int expected); +#ifdef USE_PF_INET6 +/* IPV4_INVALID_ADDR: returned by link_socket_current_remote() + * to ease redirect-gateway logic for ipv4 tunnels on ipv6 endpoints + */ +#define IPV4_INVALID_ADDR 0xffffffff +#endif in_addr_t link_socket_current_remote (const struct link_socket_info *info); void link_socket_connection_initiated (const struct buffer *buf, @@ -404,12 +429,21 @@ 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); socket_descriptor_t socket_do_accept (socket_descriptor_t sd, struct link_socket_actual *act, const bool nowait); +/* + * proto related + */ +bool proto_is_net(int proto); +bool proto_is_dgram(int proto); +bool proto_is_udp(int proto); +bool proto_is_tcp(int proto); + #if UNIX_SOCK_SUPPORT @@ -455,6 +489,11 @@ #define GETADDR_UPDATE_MANAGEMENT_STATE (1<<8) #define GETADDR_RANDOMIZE (1<<9) +/* [ab]use flags bits to get socktype info downstream */ +/* TODO(jjo): resolve tradeoff between hackiness|args-overhead */ +#define GETADDR_DGRAM (1<<10) +#define dnsflags_to_socktype(flags) ((flags & GETADDR_DGRAM) ? SOCK_DGRAM : SOCK_STREAM) + in_addr_t getaddr (unsigned int flags, const char *hostname, int resolve_retry_seconds, @@ -472,23 +511,38 @@ * Transport protocol naming and other details. */ -#define PROTO_UDPv4 0 -#define PROTO_TCPv4_SERVER 1 -#define PROTO_TCPv4_CLIENT 2 -#define PROTO_TCPv4 3 -#define PROTO_N 4 +/* + * Use enum's instead of #define to allow for easier + * optional proto support + */ +enum proto_num { + PROTO_NONE, /* catch for uninitialized */ + PROTO_UDPv4, + PROTO_TCPv4_SERVER, + PROTO_TCPv4_CLIENT, + PROTO_TCPv4, +#ifdef USE_PF_INET6 + PROTO_UDPv6, + PROTO_TCPv6_SERVER, + PROTO_TCPv6_CLIENT, + PROTO_TCPv6, +#endif + PROTO_N +}; int ascii2proto (const char* proto_name); const char *proto2ascii (int proto, bool display_form); const char *proto2ascii_all (struct gc_arena *gc); int proto_remote (int proto, bool remote); +const char *addr_family_name(int af); /* * Overhead added to packets by various protocols. */ #define IPv4_UDP_HEADER_SIZE 28 #define IPv4_TCP_HEADER_SIZE 40 -#define IPv6_UDP_HEADER_SIZE 40 +#define IPv6_UDP_HEADER_SIZE 48 +#define IPv6_TCP_HEADER_SIZE 60 extern const int proto_overhead[]; @@ -518,7 +572,7 @@ static inline bool link_socket_proto_connection_oriented (int proto) { - return proto == PROTO_TCPv4_SERVER || proto == PROTO_TCPv4_CLIENT; + return !proto_is_dgram(proto); } static inline bool @@ -533,7 +587,36 @@ static inline bool addr_defined (const struct openvpn_sockaddr *addr) { - return addr->sa.sin_addr.s_addr != 0; + if (!addr) return 0; + switch (addr->addr.sa.sa_family) { + case AF_INET: return addr->addr.in4.sin_addr.s_addr != 0; +#ifdef USE_PF_INET6 + case AF_INET6: return !IN6_IS_ADDR_UNSPECIFIED(&addr->addr.in6.sin6_addr); +#endif + default: return 0; + } +} +static inline bool +addr_defined_ipi (const struct link_socket_actual *lsa) +{ +#if ENABLE_IP_PKTINFO + if (!lsa) return 0; + switch (lsa->dest.addr.sa.sa_family) { +#ifdef HAVE_IN_PKTINFO + case AF_INET: return lsa->pi.in4.ipi_spec_dst.s_addr != 0; +#endif +#ifdef IP_RECVDSTADDR + case AF_INET: return lsa->pi.in4.s_addr != 0; +#endif +#ifdef USE_PF_INET6 + case AF_INET6: return !IN6_IS_ADDR_UNSPECIFIED(&lsa->pi.in6.ipi6_addr); +#endif + default: return 0; + } +#else + ASSERT(0); +#endif + return false; } static inline bool @@ -545,20 +628,50 @@ static inline bool addr_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2) { - return a1->sa.sin_addr.s_addr == a2->sa.sin_addr.s_addr; + switch(a1->addr.sa.sa_family) { + case AF_INET: + return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr; +#ifdef USE_PF_INET6 + case AF_INET6: + return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &a2->addr.in6.sin6_addr); +#endif + } + ASSERT(0); + return false; } static inline in_addr_t -addr_host (const struct openvpn_sockaddr *s) +addr_host (const struct openvpn_sockaddr *addr) { - return ntohl (s->sa.sin_addr.s_addr); + /* + * "public" addr returned is checked against ifconfig for + * possible clash: non sense for now given + * that we do ifconfig only IPv4 + */ +#if defined(USE_PF_INET6) + if(addr->addr.sa.sa_family != AF_INET) + return 0; +#else + ASSERT(addr->addr.sa.sa_family == AF_INET); +#endif + return ntohl (addr->addr.in4.sin_addr.s_addr); } static inline bool addr_port_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2) { - return a1->sa.sin_addr.s_addr == a2->sa.sin_addr.s_addr - && a1->sa.sin_port == a2->sa.sin_port; + switch(a1->addr.sa.sa_family) { + case AF_INET: + return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr + && a1->addr.in4.sin_port == a2->addr.in4.sin_port; +#ifdef USE_PF_INET6 + case AF_INET6: + return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &a2->addr.in6.sin6_addr) + && a1->addr.in6.sin6_port == a2->addr.in6.sin6_port; +#endif + } + ASSERT(0); + return false; } static inline bool @@ -571,6 +684,74 @@ : addr_port_match (a1, a2); } +static inline void +addr_zero_host(struct openvpn_sockaddr *addr) +{ + switch(addr->addr.sa.sa_family) { + case AF_INET: + addr->addr.in4.sin_addr.s_addr = 0; + break; +#ifdef USE_PF_INET6 + case AF_INET6: + memset(&addr->addr.in6.sin6_addr, 0, sizeof (struct in6_addr)); + break; +#endif + } +} + +static inline void +addr_copy_sa(struct openvpn_sockaddr *dst, const struct openvpn_sockaddr *src) +{ + dst->addr = src->addr; +} + +static inline void +addr_copy_host(struct openvpn_sockaddr *dst, const struct openvpn_sockaddr *src) +{ + switch(src->addr.sa.sa_family) { + case AF_INET: + dst->addr.in4.sin_addr.s_addr = src->addr.in4.sin_addr.s_addr; + break; +#ifdef USE_PF_INET6 + case AF_INET6: + dst->addr.in6.sin6_addr = src->addr.in6.sin6_addr; + break; +#endif + } +} + +static inline bool +addr_inet4or6(struct sockaddr *addr) +{ + return addr->sa_family == AF_INET || addr->sa_family == AF_INET6; +} + +int addr_guess_family(int proto, const char *name); +static inline int +af_addr_size(unsigned short af) +{ +#if defined(USE_PF_INET6) || defined (USE_PF_UNIX) + switch(af) { + case AF_INET: return sizeof (struct sockaddr_in); +#ifdef USE_PF_UNIX + case AF_UNIX: return sizeof (struct sockaddr_un); +#endif +#ifdef USE_PF_INET6 + case AF_INET6: return sizeof (struct sockaddr_in6); +#endif + default: +#if 0 + /* could be called from socket_do_accept() with empty addr */ + msg (M_ERR, "Bad address family: %d\n", af); + ASSERT(0); +#endif + return 0; + } +#else /* only AF_INET */ + return sizeof(struct sockaddr_in); +#endif +} + static inline bool link_socket_actual_match (const struct link_socket_actual *a1, const struct link_socket_actual *a2) { @@ -627,14 +808,18 @@ { if (buf->len > 0) { - if (from_addr->dest.sa.sin_family != AF_INET) - return false; - if (!link_socket_actual_defined (from_addr)) - return false; - if (info->remote_float || !addr_defined (&info->lsa->remote)) - return true; - if (addr_match_proto (&from_addr->dest, &info->lsa->remote, info->proto)) - return true; + switch (from_addr->dest.addr.sa.sa_family) { +#ifdef USE_PF_INET6 + case AF_INET6: +#endif + case AF_INET: + if (!link_socket_actual_defined (from_addr)) + return false; + if (info->remote_float || !addr_defined (&info->lsa->remote)) + return true; + if (addr_match_proto (&from_addr->dest, &info->lsa->remote, info->proto)) + return true; + } } return false; } @@ -740,7 +925,7 @@ int maxsize, struct link_socket_actual *from) { - if (sock->info.proto == PROTO_UDPv4) + if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */ { int res; @@ -751,10 +936,10 @@ #endif return res; } - else if (sock->info.proto == PROTO_TCPv4_SERVER || sock->info.proto == PROTO_TCPv4_CLIENT) + else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */ { /* from address was returned by accept */ - from->dest.sa = sock->info.lsa->actual.dest.sa; + addr_copy_sa(&from->dest, &sock->info.lsa->actual.dest); return link_socket_read_tcp (sock, buf); } else @@ -809,13 +994,14 @@ struct buffer *buf, struct link_socket_actual *to); - if (sock->sockflags & SF_USE_IP_PKTINFO) + if (proto_is_udp(sock->info.proto) && (sock->sockflags & SF_USE_IP_PKTINFO) + && addr_defined_ipi(to)) return link_socket_write_udp_posix_sendmsg (sock, buf, to); else #endif return sendto (sock->sd, BPTR (buf), BLEN (buf), 0, - (struct sockaddr *) &to->dest.sa, - (socklen_t) sizeof (to->dest.sa)); + (struct sockaddr *) &to->dest.addr.sa, + (socklen_t) af_addr_size(to->dest.addr.sa.sa_family)); } static inline int @@ -846,11 +1032,11 @@ struct buffer *buf, struct link_socket_actual *to) { - if (sock->info.proto == PROTO_UDPv4) + if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */ { return link_socket_write_udp (sock, buf, to); } - else if (sock->info.proto == PROTO_TCPv4_SERVER || sock->info.proto == PROTO_TCPv4_CLIENT) + else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */ { return link_socket_write_tcp (sock, buf, to); } diff -rauN openvpn-2.2.0/socks.c openvpn-2.2.0-debian/socks.c --- openvpn-2.2.0/socks.c 2011-04-06 18:05:52.000000000 +0200 +++ openvpn-2.2.0-debian/socks.c 2011-05-21 19:07:50.000000000 +0200 @@ -299,9 +299,9 @@ if (addr != NULL) { - addr->sa.sin_family = AF_INET; - addr->sa.sin_addr.s_addr = htonl (INADDR_ANY); - addr->sa.sin_port = htons (0); + addr->addr.in4.sin_family = AF_INET; + addr->addr.in4.sin_addr.s_addr = htonl (INADDR_ANY); + addr->addr.in4.sin_port = htons (0); } while (len < 4 + alen + 2) @@ -388,8 +388,8 @@ /* ATYP == 1 (IP V4 address) */ if (atyp == '\x01' && addr != NULL) { - memcpy (&addr->sa.sin_addr, buf + 4, sizeof (addr->sa.sin_addr)); - memcpy (&addr->sa.sin_port, buf + 8, sizeof (addr->sa.sin_port)); + memcpy (&addr->addr.in4.sin_addr, buf + 4, sizeof (addr->addr.in4.sin_addr)); + memcpy (&addr->addr.in4.sin_port, buf + 8, sizeof (addr->addr.in4.sin_port)); } @@ -507,8 +507,8 @@ if (atyp != 1) /* ATYP == 1 (IP V4) */ goto error; - buf_read (buf, &from->dest.sa.sin_addr, sizeof (from->dest.sa.sin_addr)); - buf_read (buf, &from->dest.sa.sin_port, sizeof (from->dest.sa.sin_port)); + buf_read (buf, &from->dest.addr.in4.sin_addr, sizeof (from->dest.addr.in4.sin_addr)); + buf_read (buf, &from->dest.addr.in4.sin_port, sizeof (from->dest.addr.in4.sin_port)); return; @@ -540,8 +540,8 @@ buf_write_u16 (&head, 0); /* RSV = 0 */ buf_write_u8 (&head, 0); /* FRAG = 0 */ buf_write_u8 (&head, '\x01'); /* ATYP = 1 (IP V4) */ - buf_write (&head, &to->dest.sa.sin_addr, sizeof (to->dest.sa.sin_addr)); - buf_write (&head, &to->dest.sa.sin_port, sizeof (to->dest.sa.sin_port)); + buf_write (&head, &to->dest.addr.in4.sin_addr, sizeof (to->dest.addr.in4.sin_addr)); + buf_write (&head, &to->dest.addr.in4.sin_port, sizeof (to->dest.addr.in4.sin_port)); return 10; } diff -rauN openvpn-2.2.0/syshead.h openvpn-2.2.0-debian/syshead.h --- openvpn-2.2.0/syshead.h 2011-04-21 21:13:34.000000000 +0200 +++ openvpn-2.2.0-debian/syshead.h 2011-05-21 19:07:50.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 #include +/* The following two headers are needed of USE_PF_INET6 */ +#include +#include #endif #ifdef HAVE_SYS_MMAN_H @@ -383,9 +390,10 @@ #endif /* - * Does this platform support linux-style IP_PKTINFO? + * Does this platform support linux-style IP_PKTINFO + * or bsd-style IP_RECVDSTADDR ? */ -#if defined(ENABLE_MULTIHOME) && defined(HAVE_IN_PKTINFO) && defined(IP_PKTINFO) && defined(HAVE_MSGHDR) && defined(HAVE_CMSGHDR) && defined(HAVE_IOVEC) && defined(CMSG_FIRSTHDR) && defined(CMSG_NXTHDR) && defined(HAVE_RECVMSG) && defined(HAVE_SENDMSG) +#if defined(ENABLE_MULTIHOME) && ((defined(HAVE_IN_PKTINFO)&&defined(IP_PKTINFO)) || defined(IP_RECVDSTADDR)) && defined(HAVE_MSGHDR) && defined(HAVE_CMSGHDR) && defined(HAVE_IOVEC) && defined(CMSG_FIRSTHDR) && defined(CMSG_NXTHDR) && defined(HAVE_RECVMSG) && defined(HAVE_SENDMSG) #define ENABLE_IP_PKTINFO 1 #else #define ENABLE_IP_PKTINFO 0 diff -rauN openvpn-2.2.0/tun.c openvpn-2.2.0-debian/tun.c --- openvpn-2.2.0/tun.c 2011-04-06 18:05:52.000000000 +0200 +++ openvpn-2.2.0-debian/tun.c 2011-05-21 19:10:29.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 #endif @@ -129,30 +130,6 @@ return dev; } -/* - * Called by the open_tun function of OSes to check if we - * explicitly support IPv6. - * - * In this context, explicit means that the OS expects us to - * do something special to the tun socket in order to support - * IPv6, i.e. it is not transparent. - * - * ipv6_explicitly_supported should be set to false if we don't - * have any explicit IPv6 code in the tun device handler. - * - * If ipv6_explicitly_supported is true, then we have explicit - * OS-specific tun dev code for handling IPv6. If so, tt->ipv6 - * is set according to the --tun-ipv6 command line option. - */ -static void -ipv6_support (bool ipv6, bool ipv6_explicitly_supported, struct tuntap* tt) -{ - tt->ipv6 = false; - if (ipv6_explicitly_supported) - tt->ipv6 = ipv6; - else if (ipv6) - msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS"); -} /* --ifconfig-nowarn disables some options sanity checking */ static const char ifconfig_warn_how_to_silence[] = "(silence this warning with --ifconfig-nowarn)"; @@ -423,6 +400,8 @@ int topology, /* one of the TOP_x values */ const char *ifconfig_local_parm, /* --ifconfig parm 1 */ const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */ + const char *ifconfig_ipv6_local_parm, /* --ifconfig parm 1 IPv6 */ + const char *ifconfig_ipv6_remote_parm, /* --ifconfig parm 2 IPv6 */ in_addr_t local_public, in_addr_t remote_public, const bool strict_warn, @@ -537,6 +516,40 @@ tt->did_ifconfig_setup = true; } + + if (ifconfig_ipv6_local_parm && ifconfig_ipv6_remote_parm) + { + const char *ifconfig_ipv6_local = NULL; + const char *ifconfig_ipv6_remote = NULL; + + /* + * Convert arguments to binary IPv6 addresses. + */ + + if ( inet_pton( AF_INET6, ifconfig_ipv6_local_parm, &tt->local_ipv6 ) != 1 || + inet_pton( AF_INET6, ifconfig_ipv6_remote_parm, &tt->remote_ipv6 ) != 1 ) + { + msg( M_FATAL, "init_tun: problem converting IPv6 ifconfig addresses %s and %s to binary", ifconfig_ipv6_local_parm, ifconfig_ipv6_remote_parm ); + } + tt->netbits_ipv6 = 64; + + /* + * Set ifconfig parameters + */ + ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc); + ifconfig_ipv6_remote = print_in6_addr (tt->remote_ipv6, 0, &gc); + + /* + * Set environmental variables with ifconfig parameters. + */ + if (es) + { + setenv_str (es, "ifconfig_ipv6_local", ifconfig_ipv6_local); + setenv_str (es, "ifconfig_ipv6_remote", ifconfig_ipv6_remote); + } + tt->did_ifconfig_ipv6_setup = true; + } + gc_free (&gc); return tt; } @@ -559,6 +572,28 @@ #endif } +#if defined(TARGET_WIN32) || \ + defined(TARGET_DARWIN) || defined(TARGET_NETBSD) || defined(TARGET_OPENBSD) + +/* some of the platforms will auto-add a "network route" pointing + * to the interface on "ifconfig tunX 2001:db8::1/64", others need + * an extra call to "route add..." + * -> helper function to simplify code below + */ +void add_route_connected_v6_net(struct tuntap * tt, + const struct env_set *es) +{ + struct route_ipv6 r6; + + r6.defined = true; + r6.network = tt->local_ipv6; + r6.netbits = tt->netbits_ipv6; + r6.gateway = tt->local_ipv6; + add_route_ipv6 (&r6, tt, 0, es); +} +#endif + + /* execute the ifconfig command through the shell */ void do_ifconfig (struct tuntap *tt, @@ -574,10 +609,16 @@ const char *ifconfig_local = NULL; const char *ifconfig_remote_netmask = NULL; const char *ifconfig_broadcast = NULL; + const char *ifconfig_ipv6_local = NULL; + const char *ifconfig_ipv6_remote = NULL; + bool do_ipv6 = false; struct argv argv; argv_init (&argv); + msg( M_INFO, "do_ifconfig, tt->ipv6=%d, tt->did_ifconfig_ipv6_setup=%d", + tt->ipv6, tt->did_ifconfig_ipv6_setup ); + /* * We only handle TUN/TAP devices here, not --dev null devices. */ @@ -589,6 +630,13 @@ ifconfig_local = print_in_addr_t (tt->local, 0, &gc); ifconfig_remote_netmask = print_in_addr_t (tt->remote_netmask, 0, &gc); + if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup ) + { + ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc); + ifconfig_ipv6_remote = print_in6_addr (tt->remote_ipv6, 0, &gc); + do_ipv6 = true; + } + /* * If TAP-style device, generate broadcast address. */ @@ -647,7 +695,19 @@ argv_msg (M_INFO, &argv); openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed"); } - tt->did_ifconfig = true; + if ( do_ipv6 ) + { + argv_printf( &argv, + "%s -6 addr add %s/%d dev %s", + iproute_path, + ifconfig_ipv6_local, + tt->netbits_ipv6, + actual + ); + argv_msg (M_INFO, &argv); + openvpn_execve_check (&argv, es, S_FATAL, "Linux ip -6 addr add failed"); + } + tt->did_ifconfig = true; #else if (tun) argv_printf (&argv, @@ -670,6 +730,18 @@ ); argv_msg (M_INFO, &argv); openvpn_execve_check (&argv, es, S_FATAL, "Linux ifconfig failed"); + if ( do_ipv6 ) + { + argv_printf (&argv, + "%s %s inet6 add %s/%d", + IFCONFIG_PATH, + actual, + ifconfig_ipv6_local, + tt->netbits_ipv6 + ); + argv_msg (M_INFO, &argv); + openvpn_execve_check (&argv, es, S_FATAL, "Linux ifconfig inet6 failed"); + } tt->did_ifconfig = true; #endif /*CONFIG_FEATURE_IPROUTE*/ @@ -693,7 +765,7 @@ argv_msg (M_INFO, &argv); if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-1 failed")) - solaris_error_close (tt, es, actual); + solaris_error_close (tt, es, actual, false); argv_printf (&argv, "%s %s netmask 255.255.255.255", @@ -725,7 +797,53 @@ argv_msg (M_INFO, &argv); if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-2 failed")) - solaris_error_close (tt, es, actual); + solaris_error_close (tt, es, actual, false); + + if ( do_ipv6 ) + { + argv_printf (&argv, "%s %s inet6 unplumb", + IFCONFIG_PATH, actual ); + argv_msg (M_INFO, &argv); + openvpn_execve_check (&argv, es, 0, NULL); + + if ( tt->type == DEV_TYPE_TUN ) + { + argv_printf (&argv, + "%s %s inet6 plumb %s/%d %s up", + IFCONFIG_PATH, + actual, + ifconfig_ipv6_local, + tt->netbits_ipv6, + ifconfig_ipv6_remote + ); + } + else /* tap mode */ + { + /* base IPv6 tap interface needs to be brought up first + */ + argv_printf (&argv, "%s %s inet6 plumb up", + IFCONFIG_PATH, actual ); + argv_msg (M_INFO, &argv); + if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig IPv6 (prepare) failed")) + solaris_error_close (tt, es, actual, true); + + /* we might need to do "ifconfig %s inet6 auto-dhcp drop" + * after the system has noticed the interface and fired up + * the DHCPv6 client - but this takes quite a while, and the + * server will ignore the DHCPv6 packets anyway. So we don't. + */ + + /* static IPv6 addresses need to go to a subinterface (tap0:1) + */ + argv_printf (&argv, + "%s %s inet6 addif %s/%d up", + IFCONFIG_PATH, actual, + ifconfig_ipv6_local, tt->netbits_ipv6 ); + } + argv_msg (M_INFO, &argv); + if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig IPv6 failed")) + solaris_error_close (tt, es, actual, true); + } if (!tun && tt->topology == TOP_SUBNET) { @@ -787,10 +905,42 @@ ); argv_msg (M_INFO, &argv); openvpn_execve_check (&argv, es, S_FATAL, "OpenBSD ifconfig failed"); + if ( do_ipv6 ) + { + argv_printf (&argv, + "%s %s inet6 %s/%d", + IFCONFIG_PATH, + actual, + ifconfig_ipv6_local, + tt->netbits_ipv6 + ); + argv_msg (M_INFO, &argv); + openvpn_execve_check (&argv, es, S_FATAL, "OpenBSD ifconfig inet6 failed"); + + /* and, hooray, we explicitely need to add a route... */ + add_route_connected_v6_net(tt, es); + } tt->did_ifconfig = true; #elif defined(TARGET_NETBSD) +/* whether or not NetBSD can do IPv6 can be seen by the availability of + * the TUNSIFHEAD ioctl() - see next TARGET_NETBSD block for more details + */ +#ifdef TUNSIFHEAD +# define NETBSD_MULTI_AF +#endif + + /* as on OpenBSD and Darwin, destroy and re-create tun interface + */ + argv_printf (&argv, "%s %s destroy", IFCONFIG_PATH, actual ); + argv_msg (M_INFO, &argv); + openvpn_execve_check (&argv, es, 0, "NetBSD ifconfig destroy failed"); + + argv_printf (&argv, "%s %s create", IFCONFIG_PATH, actual ); + argv_msg (M_INFO, &argv); + openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig create failed"); + if (tun) argv_printf (&argv, "%s %s %s %s mtu %d netmask 255.255.255.255 up", @@ -817,6 +967,27 @@ ); argv_msg (M_INFO, &argv); openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig failed"); + + if ( do_ipv6 ) + { +#ifdef NETBSD_MULTI_AF + argv_printf (&argv, + "%s %s inet6 %s/%d", + IFCONFIG_PATH, + actual, + ifconfig_ipv6_local, + tt->netbits_ipv6 + ); + argv_msg (M_INFO, &argv); + openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig inet6 failed"); + + /* and, hooray, we explicitely need to add a route... */ + add_route_connected_v6_net(tt, es); +#else + msg( M_INFO, "no IPv6 support for tun interfaces on NetBSD before 4.0 (if your system is newer, recompile openvpn)" ); + tt->ipv6 = false; +#endif + } tt->did_ifconfig = true; #elif defined(TARGET_DARWIN) @@ -882,6 +1053,22 @@ add_route (&r, tt, 0, es); } + if ( do_ipv6 ) + { + argv_printf (&argv, + "%s %s inet6 %s/%d", + IFCONFIG_PATH, + actual, + ifconfig_ipv6_local, + tt->netbits_ipv6 + ); + argv_msg (M_INFO, &argv); + openvpn_execve_check (&argv, es, S_FATAL, "MacOS X ifconfig inet6 failed"); + + /* and, hooray, we explicitely need to add a route... */ + add_route_connected_v6_net(tt, es); + } + #elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY) /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */ @@ -920,6 +1107,19 @@ add_route (&r, tt, 0, es); } + if ( do_ipv6 ) + { + argv_printf (&argv, + "%s %s inet6 %s/%d", + IFCONFIG_PATH, + actual, + ifconfig_ipv6_local, + tt->netbits_ipv6 + ); + argv_msg (M_INFO, &argv); + openvpn_execve_check (&argv, es, S_FATAL, "FreeBSD ifconfig inet6 failed"); + } + #elif defined (WIN32) { /* @@ -959,6 +1159,34 @@ tt->did_ifconfig = true; } + /* IPv6 always uses "netsh" interface */ + if ( do_ipv6 ) + { + char * saved_actual; + + if (!strcmp (actual, "NULL")) + msg (M_FATAL, "Error: When using --tun-ipv6, if you have more than one TAP-Win32 adapter, you must also specify --dev-node"); + + /* example: netsh interface ipv6 add address MyTap 2001:608:8003::d */ + argv_printf (&argv, + "%s%sc interface ipv6 add address %s %s", + get_win_sys_path(), + NETSH_PATH_SUFFIX, + actual, + ifconfig_ipv6_local ); + + netsh_command (&argv, 4); + + /* explicit route needed */ + /* on windows, OpenVPN does ifconfig first, open_tun later, so + * tt->actual_name might not yet be initialized, but routing code + * needs to know interface name - point to "actual", restore later + */ + saved_actual = tt->actual_name; + tt->actual_name = (char*) actual; + add_route_connected_v6_net(tt, es); + tt->actual_name = saved_actual; + } #else msg (M_FATAL, "Sorry, but I don't know how to do 'ifconfig' commands on this operating system. You should ifconfig your TUN/TAP device manually or use an --up script."); #endif @@ -991,14 +1219,16 @@ #ifndef WIN32 static void open_tun_generic (const char *dev, const char *dev_type, const char *dev_node, - bool ipv6, bool ipv6_explicitly_supported, bool dynamic, + bool ipv6_explicitly_supported, bool dynamic, struct tuntap *tt) { char tunname[256]; char dynamic_name[256]; bool dynamic_opened = false; - ipv6_support (ipv6, ipv6_explicitly_supported, tt); + + if ( tt->ipv6 && ! ipv6_explicitly_supported ) + msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS"); if (tt->type == DEV_TYPE_NULL) { @@ -1094,16 +1324,16 @@ #if !PEDANTIC void -open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt) +open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { struct ifreq ifr; - /* - * Set tt->ipv6 to true if - * (a) we have the capability of supporting --tun-ipv6, and - * (b) --tun-ipv6 was specified. + /* warn if a very old linux version is used & --tun-ipv6 set */ - ipv6_support (ipv6, LINUX_IPV6, tt); +#if LINUX_IPV6 == 0 + if ( tt->ipv6 ) + msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS"); +#endif /* * We handle --dev null specially, we do not open /dev/null for this. @@ -1215,13 +1445,13 @@ close (tt->fd); tt->fd = -1; } - open_tun_generic (dev, dev_type, dev_node, ipv6, false, true, tt); + open_tun_generic (dev, dev_type, dev_node, false, true, tt); } #else void -open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt) +open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { ASSERT (0); } @@ -1231,9 +1461,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 */ @@ -1253,7 +1483,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; @@ -1261,7 +1491,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) @@ -1404,7 +1634,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; @@ -1415,7 +1645,10 @@ bool is_tun; struct strioctl strioc_if, strioc_ppa; - ipv6_support (ipv6, true, tt); + /* improved generic TUN/TAP driver from + * http://www.whiteboard.ne.jp/~admin2/tuntap/ + * has IPv6 support + */ memset(&ifr, 0x0, sizeof(ifr)); if (tt->type == DEV_TYPE_NULL) @@ -1570,6 +1803,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; @@ -1622,11 +1867,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, @@ -1683,9 +1937,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,7 +1951,9 @@ strerror(errno)); } +#ifdef IFF_MULTICAST /* openbsd 4.x doesn't have this */ info.flags |= IFF_MULTICAST; +#endif if (ioctl (tt->fd, TUNSIFINFO, &info) < 0) { msg (M_WARN | M_ERRNO, "Can't set interface info: %s", @@ -1706,12 +1962,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); } } @@ -1774,33 +2049,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); @@ -1808,6 +2101,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) { @@ -1819,6 +2171,7 @@ { return read (tt->fd, buf, len); } +#endif /* NETBSD_MULTI_AF */ #elif defined(TARGET_FREEBSD) @@ -1832,9 +2185,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) { @@ -1920,9 +2273,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) { @@ -1991,6 +2344,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 @@ -3976,7 +4384,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]; @@ -3987,7 +4395,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) { @@ -4109,6 +4517,16 @@ msg (M_FATAL, "ERROR: This version of " PACKAGE_NAME " requires a TAP-Win32 driver that is at least version %d.%d -- If you recently upgraded your " PACKAGE_NAME " distribution, a reboot is probably required at this point to get Windows to see the new driver.", TAP_WIN32_MIN_MAJOR, TAP_WIN32_MIN_MINOR); + + /* usage of numeric constants is ugly, but this is really tied to + * *this* version of the driver + */ + if ( tt->ipv6 && tt->type == DEV_TYPE_TUN && + info[0] == 9 && info[1] < 8) + { + msg( M_INFO, "WARNING: Tap-Win32 driver version %d.%d does not support IPv6 in TUN mode. IPv6 will be disabled. Upgrade to Tap-Win32 9.8 (2.2-beta3 release or later) or use TAP mode to get IPv6", (int) info[0], (int) info[1] ); + tt->ipv6 = false; + } } /* get driver MTU */ @@ -4433,6 +4851,12 @@ if (tt) { + if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup ) + { + /* netsh interface ipv6 delete address \"%s\" %s */ + const char * ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc); + msg( M_WARN, "TODO: remove IPv6 address %s", ifconfig_ipv6_local ); + } #if 1 if (tt->ipapi_context_defined) { @@ -4536,9 +4960,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 -rauN openvpn-2.2.0/tun.h openvpn-2.2.0-debian/tun.h --- openvpn-2.2.0/tun.h 2011-04-06 18:04:54.000000000 +0200 +++ openvpn-2.2.0-debian/tun.h 2011-05-21 19:10:29.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 -rauN openvpn-2.2.0/win32.c openvpn-2.2.0-debian/win32.c --- openvpn-2.2.0/win32.c 2011-04-14 21:10:29.000000000 +0200 +++ openvpn-2.2.0-debian/win32.c 2011-05-21 19:10:29.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 -rauN openvpn-2.2.0/win32.h openvpn-2.2.0-debian/win32.h --- openvpn-2.2.0/win32.h 2011-04-14 21:10:29.000000000 +0200 +++ openvpn-2.2.0-debian/win32.h 2011-05-21 19:10:29.000000000 +0200 @@ -195,7 +195,10 @@ DWORD flags; int status; bool addr_defined; - struct sockaddr_in addr; + union { + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + }; int addrlen; struct buffer buf_init; struct buffer buf; @@ -269,6 +272,8 @@ /* call self in a subprocess */ void fork_to_self (const char *cmdline); +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); +int inet_pton(int af, const char *src, void *st); /* Find temporary directory */ const char *win_get_tempdir();