--- README.ipv6	1970-01-01 01:00:00.000000000 +0100
+++ README.ipv6	2010-02-22 00:35:18.000000000 +0100
@@ -0,0 +1,81 @@
+[ Last updated: 16-Nov-2009. ]
+
+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.6
+    - udp4->upd6,tcp4->tcp6 (ipv4/6 mapped): GNU/Linux
+      (gives a warning on local!=remote proto matching)
+  o NOT:
+    - win32: tcp4->tcp6 (ipv4/6 mapped) fails w/connection refused
+  o NOT tested:
+    - mgmt console
+
+* Build setup:
+  ./configure --enable-ipv6        (by default)
+
+* Usage:
+  For IPv6 just specify "-p upd6" an proper IPv6 hostnames, adapting the example
+  from man page ...
+
+  On may:
+    openvpn --proto udp6 --remote <june_IPv6_addr> --dev tun1 \
+      --ifconfig 10.4.0.1 10.4.0.2 --verb 5 --secret key
+
+  On june:
+    openvpn --proto udp6 --remote <may_IPv6_addr>  --dev tun1 \
+      --ifconfig 10.4.0.2 10.4.0.1 --verb 5 --secret key
+
+  Same for --proto tcp6-client, tcp6-server.
+
+* Main code changes summary:
+  - socket.h: New struct openvpn_sockaddr type that holds sockaddrs and pktinfo,
+    (here I omitted #ifdef USE_PF_xxxx, see socket.h )
+
+    struct openvpn_sockaddr {
+    	union {
+    		struct sockaddr sa;
+    		struct sockaddr_in in;
+    		struct sockaddr_in6 in6;
+    	} addr;
+    };
+
+    struct link_socket_addr
+    {
+            struct openvpn_sockaddr local;
+            struct openvpn_sockaddr remote;
+            struct openvpn_sockaddr actual;
+    };
+
+    PRO: allows simple type overloading: local.addr.sa, local.addr.in, local.addr.in6 ... etc
+    (also local.pi.in and local.pi.in6)
+
+  - several function prototypes moved from sockaddr_in to openvpn_sockaddr
+  - several new sockaddr functions needed to "generalize" AF_xxxx operations:
+    addr_copy(), addr_zero(), ...etc
+    proto_is_udp(), proto_is_dgram(), proto_is_net()
+
+* TODO: See TODO.ipv6
+
+--
+JuanJo Ciarlante   jjo () google () com ............................
+:                                                                  :
+.                                         Linux IP Aliasing author .
+.   Modular algo (AES et all) support for FreeSWAN/OpenSWAN author .
+.                                        OpenVPN over IPv6 support .
+:......     plus other scattered free software bits in the wild ...:
--- TODO.ipv6	1970-01-01 01:00:00.000000000 +0100
+++ TODO.ipv6	2010-02-22 00:35:18.000000000 +0100
@@ -0,0 +1,30 @@
+[ Last updated: 11-Nov-2009. ]
+
+* All platforms:
+  o mgmt console: as currently passes straight in_addr_t bits around
+
+  o make possible to get AF from getaddrinfo() answer, ie allow openvpn to
+    use ipv4/6 if DNS returns A/AAAA without specifying protocol.
+    Hard: requires deep changes in initialization/calling logic
+
+  o use AI_PASSIVE
+
+  o the getaddr()/getaddr6() interface is not prepared for handling socktype
+    "tagging", currently I abuse the sockflags bits for getting the ai_socktype
+    downstream.
+
+  o implement comparison for mapped addesses: server in dual stack
+    listening IPv6 must permit incoming streams from allowed IPv4 peer,
+    currently you need to pass eg:  --remote ffff::1.2.3.4
+
+  o do something with multi mode learn routes, for now just ignoring
+    ipv6 addresses seems the most sensible thing to do, because there's
+    no support for intra-tunnel ipv6 stuff.
+
+* win32:
+  o find out about mapped addresses, as I can't make it work
+    with bound at ::1 and connect to 127.0.0.1
+
+* N/A:
+  o this is ipv6 *endpoint* support, so don't expect "ifconfig6"-like
+    support in this patch 
--- buffer.c	2009-10-01 20:02:18.000000000 +0200
+++ buffer.c	2010-02-22 00:35:18.000000000 +0100
@@ -215,6 +215,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.
--- buffer.h	2009-10-01 20:02:18.000000000 +0200
+++ buffer.h	2010-02-22 00:35:18.000000000 +0100
@@ -278,6 +278,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, ...)
--- config.h.in	2009-12-12 00:27:04.000000000 +0100
+++ config.h.in	2010-02-22 00:35:26.000000000 +0100
@@ -529,6 +529,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
 
--- configure	2009-12-12 00:27:03.000000000 +0100
+++ configure	2010-02-22 00:35:26.000000000 +0100
@@ -1323,6 +1323,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)
@@ -2553,6 +2554,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"
@@ -9815,6 +9825,73 @@
 
 LDFLAGS="$OLDLDFLAGS"
 
+if test "$PF_INET6" = "yes"; then
+  { echo "$as_me:$LINENO: checking for struct sockaddr_in6 for IPv6 support..." >&5
+echo "$as_me: checking for struct sockaddr_in6 for IPv6 support..." >&6;}
+  { echo "$as_me:$LINENO: checking for struct sockaddr_in6" >&5
+echo $ECHO_N "checking for struct sockaddr_in6... $ECHO_C" >&6; }
+if test "${ac_cv_type_struct_sockaddr_in6+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include "syshead.h"
+
+typedef struct sockaddr_in6 ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+  return 0;
+if (sizeof (ac__type_new_))
+  return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_type_struct_sockaddr_in6=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_cv_type_struct_sockaddr_in6=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_struct_sockaddr_in6" >&5
+echo "${ECHO_T}$ac_cv_type_struct_sockaddr_in6" >&6; }
+if test $ac_cv_type_struct_sockaddr_in6 = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define USE_PF_INET6 1
+_ACEOF
+
+fi
+
+fi
+
 
 if test "$MEMCHECK" = "valgrind"; then
    { echo "$as_me:$LINENO: checking for valgrind tool and Header files..." >&5
--- configure.ac	2009-10-01 20:02:18.000000000 +0200
+++ configure.ac	2010-02-22 00:35:18.000000000 +0100
@@ -135,6 +135,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"],
@@ -550,6 +556,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
--- init.c	2009-10-01 20:02:18.000000000 +0200
+++ init.c	2010-02-22 00:35:18.000000000 +0100
@@ -94,7 +94,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;
@@ -933,7 +933,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,
@@ -1335,7 +1340,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;
@@ -1430,13 +1435,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;
     }
@@ -2572,7 +2586,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
 	{
@@ -2834,7 +2848,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;
@@ -3117,17 +3135,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 ();
 
@@ -3233,7 +3242,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);
 }
 
--- manage.c	2009-10-01 22:30:21.000000000 +0200
+++ manage.c	2010-02-22 00:35:18.000000000 +0100
@@ -1877,9 +1877,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
@@ -1891,7 +1891,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);
 	    }
 	}
@@ -2258,7 +2258,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);
     }
 
--- mroute.c	2009-10-01 20:02:18.000000000 +0200
+++ mroute.c	2010-02-22 00:35:18.000000000 +0100
@@ -226,25 +226,47 @@
 				      const struct openvpn_sockaddr *osaddr,
 				      bool use_port)
 {
-  if (osaddr->sa.sin_family == AF_INET)
+  switch (osaddr->addr.sa.sa_family) 
+  {
+    case AF_INET:
     {
       if (use_port)
 	{
 	  addr->type = MR_ADDR_IPV4 | MR_WITH_PORT;
 	  addr->netbits = 0;
 	  addr->len = 6;
-	  memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
-	  memcpy (addr->addr + 4, &osaddr->sa.sin_port, 2);
+	  memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4);
+	  memcpy (addr->addr + 4, &osaddr->addr.in4.sin_port, 2);
 	}
       else
 	{
 	  addr->type = MR_ADDR_IPV4;
 	  addr->netbits = 0;
 	  addr->len = 4;
-	  memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
+	  memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4);
 	}
       return true;
     }
+#ifdef USE_PF_INET6
+    case AF_INET6:
+      if (use_port)
+	{
+	  addr->type = MR_ADDR_IPV6 | MR_WITH_PORT;
+	  addr->netbits = 0;
+	  addr->len = 18;
+	  memcpy (addr->addr, &osaddr->addr.in6.sin6_addr, 16);
+	  memcpy (addr->addr + 16, &osaddr->addr.in6.sin6_port, 2);
+	}
+      else
+	{
+	  addr->type = MR_ADDR_IPV6;
+	  addr->netbits = 0;
+	  addr->len = 16;
+	  memcpy (addr->addr, &osaddr->addr.in6.sin6_addr, 16);
+	}
+      return true;
+#endif
+  }
   return false;
 }
 
--- mtcp.c	2009-10-01 20:02:18.000000000 +0200
+++ mtcp.c	2010-02-22 00:35:18.000000000 +0100
@@ -153,6 +153,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");
--- multi.c	2009-10-25 01:17:29.000000000 +0200
+++ multi.c	2010-02-22 00:35:18.000000000 +0100
@@ -1066,8 +1066,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)
@@ -2489,9 +2489,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, true);
@@ -2642,16 +2642,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
--- occ.c	2009-10-01 20:02:18.000000000 +0200
+++ occ.c	2010-02-22 00:35:18.000000000 +0100
@@ -369,7 +369,7 @@
 	       c->c2.max_send_size_remote,
 	       c->c2.max_recv_size_local);
 	  if (!c->options.fragment
-	      && c->options.ce.proto == PROTO_UDPv4
+	      && (proto_is_dgram(c->options.ce.proto))
 	      && c->c2.max_send_size_local > TUN_MTU_MIN
 	      && (c->c2.max_recv_size_remote < c->c2.max_send_size_local
 		  || c->c2.max_recv_size_local < c->c2.max_send_size_remote))
--- openvpn.8	2009-12-11 09:04:24.000000000 +0100
+++ openvpn.8	2010-02-22 00:35:18.000000000 +0100
@@ -5333,13 +5333,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
@@ -5351,7 +5354,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
@@ -5363,6 +5366,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
--- options.c	2009-12-11 09:09:39.000000000 +0100
+++ options.c	2010-02-22 00:35:18.000000000 +0100
@@ -73,6 +73,12 @@
 #ifdef ENABLE_PKCS11
   " [PKCS11]"
 #endif
+#ifdef ENABLE_IP_PKTINFO
+  " [MH]"
+#endif
+#ifdef USE_PF_INET6
+  " [PF_INET6]"
+#endif
   " built on " __DATE__
 ;
 
@@ -94,6 +100,9 @@
   "--mode m        : Major mode, m = 'p2p' (default, point-to-point) or 'server'.\n"
   "--proto p       : Use protocol p for communicating with peer.\n"
   "                  p = udp (default), tcp-server, or tcp-client\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"
@@ -1538,11 +1547,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
@@ -1551,7 +1576,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
 
@@ -1564,7 +1589,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");
   
@@ -1629,16 +1655,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
@@ -1656,7 +1686,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
@@ -1670,11 +1705,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");
@@ -1702,9 +1754,17 @@
 	msg (M_USAGE, "--inetd cannot be used with --mode server");
       if (options->ipchange)
 	msg (M_USAGE, "--ipchange cannot be used with --mode server (use --client-connect instead)");
-      if (!(ce->proto == PROTO_UDPv4 || ce->proto == PROTO_TCPv4_SERVER))
-	msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server");
-      if (ce->proto != PROTO_UDPv4 && (options->cf_max || options->cf_per))
+      if (!(proto_is_dgram(ce->proto) || ce->proto == PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+	    || ce->proto == PROTO_TCPv6_SERVER
+#endif
+	    ))
+	msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server"
+#ifdef USE_PF_INET6
+	    " or --proto tcp6-server"
+#endif
+	     );
+      if (!proto_is_udp(ce->proto) && (options->cf_max || options->cf_per))
 	msg (M_USAGE, "--connect-freq only works with --mode server --proto udp.  Try --max-clients instead.");
       if (!(dev == DEV_TYPE_TAP || (dev == DEV_TYPE_TUN && options->topology == TOP_SUBNET)) && options->ifconfig_pool_netmask)
 	msg (M_USAGE, "The third parameter to --ifconfig-pool (netmask) is only valid in --dev tap mode");
@@ -1797,7 +1857,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");
@@ -1966,6 +2026,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
 
--- ps.c	2009-10-01 20:02:18.000000000 +0200
+++ ps.c	2010-02-22 00:35:18.000000000 +0100
@@ -332,9 +332,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
--- route.c	2009-10-01 20:02:18.000000000 +0200
+++ route.c	2010-02-22 00:35:18.000000000 +0100
@@ -572,13 +572,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 */
--- socket.c	2009-12-11 05:59:45.000000000 +0100
+++ socket.c	2010-02-22 00:35:18.000000000 +0100
@@ -37,23 +37,28 @@
 #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
 };
 
 /*
  * Convert sockflags/getaddr_flags into getaddr_flags
  */
 static unsigned int
-sf2gaf(const unsigned int getaddr_flags,
+sf2gaf(unsigned int getaddr_flags,
        const unsigned int sockflags)
 {
-  if (sockflags & SF_HOST_RANDOMIZE)
-    return getaddr_flags | GETADDR_RANDOMIZE;
-  else
-    return getaddr_flags;
+  getaddr_flags |= (sockflags & SF_GETADDRINFO_DGRAM) ? GETADDR_DGRAM : 0;
+  getaddr_flags |= (sockflags & SF_HOST_RANDOMIZE) ? GETADDR_RANDOMIZE : 0;
+  return getaddr_flags;
 }
 
 /*
@@ -247,6 +252,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.
@@ -381,20 +581,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
@@ -587,6 +820,44 @@
   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;
+      setsockopt (sd, IPPROTO_IPV6, IPV6_PKTINFO, (void*)&pad, sizeof(pad));
+    }
+#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)
 {
@@ -594,6 +865,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)
@@ -605,6 +877,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);
@@ -642,7 +926,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);
@@ -650,7 +939,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");
@@ -663,7 +952,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 */
@@ -679,7 +968,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);
@@ -780,7 +1070,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",
@@ -801,7 +1091,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)
@@ -859,7 +1149,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
@@ -937,7 +1227,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);
@@ -1002,15 +1305,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 */
@@ -1033,14 +1375,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)
 	    {
@@ -1083,13 +1443,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,
@@ -1109,8 +1487,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? */
@@ -1227,7 +1616,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;
     }
@@ -1351,7 +1744,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)
 	    {
@@ -1386,7 +1783,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
@@ -1473,8 +1874,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);
 
@@ -1489,7 +1890,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);
 	}
     }
 
@@ -1678,13 +2079,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);
 }
 
@@ -1699,10 +2107,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;
 }
@@ -1929,28 +2352,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]");
+	  mutex_lock_static (L_INET_NTOA);
+	  buf_puts (&out, (addr_is_defined ? inet_ntoa (addr->addr.in4.sin_addr) : "[undef]"));
+	  mutex_unlock_static (L_INET_NTOA);
 
-      mutex_lock_static (L_INET_NTOA);
-      if (!(flags & PS_DONT_SHOW_ADDR))
-	buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]"));
-      mutex_unlock_static (L_INET_NTOA);
+	  if (((flags & PS_SHOW_PORT) || (addr_is_defined && (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 *
@@ -1970,12 +2426,40 @@
       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_addr = act->pi.in4.ipi_spec_dst;
+		  buf_printf (&out, " (via %s)", print_sockaddr_ex (&sa, separator, 0, gc));
+		}
+#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 (getnameinfo((struct sockaddr *)&sin6, sizeof (struct sockaddr_in6),
+				      buf, sizeof (buf), NULL, 0, NI_NUMERICHOST) == 0)
+			buf_printf (&out, " (via %s)", buf);
+		      else
+			buf_printf (&out, " (via [getnameinfo() err])");
+		    }
+		}
+	      break;
+	    }
+#endif /* USE_PF_INET6 */
+
 	}
 #endif
       return BSTR (&out);
@@ -2012,20 +2496,39 @@
 {
   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);
 
-  mutex_lock_static (L_INET_NTOA);
-  setenv_str (es, name_buf, inet_ntoa (addr->sa.sin_addr));
-  mutex_unlock_static (L_INET_NTOA);
+      mutex_lock_static (L_INET_NTOA);
+      setenv_str (es, name_buf, inet_ntoa (addr->addr.in4.sin_addr));
+      mutex_unlock_static (L_INET_NTOA);
+
+      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->sa.sin_port)
-    {
       openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix);
-      setenv_int (es, name_buf, ntohs (addr->sa.sin_port));
+      setenv_int (es, name_buf, ntohs (addr->addr.in6.sin6_port));
+      break;
     }
+#endif
 }
 
 void
@@ -2035,7 +2538,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);
     }
 }
@@ -2056,16 +2560,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)
 {
@@ -2105,6 +2656,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
@@ -2119,10 +2709,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;
 }
@@ -2181,10 +2776,24 @@
 #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;
+  struct in_pktinfo pi;
+};
+#ifdef USE_PF_INET6
+struct openvpn_in6_pktinfo
+{
+  struct cmsghdr cmsghdr;
+  struct in6_pktinfo pi6;
+};
+#endif
+
+union openvpn_pktinfo {
+	struct openvpn_in4_pktinfo cmsgpi;
+#ifdef USE_PF_INET6
+	struct openvpn_in6_pktinfo cmsgpi6;
+#endif
 };
 #pragma pack()
 
@@ -2195,15 +2804,15 @@
 				    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);
@@ -2220,9 +2829,21 @@
 	  && cmsg->cmsg_len >= sizeof (opi))
 	{
 	  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;
+	}
+#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;
 }
@@ -2234,18 +2855,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;
 }
 
@@ -2282,26 +2905,54 @@
   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 opi;
+        struct in_pktinfo *pkti;
+        mesg.msg_name = &to->dest.addr.sa;
+        mesg.msg_namelen = sizeof (struct sockaddr_in);
+        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.in4.ipi_ifindex;
+        pkti->ipi_spec_dst = to->pi.in4.ipi_spec_dst;
+        pkti->ipi_addr.s_addr = 0;
+        break;
+      }
+#ifdef USE_PF_INET6
+    case AF_INET6:
+      {
+        struct openvpn_in6_pktinfo opi6;
+        struct in6_pktinfo *pkti6;
+        mesg.msg_name = &to->dest.addr.sa;
+        mesg.msg_namelen = sizeof (struct sockaddr_in6);
+        mesg.msg_control = &opi6;
+        mesg.msg_controllen = sizeof (opi6);
+        mesg.msg_flags = 0;
+        cmsg = CMSG_FIRSTHDR (&mesg);
+        cmsg->cmsg_len = sizeof (opi6);
+        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);
 }
 
@@ -2322,11 +2973,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);
 	}
@@ -2346,10 +2997,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,
@@ -2361,7 +3017,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(
@@ -2381,8 +3037,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;
 
@@ -2441,12 +3103,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,
@@ -2459,7 +3131,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;
@@ -2598,13 +3270,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)
--- socket.h	2009-10-01 20:02:18.000000000 +0200
+++ socket.h	2010-02-22 00:35:18.000000000 +0100
@@ -70,7 +70,13 @@
 struct openvpn_sockaddr
 {
   /*int dummy;*/ /* add offset to force a bug if sa not explicitly dereferenced */
-  struct sockaddr_in sa;
+  union {
+    struct sockaddr sa;
+    struct sockaddr_in in4;
+#ifdef USE_PF_INET6
+    struct sockaddr_in6 in6;
+#endif
+  } addr;
 };
 
 /* actual address of remote, based on source address of received packets */
@@ -79,7 +85,12 @@
   /*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 {
+    struct in_pktinfo in4;
+#ifdef USE_PF_INET6
+    struct in6_pktinfo in6;
+#endif
+  } pi;
 #endif
 };
 
@@ -199,6 +210,7 @@
 # define SF_TCP_NODELAY (1<<1)
 # define SF_PORT_SHARE (1<<2)
 # define SF_HOST_RANDOMIZE (1<<3)
+# define SF_GETADDRINFO_DGRAM (1<<4)
   unsigned int sockflags;
 
   /* for stream sockets */
@@ -371,6 +383,12 @@
 
 void bad_address_length (int actual, int expected);
 
+#ifdef USE_PF_INET6
+/* IPV4_INVALID_ADDR: returned by link_socket_current_remote()
+ * to ease redirect-gateway logic for ipv4 tunnels on ipv6 endpoints
+ */
+#define IPV4_INVALID_ADDR 0xffffffff
+#endif
 in_addr_t link_socket_current_remote (const struct link_socket_info *info);
 
 void link_socket_connection_initiated (const struct buffer *buf,
@@ -410,6 +428,14 @@
 socket_descriptor_t socket_do_accept (socket_descriptor_t sd,
 				      struct link_socket_actual *act,
 				      const bool nowait);
+/*
+ * proto related
+ */
+bool proto_is_net(int proto);
+bool proto_is_dgram(int proto);
+bool proto_is_udp(int proto);
+bool proto_is_tcp(int proto);
+
 
 #if UNIX_SOCK_SUPPORT
 
@@ -450,6 +476,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,
@@ -460,23 +491,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[];
 
@@ -500,7 +546,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
@@ -515,7 +561,31 @@
 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) {
+    case AF_INET: return lsa->pi.in4.ipi_spec_dst.s_addr != 0;
+#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
@@ -527,20 +597,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
@@ -553,6 +653,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)
 {
@@ -609,14 +777,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;
 }
@@ -722,7 +894,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;
 
@@ -733,10 +905,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
@@ -791,13 +963,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
@@ -828,11 +1001,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);
     }
--- socks.c	2009-10-01 20:02:18.000000000 +0200
+++ socks.c	2010-02-22 00:35:18.000000000 +0100
@@ -174,9 +174,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)
@@ -263,8 +263,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));
     }
 
 
@@ -382,8 +382,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;
 
@@ -415,8 +415,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;
 }
--- syshead.h	2009-10-01 20:02:18.000000000 +0200
+++ syshead.h	2010-02-22 00:35:18.000000000 +0100
@@ -28,6 +28,10 @@
 /*
  * Only include if not during configure
  */
+#ifdef WIN32
+/* USE_PF_INET6: win32 ipv6 exists only after 0x0501 (XP) */
+#define WINVER 0x0501
+#endif
 #ifndef PACKAGE_NAME
 #ifdef _MSC_VER
 #include "config-win32.h"
@@ -338,6 +342,9 @@
 #ifdef WIN32
 #include <iphlpapi.h>
 #include <wininet.h>
+/* The following two headers are needed of USE_PF_INET6 */
+#include <winsock2.h>
+#include <ws2tcpip.h>
 #endif
 
 #ifdef HAVE_SYS_MMAN_H
--- tun.c	2009-11-20 14:09:14.000000000 +0100
+++ tun.c	2010-02-22 00:35:18.000000000 +0100
@@ -1605,7 +1605,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",
--- win32.h	2009-11-20 14:09:14.000000000 +0100
+++ win32.h	2010-02-22 00:35:18.000000000 +0100
@@ -193,7 +193,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;
