diff -ur vtun-3.0.1/cfg_file.l vtun-3.0.1-ipv6/cfg_file.l
--- vtun-3.0.1/cfg_file.l	2006-12-11 16:55:06.000000000 +0900
+++ vtun-3.0.1-ipv6/cfg_file.l	2009-10-15 19:59:50.000000000 +0900
@@ -73,6 +73,7 @@
 comment		\#.*\n
 fname		[A-Za-z0-9\_\.\-]+	
 path		(\/{fname})+
+ipaddr		[A-Fa-f0-9:\.]+
 string		\".*\"
 
 %x OPTION PARAM  
@@ -170,6 +171,11 @@
 		  return PATH;
 		}
 
+<PARAM>{ipaddr}	{
+		  yylval.str = yytext;
+		  return IPADDR;
+		}
+
 <PARAM>\;	{ 
 		  POP_STATE();
 		}
diff -ur vtun-3.0.1/cfg_file.y vtun-3.0.1-ipv6/cfg_file.y
--- vtun-3.0.1/cfg_file.y	2006-12-11 16:55:06.000000000 +0900
+++ vtun-3.0.1-ipv6/cfg_file.y	2009-10-15 20:00:00.000000000 +0900
@@ -79,6 +79,7 @@
 
 %token <str> K_HOST K_ERROR
 %token <str> WORD PATH STRING
+%token <str> IPADDR
 %token <num> NUM 
 %token <dnum> DNUM
 
@@ -202,6 +203,11 @@
 			  vtun.bind_addr.type = VTUN_ADDR_NAME;
 			}
 
+  | K_ADDR IPADDR	{
+			  vtun.bind_addr.name = strdup($2);
+			  vtun.bind_addr.type = VTUN_ADDR_NAME;
+			}
+
   | K_IFACE WORD	{
 			  vtun.bind_addr.name = strdup($2);
 			  vtun.bind_addr.type = VTUN_ADDR_IFACE;
diff -ur vtun-3.0.1/client.c vtun-3.0.1-ipv6/client.c
--- vtun-3.0.1/client.c	2006-12-11 16:55:06.000000000 +0900
+++ vtun-3.0.1-ipv6/client.c	2009-10-19 10:04:17.000000000 +0900
@@ -55,7 +55,9 @@
 
 void client(struct vtun_host *host)
 {
-     struct sockaddr_in my_addr,svr_addr;
+     struct sockaddr_storage my_addr_storage,svr_addr_storage;
+     struct sockaddr *my_addr = (struct sockaddr *)&my_addr_storage;
+     struct sockaddr *svr_addr = (struct sockaddr *)&svr_addr_storage;
      struct sigaction sa;
      int s, opt, reconnect;	
 
@@ -90,18 +92,18 @@
 	set_title("%s init initializing", host->host);
 
 	/* Set server address */
-        if( server_addr(&svr_addr, host) < 0 )
+        if( server_addr(svr_addr, AF_UNSPEC, host) < 0 )
 	   continue;
 
 	/* Set local address */
-	if( local_addr(&my_addr, host, 0) < 0 )
+	if( local_addr(my_addr, svr_addr->sa_family, host, 0) < 0 )
 	   continue;
 
 	/* We have to create socket again every time
 	 * we want to connect, since STREAM sockets 
 	 * can be successfully connected only once.
 	 */
-        if( (s = socket(AF_INET,SOCK_STREAM,0))==-1 ){
+        if( (s = socket(svr_addr->sa_family,SOCK_STREAM,0))==-1 ){
 	   vtun_syslog(LOG_ERR,"Can't create socket. %s(%d)", 
 		strerror(errno), errno);
 	   continue;
@@ -111,7 +113,7 @@
         opt=1;
         setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 
 
-        if( bind(s,(struct sockaddr *)&my_addr,sizeof(my_addr)) ){
+        if( bind(s,my_addr,sockaddrlen(my_addr)) ){
 	   vtun_syslog(LOG_ERR,"Can't bind socket. %s(%d)",
 		strerror(errno), errno);
 	   continue;
@@ -128,7 +130,7 @@
 	set_title("%s connecting to %s", host->host, vtun.svr_name);
         vtun_syslog(LOG_INFO,"Connecting to %s", vtun.svr_name);
 
-        if( connect_t(s,(struct sockaddr *) &svr_addr, host->timeout) ){
+        if( connect_t(s,svr_addr,host->timeout) ){
 	   vtun_syslog(LOG_INFO,"Connect to %s failed. %s(%d)", vtun.svr_name,
 					strerror(errno), errno);
         } else {
diff -ur vtun-3.0.1/config.h.in vtun-3.0.1-ipv6/config.h.in
--- vtun-3.0.1/config.h.in	2006-12-11 16:55:06.000000000 +0900
+++ vtun-3.0.1-ipv6/config.h.in	2009-10-19 10:04:35.000000000 +0900
@@ -33,6 +33,9 @@
 /* Define if you have the <netdb.h> header file.  */
 #undef HAVE_NETDB_H
 
+/* Define if you have the <ifaddrs.h> header file.  */
+#undef HAVE_IFADDRS_H
+
 /* Define if you have the <sched.h> header file.  */
 #undef HAVE_SCHED_H
 
diff -ur vtun-3.0.1/configure.in vtun-3.0.1-ipv6/configure.in
--- vtun-3.0.1/configure.in	2006-12-11 16:55:06.000000000 +0900
+++ vtun-3.0.1-ipv6/configure.in	2009-10-19 10:05:28.000000000 +0900
@@ -40,6 +40,12 @@
    SOCKS=$enableval,
    SOCKS=no
 )
+dnl IPv6 support
+AC_ARG_ENABLE(ipv6,
+   --disable-ipv6    	   Don not compile with IPv6 support,
+   IPV6=$enableval,
+   IPV6=yes
+)
 
 AC_ARG_WITH(ssl-headers,
    --with-ssl-headers=DIR  Crypto Include files location,
@@ -89,15 +95,16 @@
 AC_CHECK_HEADERS(sys/resource.h netdb.h sched.h resolv.h arpa/inet.h)
 AC_CHECK_HEADERS(netinet/ip.h netinet/in.h netinet/tcp.h netinet/in_systm.h)
 AC_CHECK_HEADERS(libutil.h sys/sockio.h)
+AC_CHECK_HEADERS(ifaddrs.h)
 
 dnl Check for libsocket
 AC_SEARCH_LIBS(socket, socket)
 
 dnl Check for libnsl
-AC_SEARCH_LIBS(inet_ntoa, nsl)
+AC_SEARCH_LIBS(inet_ntop, nsl inet6)
 
 dnl Check for libresolv
-AC_SEARCH_LIBS(gethostbyname, resolv nsl)
+AC_SEARCH_LIBS(getaddrinfo, resolv nsl inet6)
 
 dnl Check for librt
 AC_SEARCH_LIBS(nanosleep, rt posix4)
diff -ur vtun-3.0.1/netlib.c vtun-3.0.1-ipv6/netlib.c
--- vtun-3.0.1/netlib.c	2006-12-11 16:55:06.000000000 +0900
+++ vtun-3.0.1-ipv6/netlib.c	2009-10-19 14:14:59.000000000 +0900
@@ -71,17 +71,39 @@
 #include <netdb.h>
 #endif
 
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#endif
+
 #include "vtun.h"
 #include "lib.h"
 #include "netlib.h"
 
+int sockaddrlen(struct sockaddr *saddr)
+{
+     int saddrlen = -1;
+     switch( saddr->sa_family ){
+        case AF_INET:
+           saddrlen = sizeof (struct sockaddr_in);
+           break;
+        case AF_INET6:
+           saddrlen = sizeof (struct sockaddr_in6);
+           break;
+        default:
+           vtun_syslog(LOG_ERR,"unknown address family: %d", saddr->sa_family);
+     }
+     return saddrlen;
+}
+
 /* Connect with timeout */
 int connect_t(int s, struct sockaddr *svr, time_t timeout) 
 {
+     size_t svrlen = sockaddrlen(svr);
+
 #if defined(VTUN_SOCKS) && VTUN_SOCKS == 2
      /* Some SOCKS implementations don't support
       * non blocking connect */
-     return connect(s,svr,sizeof(struct sockaddr));
+     return connect(s,svr,svrlen);
 #else
      int sock_flags;
      fd_set fdset;
@@ -93,7 +115,7 @@
      if( fcntl(s,F_SETFL,O_NONBLOCK) < 0 )
         return -1;
 
-     if( connect(s,svr,sizeof(struct sockaddr)) < 0 && errno != EINPROGRESS)
+     if( connect(s,svr,svrlen) < 0 && errno != EINPROGRESS)
         return -1;
 
      FD_ZERO(&fdset);
@@ -115,9 +137,28 @@
 }
 
 /* Get interface address */
-unsigned long getifaddr(char * ifname) 
+int getifaddr(char * ifname, struct sockaddr *addr, int addr_family)
 {
-     struct sockaddr_in addr;
+#ifdef HAVE_IFADDRS_H
+     struct ifaddrs *ifaddr, *ifa;
+     if( getifaddrs(&ifaddr) == -1 ){
+        vtun_syslog(LOG_ERR,"getifaddrs() failed: %s", strerror(errno));
+        return -1;
+     }
+     for( ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next ){
+        if( strcmp(ifname, ifa->ifa_name) != 0 )
+           continue;
+        if( ifa->ifa_addr->sa_family == AF_PACKET )
+           continue;
+        if( addr_family == AF_UNSPEC || addr_family == ifa->ifa_addr->sa_family ){
+           if( ifa->ifa_addr->sa_family == AF_INET6 )
+              memcpy(addr, ifa->ifa_addr, sizeof (struct sockaddr_in6));
+           else
+              memcpy(addr, ifa->ifa_addr, sizeof (struct sockaddr_in));
+         }
+     }
+     freeifaddrs(ifaddr);
+#else
      struct ifreq ifr;
      int s;
 
@@ -133,9 +174,9 @@
      }
      close(s);
 
-     addr = *((struct sockaddr_in *) &ifr.ifr_addr);
-
-     return addr.sin_addr.s_addr;
+     *(struct sockaddr_in *)addr = *((struct sockaddr_in *) &ifr.ifr_addr);
+#endif
+     return 0;
 }
 
 /* 
@@ -144,33 +185,46 @@
  */
 int udp_session(struct vtun_host *host) 
 {
-     struct sockaddr_in saddr; 
+     struct sockaddr_storage saddr_storage;
+     struct sockaddr *saddr = (struct sockaddr *)&saddr_storage;
+     size_t saddrlen;
      short port;
      int s,opt;
 
-     if( (s=socket(AF_INET,SOCK_DGRAM,0))== -1 ){
-        vtun_syslog(LOG_ERR,"Can't create socket");
-        return -1;
+     /* Set local address and port */
+     local_addr(saddr, AF_UNSPEC, host, 1);
+     saddrlen = sockaddrlen(saddr);
+     if( saddrlen <= 0 ) return -1;
+     if( (s=socket(saddr->sa_family,SOCK_DGRAM,0))== -1 ){
+	vtun_syslog(LOG_ERR,"Can't create socket");
+	return -1;
      }
 
      opt=1;
      setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 
-    
-     /* Set local address and port */
-     local_addr(&saddr, host, 1);
-     if( bind(s,(struct sockaddr *)&saddr,sizeof(saddr)) ){
+
+     if( bind(s,saddr,saddrlen) ){
         vtun_syslog(LOG_ERR,"Can't bind to the socket");
         return -1;
      }
 
-     opt = sizeof(saddr);
-     if( getsockname(s,(struct sockaddr *)&saddr,&opt) ){
+     opt = saddrlen;
+     if( getsockname(s,saddr,&opt) ){
         vtun_syslog(LOG_ERR,"Can't get socket name");
         return -1;
      }
 
      /* Write port of the new UDP socket */
-     port = saddr.sin_port;
+     switch( saddr->sa_family ){
+        case AF_INET:
+	   port = ((struct sockaddr_in *)saddr)->sin_port;
+	   break;
+        case AF_INET6:
+	   port = ((struct sockaddr_in6 *)saddr)->sin6_port;
+	   break;
+        default:
+           vtun_syslog(LOG_ERR,"unknown address family: %d", saddr->sa_family);
+     }
      if( write_n(host->rmt_fd,(char *)&port,sizeof(short)) < 0 ){
         vtun_syslog(LOG_ERR,"Can't write port number");
         return -1;
@@ -183,14 +237,13 @@
         return -1;
      }
 
-     opt = sizeof(saddr);
-     if( getpeername(host->rmt_fd,(struct sockaddr *)&saddr,&opt) ){
+     opt = saddrlen;
+     if( getpeername(host->rmt_fd,saddr,&opt) ){
         vtun_syslog(LOG_ERR,"Can't get peer name");
         return -1;
      }
 
-     saddr.sin_port = port;
-     if( connect(s,(struct sockaddr *)&saddr,sizeof(saddr)) ){
+     if( connect(s,saddr,saddrlen) ){
         vtun_syslog(LOG_ERR,"Can't connect socket");
         return -1;
      }
@@ -205,85 +258,179 @@
 }
 
 /* Set local address */
-int local_addr(struct sockaddr_in *addr, struct vtun_host *host, int con)
+int local_addr(struct sockaddr *addr, int addr_family, struct vtun_host *host, int con)
 {
      int opt;
+     char ip[INET6_ADDRSTRLEN + 1];
 
      if( con ){
         /* Use address of the already connected socket. */
-        opt = sizeof(struct sockaddr_in);
+        opt = sizeof(struct sockaddr_storage);
         if( getsockname(host->rmt_fd, (struct sockaddr *)addr, &opt) < 0 ){
            vtun_syslog(LOG_ERR,"Can't get local socket address");
            return -1; 
         }
      } else {
-        if (generic_addr(addr, &host->src_addr) < 0)
+        if (generic_addr(addr, addr_family, &host->src_addr) < 0)
                  return -1;
               }
 
-     host->sopt.laddr = strdup(inet_ntoa(addr->sin_addr));
+     switch( addr->sa_family ){
+        case AF_INET:
+           inet_ntop(AF_INET, &((struct sockaddr_in *)&addr)->sin_addr,
+                     ip, sizeof(ip));
+           break;
+        case AF_INET6:
+           inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr,
+                     ip, sizeof(ip));
+           break;
+        default:
+           vtun_syslog(LOG_ERR,"unknown address family: %d", addr->sa_family);
+           return -1;
+     }
+     host->sopt.laddr = strdup(ip);
 
      return 0;
 }
 
-int server_addr(struct sockaddr_in *addr, struct vtun_host *host)
+int server_addr(struct sockaddr *addr, int saddr_family, struct vtun_host *host)
 {
-     struct hostent * hent;
-
-     memset(addr,0,sizeof(struct sockaddr_in));
-     addr->sin_family = AF_INET;
-     addr->sin_port = htons(vtun.bind_addr.port);
+     char ip[INET6_ADDRSTRLEN + 1];
+     char port_str[16];
+     struct addrinfo addrinfo_hints;
+     struct addrinfo *addrinfo_results = NULL;
+     struct addrinfo *addrinfo;
+     int r;
 
      /* Lookup server's IP address.
       * We do it on every reconnect because server's IP 
       * address can be dynamic.
       */
-     if( !(hent = gethostbyname(vtun.svr_name)) ){
-        vtun_syslog(LOG_ERR, "Can't resolv server address: %s", vtun.svr_name);
+     memset(&addrinfo_hints, 0, sizeof(struct addrinfo));
+     addrinfo_hints.ai_family   = saddr_family;
+     addrinfo_hints.ai_socktype = SOCK_DGRAM;
+     snprintf(port_str, sizeof (port_str), "%d", vtun.bind_addr.port);
+     if((r = getaddrinfo(vtun.svr_name, port_str, &addrinfo_hints, &addrinfo_results)) != 0 ){
+        vtun_syslog(LOG_ERR,
+                    "Can't resolv server address: %s: %s",
+                    gai_strerror(r), vtun.svr_name);
         return -1;
      }
-     addr->sin_addr.s_addr = *(unsigned long *)hent->h_addr; 
 
-     host->sopt.raddr = strdup(inet_ntoa(addr->sin_addr));
+     /* Checks the routing of each address family by connecting via UDP. */
+     addr->sa_family = 0;
+     for( addrinfo = addrinfo_results; addrinfo; addrinfo = addrinfo->ai_next ) {
+        if( addrinfo->ai_family != AF_INET6 && addrinfo->ai_family != AF_INET )
+	  continue;
+        int s = socket(addrinfo->ai_family, addrinfo->ai_socktype, 0);
+        if(s < 0) continue;
+        if( connect(s, addrinfo->ai_addr, addrinfo->ai_addrlen) == 0 )
+           memcpy(addr, addrinfo->ai_addr, addrinfo->ai_addrlen);
+	close(s);
+     }
+     freeaddrinfo(addrinfo_results);
+     if (addr->sa_family == 0) {
+        vtun_syslog(LOG_ERR, "No routing for server address: %s", vtun.svr_name);
+        return -1;
+     }
+
+     switch( addr->sa_family ){
+        case AF_INET:
+           inet_ntop(AF_INET, &((struct sockaddr_in *)&addr)->sin_addr,
+                     ip, sizeof ip);
+	   host->sopt.raddr = strdup(ip);
+           host->sopt.rport = vtun.bind_addr.port;
+           break;
+        case AF_INET6:
+           inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr,
+                     ip, sizeof ip);
+           break;
+     default:
+           vtun_syslog(LOG_ERR, "unknown server address family: %d", addr->sa_family);
+           return -1;
+     }
+
+     host->sopt.raddr = strdup(ip);
      host->sopt.rport = vtun.bind_addr.port;
 
      return 0; 
 }
 
 /* Set address by interface name, ip address or hostname */
-int generic_addr(struct sockaddr_in *addr, struct vtun_addr *vaddr)
+int generic_addr(struct sockaddr *addr, int saddr_family, struct vtun_addr *vaddr)
 {
-     struct hostent *hent;
-     memset(addr, 0, sizeof(struct sockaddr_in));
-  
-     addr->sin_family = AF_INET;
-  
+     struct addrinfo addrinfo_hints;
+     struct addrinfo *addrinfo_results = NULL;
+     char port_str[16];
+     int r;
+
      switch (vaddr->type) {
         case VTUN_ADDR_IFACE:
-	 if (!(addr->sin_addr.s_addr =
-	       getifaddr(vaddr->name))) {
-	    vtun_syslog(LOG_ERR,
-	                "Can't get address of interface %s",
-	                vaddr->name);
-	    return -1;
-	 }
+	   /* FIXME:
+            * In IPv6, the option "bindaddr{ iface ... }" doesn't work well.
+            * Because one network device has two or more IPv6 addresses.
+	    */
+	   if (getifaddr(vaddr->name, addr, saddr_family)) {
+            vtun_syslog(LOG_ERR,
+                        "Can't get address of interface %s",
+                        vaddr->name);
+            return -1;
+           }
+           switch (addr->sa_family) {
+               case AF_INET:
+                    ((struct sockaddr_in *)addr)->sin_port = htons(vaddr->port); break;
+               case AF_INET6:
+                    ((struct sockaddr_in6 *)addr)->sin6_port = htons(vaddr->port); break;
+               default:
+                    vtun_syslog(LOG_ERR,"unknown address family: %d", addr->sa_family);
+                    return -1;
+           }
            break;
         case VTUN_ADDR_NAME:
-	 if (!(hent = gethostbyname(vaddr->name))) {
+           memset(&addrinfo_hints, 0, sizeof addrinfo_hints);
+           addrinfo_hints.ai_family   = saddr_family;
+           addrinfo_hints.ai_socktype = SOCK_DGRAM;
+           snprintf(port_str, sizeof (port_str), "%d", vtun.bind_addr.port);
+           if((r = getaddrinfo(vaddr->name, port_str, &addrinfo_hints, &addrinfo_results)) != 0 ){
 	    vtun_syslog(LOG_ERR,
-	                "Can't resolv local address %s",
-	                vaddr->name);
+	                "Can't resolv local address: %s: %s",
+	                gai_strerror(r), vaddr->name);
 	    return -1;
            }
-	 addr->sin_addr.s_addr = *(unsigned long *) hent->h_addr;
+	   memcpy(addr, addrinfo_results->ai_addr, addrinfo_results->ai_addrlen);
+	   freeaddrinfo(addrinfo_results);
            break;
         default:
-           addr->sin_addr.s_addr = INADDR_ANY;
+           if( saddr_family == AF_UNSPEC ){
+              /* IPv6 may be better on dual stack environment,
+               * when IPv6 loopback address is available. */
+              struct sockaddr_in6 lo6addr;
+              int s;
+              lo6addr.sin6_family = AF_INET6;
+              lo6addr.sin6_addr = in6addr_loopback;
+              lo6addr.sin6_port = htons(vaddr->port);
+              s = socket(AF_INET6, SOCK_DGRAM, 0);
+              if(s >= 0 && connect(s, (struct sockaddr *)&lo6addr, sizeof(lo6addr)) == 0 )
+                 saddr_family = AF_INET6;
+              else
+                 saddr_family = AF_INET;
+              if(s >= 0)
+                close(s);
+           }
+           memset(&addrinfo_hints, 0, sizeof addrinfo_hints);
+           addrinfo_hints.ai_family = saddr_family;
+           addrinfo_hints.ai_socktype = SOCK_DGRAM;
+           addrinfo_hints.ai_flags = AI_PASSIVE;
+           snprintf(port_str, sizeof (port_str), "%d", vaddr->port);
+           if((r = getaddrinfo(NULL, port_str, &addrinfo_hints, &addrinfo_results)) != 0 ){
+              vtun_syslog(LOG_ERR,
+                          "Can't resolv bind address: %s: %s", port_str, gai_strerror(r));
+	      return -1;
+           }
+	   memcpy(addr, addrinfo_results->ai_addr, addrinfo_results->ai_addrlen);
+	   freeaddrinfo(addrinfo_results);
            break;
      }
-  
-     if (vaddr->port)
-        addr->sin_port = htons(vaddr->port);
 
      return 0; 
 }
diff -ur vtun-3.0.1/netlib.h vtun-3.0.1-ipv6/netlib.h
--- vtun-3.0.1/netlib.h	2006-12-11 16:55:06.000000000 +0900
+++ vtun-3.0.1-ipv6/netlib.h	2009-10-16 16:32:11.000000000 +0900
@@ -32,12 +32,14 @@
 #include <netinet/in.h>
 #endif
 
-unsigned long getifaddr(char * ifname);
+int sockaddrlen(struct sockaddr *saddr);
+short sockaddrport(struct sockaddr *saddr);
+int getifaddr(char * ifname, struct sockaddr *addr, int saddr_family);
 int connect_t(int s, struct sockaddr *svr, time_t timeout); 
 int udp_session(struct vtun_host *host); 
 
-int local_addr(struct sockaddr_in *addr, struct vtun_host *host, int con);
-int server_addr(struct sockaddr_in *addr, struct vtun_host *host);
-int generic_addr(struct sockaddr_in *addr, struct vtun_addr *vaddr);
+int local_addr(struct sockaddr *addr, int saddr_family, struct vtun_host *host, int con);
+int server_addr(struct sockaddr *addr, int saddr_family, struct vtun_host *host);
+int generic_addr(struct sockaddr *addr, int saddr_family, struct vtun_addr *vaddr);
 
 #endif /* _VTUN_NETDEV_H */
diff -ur vtun-3.0.1/server.c vtun-3.0.1-ipv6/server.c
--- vtun-3.0.1/server.c	2009-10-15 14:26:53.000000000 +0900
+++ vtun-3.0.1-ipv6/server.c	2009-10-19 10:04:04.000000000 +0900
@@ -49,6 +49,7 @@
 #include "auth.h"
 
 #include "compat.h"
+#include "netlib.h"
 
 static volatile sig_atomic_t server_term;
 static void sig_term(int sig)
@@ -59,24 +60,49 @@
 
 void connection(int sock)
 {
-     struct sockaddr_in my_addr, cl_addr;
+     struct sockaddr_storage my_addr_storage,cl_addr_storage;
+     struct sockaddr *my_addr = (struct sockaddr *)&my_addr_storage;
+     struct sockaddr *cl_addr = (struct sockaddr *)&cl_addr_storage;
      struct vtun_host *host;
      struct sigaction sa;
-     char *ip;
+     char cl_ip[INET6_ADDRSTRLEN+1], my_ip[INET6_ADDRSTRLEN+1];
+     int cl_port;
      int opt;
 
-     opt = sizeof(struct sockaddr_in);
-     if( getpeername(sock, (struct sockaddr *) &cl_addr, &opt) ){
+     opt = sizeof(struct sockaddr_storage);
+     if( getpeername(sock, (struct sockaddr *) cl_addr, &opt) ){
         vtun_syslog(LOG_ERR, "Can't get peer name");
         exit(1);
      }
-     opt = sizeof(struct sockaddr_in);
-     if( getsockname(sock, (struct sockaddr *) &my_addr, &opt) < 0 ){
+     opt = sizeof(struct sockaddr_storage);
+     if( getsockname(sock, (struct sockaddr *) my_addr, &opt) < 0 ){
         vtun_syslog(LOG_ERR, "Can't get local socket address");
         exit(1); 
      }
 
-     ip = strdup(inet_ntoa(cl_addr.sin_addr));
+     switch( cl_addr->sa_family ){
+        case AF_INET:
+           inet_ntop(my_addr->sa_family,
+                     &((struct sockaddr_in *)my_addr)->sin_addr,
+                     my_ip, sizeof(my_ip));
+           inet_ntop(cl_addr->sa_family,
+                     &((struct sockaddr_in *)cl_addr)->sin_addr,
+                     cl_ip, sizeof(cl_ip));
+           cl_port = ((struct sockaddr_in *)cl_addr)->sin_port;
+           break;
+        case AF_INET6:
+           inet_ntop(my_addr->sa_family,
+                     &((struct sockaddr_in6 *)my_addr)->sin6_addr,
+                     my_ip, sizeof(my_ip));
+           inet_ntop(cl_addr->sa_family,
+                     &((struct sockaddr_in6 *)cl_addr)->sin6_addr,
+                     cl_ip, sizeof(cl_ip));
+           cl_port = ((struct sockaddr_in6 *)cl_addr)->sin6_port;
+           break;
+        default:
+           vtun_syslog(LOG_ERR, "unknown address family: %d", cl_addr->sa_family);
+           exit(1);
+     }
 
      io_init();
 
@@ -85,14 +111,14 @@
 	sa.sa_flags=SA_NOCLDWAIT;;
         sigaction(SIGHUP,&sa,NULL);
 
-	vtun_syslog(LOG_INFO,"Session %s[%s:%d] opened", host->host, ip, 
-					ntohs(cl_addr.sin_port) );
+	vtun_syslog(LOG_INFO,"Session %s[%s:%d] opened", host->host, cl_ip,
+					ntohs(cl_port) );
         host->rmt_fd = sock; 
 	
-        host->sopt.laddr = strdup(inet_ntoa(my_addr.sin_addr));
+        host->sopt.laddr = strdup(my_ip);
         host->sopt.lport = vtun.bind_addr.port;
-        host->sopt.raddr = strdup(ip);
-	host->sopt.rport = ntohs(cl_addr.sin_port);
+        host->sopt.raddr = strdup(cl_ip);
+	host->sopt.rport = ntohs(cl_port);
 
 	/* Start tunnel */
 	tunnel(host);
@@ -102,8 +128,8 @@
 	/* Unlock host. (locked in auth_server) */	
 	unlock_host(host);
      } else {
-        vtun_syslog(LOG_INFO,"Denied connection from %s:%d", ip,
-					ntohs(cl_addr.sin_port) );
+        vtun_syslog(LOG_INFO,"Denied connection from %s:%d", cl_ip,
+					ntohs(cl_port) );
      }
      close(sock);
 
@@ -113,20 +139,19 @@
 void listener(void)
 {
      struct sigaction sa;
-     struct sockaddr_in my_addr, cl_addr;
+     struct sockaddr_storage my_addr_storage,cl_addr_storage;
+     struct sockaddr *my_addr = (struct sockaddr *)&my_addr_storage;
+     struct sockaddr *cl_addr = (struct sockaddr *)&cl_addr_storage;
      int s, s1, opt;
 
-     memset(&my_addr, 0, sizeof(my_addr));
-     my_addr.sin_family = AF_INET;
-
      /* Set listen address */
-     if( generic_addr(&my_addr, &vtun.bind_addr) < 0)
+     if( generic_addr(my_addr, AF_UNSPEC, &vtun.bind_addr) < 0)
      {
         vtun_syslog(LOG_ERR, "Can't fill in listen socket");
         exit(1);
      }
 
-     if( (s=socket(AF_INET,SOCK_STREAM,0))== -1 ){
+     if( (s=socket(my_addr->sa_family,SOCK_STREAM,0))== -1 ){
 	vtun_syslog(LOG_ERR,"Can't create socket");
 	exit(1);
      }
@@ -134,7 +159,7 @@
      opt=1;
      setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 
 
-     if( bind(s,(struct sockaddr *)&my_addr,sizeof(my_addr)) ){
+     if( bind(s,my_addr,sockaddrlen(my_addr)) ){
 	vtun_syslog(LOG_ERR,"Can't bind to the socket");
 	exit(1);
      }
@@ -154,8 +179,8 @@
      set_title("waiting for connections on port %d", vtun.bind_addr.port);
 
      while( (!server_term) || (server_term == VTUN_SIG_HUP) ){
-        opt=sizeof(cl_addr);
-	if( (s1=accept(s,(struct sockaddr *)&cl_addr,&opt)) < 0 )
+        opt=sockaddrlen(my_addr);
+	if( (s1=accept(s,cl_addr,&opt)) < 0 )
 	   continue; 
 
 	switch( fork() ){
