Blob Blame History Raw
From: Michael Santos <michael.santos@gmail.com>
Date: Sun, 18 Oct 2015 16:20:37 -0400
Subject: [PATCH] epmd: support IPv6 node registration

Allow IPv6 nodes to register with and query epmd. On systems with
IPv6 support:

* epmd listens on both the IPv4 and IPv6 ANY or loopback sockets

* the epmd cli client connects to epmd over the IPv6 loopback

* distributed nodes started with "-proto_dist inet6_tcp" will register
  with epmd over IPv6

To work on IPv6 capable systems that have IPv6 support disabled,
epmd ignores errors opening the socket if the protocol is not
supported. Similarly, the epmd client will fall back to IPv4 if the IPv6
socket is not available.

Update the minimum supported version of Windows to Windows Vista to
support IPv6.

diff --git a/erts/configure.in b/erts/configure.in
index d9bc1ec..756f3cb 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -424,7 +424,7 @@ case $host_os in
     win32)
 	# The ethread library requires _WIN32_WINNT of at least 0x0403.
 	# -D_WIN32_WINNT=* from CPPFLAGS is saved in ETHR_DEFS.
-	CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0501 -DWINVER=0x0501"
+	CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600 -DWINVER=0x0600"
 	;;
     darwin*)
         CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE"
diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml
index 3e70054..3c9313e 100644
--- a/erts/doc/src/epmd.xml
+++ b/erts/doc/src/epmd.xml
@@ -36,7 +36,7 @@
   <comsummary>
   <p>Erlang Port Mapper Daemon</p>
   <taglist>
-  <tag><c><![CDATA[epmd [-d|-debug] [DbgExtra...] [-port No] [-daemon] [-relaxed_command_check]]]></c></tag>
+  <tag><c><![CDATA[epmd [-d|-debug] [DbgExtra...] [-address Addresses] [-port No] [-daemon] [-relaxed_command_check]]]></c></tag>
   <item>
   <p>Starts the port mapper daemon</p>
   </item>
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index 528a2d9..0b8f821 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -381,6 +381,33 @@
           similar to <c><![CDATA[code:add_pathsz/1]]></c>. See
           <seealso marker="kernel:code">code(3)</seealso>.</p>
       </item>
+      <tag><c><![CDATA[-path Dir1 Dir2 ...]]></c></tag>
+      <item>
+        <p>Replaces the path specified in the boot script. See
+          <seealso marker="sasl:script">script(4)</seealso>.</p>
+      </item>
+      <tag><c><![CDATA[-proto_dist Proto]]></c></tag>
+      <item>
+        <p>Specify a protocol for Erlang distribution.</p>
+	    <taglist>
+	      <tag><c>inet_tcp</c></tag>
+              <item>
+                  <p>TCP over IPv4 (the default)</p>
+              </item>
+	      <tag><c>inet_tls</c></tag>
+              <item>
+                  <p>distribution over TLS/SSL</p>
+              </item>
+	      <tag><c>inet6_tcp</c></tag>
+              <item>
+                  <p>TCP over IPv6</p>
+              </item>
+        </taglist>
+        <p>For example, to start up IPv6 distributed nodes:</p>
+<pre>
+% <input>erl -name test@ipv6node.example.com -proto_dist inet6_tcp</input>
+</pre>
+      </item>
       <tag><c><![CDATA[-remsh Node]]></c></tag>
       <item>
         <p>Starts Erlang with a remote shell connected to <c><![CDATA[Node]]></c>.</p>
diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c
index 1757fa9..ebae0a5 100644
--- a/erts/epmd/src/epmd.c
+++ b/erts/epmd/src/epmd.c
@@ -342,7 +342,7 @@ static void run_daemon(EpmdVars *g)
     for (fd = 0; fd < g->max_conn ; fd++) /* close all files ... */
         close(fd);
     /* Syslog on linux will try to write to whatever if we dont
-       inform it of that the log is closed. */
+       inform it that the log is closed. */
     closelog();
 
     /* These chouldn't be needed but for safety... */
diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c
index 8817bde..ea7dac7 100644
--- a/erts/epmd/src/epmd_cli.c
+++ b/erts/epmd/src/epmd_cli.c
@@ -135,19 +135,33 @@ void epmd_call(EpmdVars *g,int what)
 static int conn_to_epmd(EpmdVars *g)
 {
     struct EPMD_SOCKADDR_IN address;
+    size_t salen = 0;
     int connect_sock;
-    
-    connect_sock = socket(FAMILY, SOCK_STREAM, 0);
-    if (connect_sock<0)
-	goto error;
+    unsigned short sport = g->port;
+
+#if defined(EPMD6)
+    SET_ADDR6(address, in6addr_loopback, sport);
+    salen = sizeof(struct sockaddr_in6);
+
+    connect_sock = socket(AF_INET6, SOCK_STREAM, 0);
+    if (connect_sock>=0) {
+
+    if (connect(connect_sock, (struct sockaddr*)&address, salen) == 0)
+	return connect_sock;
 
-    { /* store port number in unsigned short */
-      unsigned short sport = g->port;
-      SET_ADDR(address, EPMD_ADDR_LOOPBACK, sport);
+    close(connect_sock);
     }
+#endif
+    SET_ADDR(address, htonl(INADDR_LOOPBACK), sport);
+    salen = sizeof(struct sockaddr_in);
 
-    if (connect(connect_sock, (struct sockaddr*)&address, sizeof address) < 0) 
+    connect_sock = socket(AF_INET, SOCK_STREAM, 0);
+    if (connect_sock<0)
 	goto error;
+
+    if (connect(connect_sock, (struct sockaddr*)&address, salen) < 0)
+	goto error;
+
     return connect_sock;
 
  error:
diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h
index 363923e..c6d9173 100644
--- a/erts/epmd/src/epmd_int.h
+++ b/erts/epmd/src/epmd_int.h
@@ -47,6 +47,7 @@
 #  ifndef WINDOWS_H_INCLUDES_WINSOCK2_H
 #    include <winsock2.h>
 #  endif
+#  include <ws2tcpip.h>
 #  include <windows.h>
 #  include <process.h>
 #endif
@@ -114,6 +115,10 @@
 #  include <systemd/sd-daemon.h>
 #endif
 
+#if defined(HAVE_IN6) && defined(AF_INET6) && defined(HAVE_INET_PTON)
+#  define EPMD6
+#endif
+
 /* ************************************************************************ */
 /* Replace some functions by others by making the function name a macro */
 
@@ -167,33 +172,53 @@
 /* ************************************************************************ */
 /* Macros that let us use IPv6                                              */
 
-#if defined(HAVE_IN6) && defined(AF_INET6) && defined(EPMD6)
+#if HAVE_IN6
+#  if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY
+#    if HAVE_DECL_IN6ADDR_ANY_INIT
+static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } };
+#    else
+static const struct in6_addr in6addr_any =
+    { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } };
+#    endif /* HAVE_IN6ADDR_ANY_INIT */
+#  endif /* ! HAVE_DECL_IN6ADDR_ANY */
+
+#  if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK
+#    if HAVE_DECL_IN6ADDR_LOOPBACK_INIT
+static const struct in6_addr in6addr_loopback =
+    { { IN6ADDR_LOOPBACK_INIT } };
+#    else
+static const struct in6_addr in6addr_loopback =
+    { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } };
+#    endif /* HAVE_IN6ADDR_LOOPBACK_INIT */
+#  endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */
+#endif /* HAVE_IN6 */
+
+#define IS_ADDR_LOOPBACK(addr) ((addr).s_addr == htonl(INADDR_LOOPBACK))
+
+#if defined(EPMD6)
 
-#define EPMD_SOCKADDR_IN sockaddr_in6
-#define EPMD_IN_ADDR in6_addr
-#define EPMD_S_ADDR s6_addr
-#define EPMD_ADDR_LOOPBACK in6addr_loopback.s6_addr
-#define EPMD_ADDR_ANY in6addr_any.s6_addr
+#define EPMD_SOCKADDR_IN sockaddr_storage
 #define FAMILY AF_INET6
 
-#define SET_ADDR(dst, addr, port) do { \
-    memset((char*)&(dst), 0, sizeof(dst)); \
-    memcpy((char*)&(dst).sin6_addr.s6_addr, (char*)&(addr), 16); \
-    (dst).sin6_family = AF_INET6; \
-    (dst).sin6_flowinfo = 0; \
-    (dst).sin6_port = htons(port); \
+#define SET_ADDR6(dst, addr, port) do { \
+    struct sockaddr_in6 *sa = (struct sockaddr_in6 *)&(dst); \
+    memset(sa, 0, sizeof(dst)); \
+    sa->sin6_family = AF_INET6; \
+    sa->sin6_addr = (addr); \
+    sa->sin6_port = htons(port); \
  } while(0)
 
-#define IS_ADDR_LOOPBACK(addr) \
-    (memcmp((addr).s6_addr, in6addr_loopback.s6_addr, 16) == 0)
+#define SET_ADDR(dst, addr, port) do { \
+    struct sockaddr_in *sa = (struct sockaddr_in *)&(dst); \
+    memset(sa, 0, sizeof(dst)); \
+    sa->sin_family = AF_INET; \
+    sa->sin_addr.s_addr = (addr); \
+    sa->sin_port = htons(port); \
+ } while(0)
 
 #else /* Not IP v6 */
 
 #define EPMD_SOCKADDR_IN sockaddr_in
-#define EPMD_IN_ADDR in_addr
-#define EPMD_S_ADDR s_addr
-#define EPMD_ADDR_LOOPBACK htonl(INADDR_LOOPBACK)
-#define EPMD_ADDR_ANY htonl(INADDR_ANY)
 #define FAMILY AF_INET
 
 #define SET_ADDR(dst, addr, port) do { \
@@ -203,8 +228,6 @@
     (dst).sin_port = htons(port); \
  } while(0)
 
-#define IS_ADDR_LOOPBACK(addr) ((addr).s_addr == htonl(INADDR_LOOPBACK))
-
 #endif /* Not IP v6 */
 
 /* ************************************************************************ */
diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c
index 78524a6..28e30d4 100644
--- a/erts/epmd/src/epmd_srv.c
+++ b/erts/epmd/src/epmd_srv.c
@@ -70,6 +70,7 @@ static time_t current_time(EpmdVars*);
 
 static Connection *conn_init(EpmdVars*);
 static int conn_open(EpmdVars*,int);
+static int conn_local_peer_check(EpmdVars*, int);
 static int conn_close_fd(EpmdVars*,int);
 
 static void node_init(EpmdVars*);
@@ -200,10 +201,11 @@ void run(EpmdVars *g)
 {
   struct EPMD_SOCKADDR_IN iserv_addr[MAX_LISTEN_SOCKETS];
   int listensock[MAX_LISTEN_SOCKETS];
-  int num_sockets;
+  int num_sockets = 0;
   int i;
   int opt;
   unsigned short sport = g->port;
+  int bound = 0;
 
   node_init(g);
   g->conn = conn_init(g);
@@ -246,64 +248,82 @@ void run(EpmdVars *g)
   if (g->addresses != NULL && /* String contains non-separator characters if: */
       g->addresses[strspn(g->addresses," ,")] != '\000')
     {
-      char *tmp;
-      char *token;
-      int loopback_ok = 0;
+      char *tmp = NULL;
+      char *token = NULL;
+
+      /* Always listen on the loopback. */
+      SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_LOOPBACK),sport);
+      num_sockets++;
+#if defined(EPMD6)
+      SET_ADDR6(iserv_addr[num_sockets],in6addr_loopback,sport);
+      num_sockets++;
+#endif
 
-      if ((tmp = (char *)malloc(strlen(g->addresses) + 1)) == NULL)
+	  if ((tmp = strdup(g->addresses)) == NULL)
 	{
 	  dbg_perror(g,"cannot allocate memory");
 	  epmd_cleanup_exit(g,1);
 	}
-      strcpy(tmp,g->addresses);
 
-      for(token = strtok(tmp,", "), num_sockets = 0;
+      for(token = strtok(tmp,", ");
 	  token != NULL;
-	  token = strtok(NULL,", "), num_sockets++)
+	  token = strtok(NULL,", "))
 	{
-	  struct EPMD_IN_ADDR addr;
-#ifdef HAVE_INET_PTON
-	  int ret;
+	  struct in_addr addr;
+#if defined(EPMD6)
+	  struct in6_addr addr6;
+	  struct sockaddr_storage *sa = &iserv_addr[num_sockets];
 
-	  if ((ret = inet_pton(FAMILY,token,&addr)) == -1)
+	  if (inet_pton(AF_INET6,token,&addr6) == 1)
 	    {
-	      dbg_perror(g,"cannot convert IP address to network format");
-	      epmd_cleanup_exit(g,1);
+	      SET_ADDR6(iserv_addr[num_sockets],addr6,sport);
+	    }
+	  else if (inet_pton(AF_INET,token,&addr) == 1)
+	    {
+	      SET_ADDR(iserv_addr[num_sockets],addr.s_addr,sport);
+	    }
+	  else
+#else
+	  if ((addr.s_addr = inet_addr(token)) != INADDR_NONE)
+	    {
+	      SET_ADDR(iserv_addr[num_sockets],addr.s_addr,sport);
 	    }
-	  else if (ret == 0)
-#elif !defined(EPMD6)
-	  if ((addr.EPMD_S_ADDR = inet_addr(token)) == INADDR_NONE)
+	  else
 #endif
 	    {
 	      dbg_tty_printf(g,0,"cannot parse IP address \"%s\"",token);
 	      epmd_cleanup_exit(g,1);
 	    }
 
+#if defined(EPMD6)
+	  if (sa->ss_family == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&addr6))
+	      continue;
+
+	  if (sa->ss_family == AF_INET)
+#endif
 	  if (IS_ADDR_LOOPBACK(addr))
-	    loopback_ok = 1;
+	    continue;
 
-	  if (num_sockets - loopback_ok == MAX_LISTEN_SOCKETS - 1)
+	  num_sockets++;
+
+	  if (num_sockets >= MAX_LISTEN_SOCKETS)
 	    {
 	      dbg_tty_printf(g,0,"cannot listen on more than %d IP addresses",
 			     MAX_LISTEN_SOCKETS);
 	      epmd_cleanup_exit(g,1);
 	    }
-
-	  SET_ADDR(iserv_addr[num_sockets],addr.EPMD_S_ADDR,sport);
 	}
 
       free(tmp);
-
-      if (!loopback_ok)
-	{
-	  SET_ADDR(iserv_addr[num_sockets],EPMD_ADDR_LOOPBACK,sport);
-	  num_sockets++;
-	}
     }
   else
     {
-      SET_ADDR(iserv_addr[0],EPMD_ADDR_ANY,sport);
-      num_sockets = 1;
+      SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_ANY),sport);
+      num_sockets++;
+#if defined(EPMD6)
+      SET_ADDR6(iserv_addr[num_sockets],in6addr_any,sport);
+      num_sockets++;
+#endif
     }
 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
     }
@@ -334,13 +354,39 @@ void run(EpmdVars *g)
 #endif
   for (i = 0; i < num_sockets; i++)
     {
-      if ((listensock[i] = socket(FAMILY,SOCK_STREAM,0)) < 0)
+      struct sockaddr *sa = (struct sockaddr *)&iserv_addr[i];
+#if defined(EPMD6)
+      size_t salen = (sa->sa_family == AF_INET6 ?
+              sizeof(struct sockaddr_in6) :
+              sizeof(struct sockaddr_in));
+#else
+      size_t salen = sizeof(struct sockaddr_in);
+#endif
+
+      if ((listensock[i] = socket(sa->sa_family,SOCK_STREAM,0)) < 0)
 	{
-	  dbg_perror(g,"error opening stream socket");
+	  switch (errno) {
+	      case EAFNOSUPPORT:
+	      case EPROTONOSUPPORT:
+	          continue;
+	      default:
+	          dbg_perror(g,"error opening stream socket");
+	          epmd_cleanup_exit(g,1);
+	  }
+	}
+      g->listenfd[bound++] = listensock[i];
+
+#if HAVE_DECL_IPV6_V6ONLY
+      opt = 1;
+      if (sa->sa_family == AF_INET6 &&
+          setsockopt(listensock[i],IPPROTO_IPV6,IPV6_V6ONLY,&opt,
+              sizeof(opt)) <0)
+	{
+	  dbg_perror(g,"can't set IPv6 only socket option");
 	  epmd_cleanup_exit(g,1);
 	}
-      g->listenfd[i] = listensock[i];
-  
+#endif
+
       /*
        * Note that we must not enable the SO_REUSEADDR on Windows,
        * because addresses will be reused even if they are still in use.
@@ -372,8 +418,7 @@ void run(EpmdVars *g)
 	dbg_perror(g,"failed to set non-blocking mode of listening socket %d",
 		   listensock[i]);
 
-      if (bind(listensock[i], (struct sockaddr*) &iserv_addr[i],
-	  sizeof(iserv_addr[i])) < 0)
+      if (bind(listensock[i], (struct sockaddr*) &iserv_addr[i], salen) < 0)
 	{
 	  if (errno == EADDRINUSE)
 	    {
@@ -394,6 +439,11 @@ void run(EpmdVars *g)
       }
       select_fd_set(g, listensock[i]);
     }
+  if (bound == 0) {
+      dbg_perror(g,"unable to bind any address");
+      epmd_cleanup_exit(g,1);
+  }
+  num_sockets = bound;
 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
     }
     sd_notifyf(0, "READY=1\n"
@@ -438,8 +488,8 @@ void run(EpmdVars *g)
 	}
 
 	for (i = 0; i < num_sockets; i++)
-	  if (FD_ISSET(listensock[i],&read_mask)) {
-	    if (do_accept(g, listensock[i]) && g->active_conn < g->max_conn) {
+	  if (FD_ISSET(g->listenfd[i],&read_mask)) {
+	    if (do_accept(g, g->listenfd[i]) && g->active_conn < g->max_conn) {
 	      /*
 	       * The accept() succeeded, and we have at least one file
 	       * descriptor still free, which means that another accept()
@@ -1001,15 +1051,6 @@ static int conn_open(EpmdVars *g,int fd)
 
   for (i = 0; i < g->max_conn; i++) {
     if (g->conn[i].open == EPMD_FALSE) {
-      struct sockaddr_in si;
-      struct sockaddr_in di;
-#ifdef HAVE_SOCKLEN_T
-      socklen_t st;
-#else
-      int st;
-#endif
-      st = sizeof(si);
-
       g->active_conn++;
       s = &g->conn[i];
      
@@ -1020,20 +1061,7 @@ static int conn_open(EpmdVars *g,int fd)
       s->open = EPMD_TRUE;
       s->keep = EPMD_FALSE;
 
-      /* Determine if connection is from localhost */
-      if (getpeername(s->fd,(struct sockaddr*) &si,&st) ||
-	  st < sizeof(si)) {
-	  /* Failure to get peername is regarded as non local host */
-	  s->local_peer = EPMD_FALSE;
-      } else {
-	  /* Only 127.x.x.x and connections from the host's IP address
-	     allowed, no false positives */
-	  s->local_peer =
-	      (((((unsigned) ntohl(si.sin_addr.s_addr)) & 0xFF000000U) ==
-	       0x7F000000U) ||
-	       (getsockname(s->fd,(struct sockaddr*) &di,&st) ?
-	       EPMD_FALSE : si.sin_addr.s_addr == di.sin_addr.s_addr));
-      }
+      s->local_peer = conn_local_peer_check(g, s->fd);
       dbg_tty_printf(g,2,(s->local_peer) ? "Local peer connected" :
 		     "Non-local peer connected");
 
@@ -1041,7 +1069,7 @@ static int conn_open(EpmdVars *g,int fd)
       s->got  = 0;
       s->mod_time = current_time(g); /* Note activity */
 
-      s->buf = (char *)malloc(INBUF_SIZE);
+      s->buf = malloc(INBUF_SIZE);
 
       if (s->buf == NULL) {
 	dbg_printf(g,0,"epmd: Insufficient memory");
@@ -1059,6 +1087,60 @@ static int conn_open(EpmdVars *g,int fd)
   return EPMD_FALSE;
 }
 
+static int conn_local_peer_check(EpmdVars *g, int fd)
+{
+  struct EPMD_SOCKADDR_IN si;
+  struct EPMD_SOCKADDR_IN di;
+
+  struct sockaddr_in *si4 = (struct sockaddr_in *)&si;
+  struct sockaddr_in *di4 = (struct sockaddr_in *)&di;
+
+#if defined(EPMD6)
+  struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&si;
+  struct sockaddr_in6 *di6 = (struct sockaddr_in6 *)&di;
+#endif
+
+#ifdef HAVE_SOCKLEN_T
+  socklen_t st;
+#else
+  int st;
+#endif
+
+  st = sizeof(si);
+
+  /* Determine if connection is from localhost */
+  if (getpeername(fd,(struct sockaddr*) &si,&st) ||
+	  st > sizeof(si)) {
+	  /* Failure to get peername is regarded as non local host */
+	  return EPMD_FALSE;
+  }
+
+  /* Only 127.x.x.x and connections from the host's IP address
+	 allowed, no false positives */
+#if defined(EPMD6)
+  if (si.ss_family == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&(si6->sin6_addr)))
+	  return EPMD_TRUE;
+
+  if (si.ss_family == AF_INET)
+#endif
+  if ((((unsigned) ntohl(si4->sin_addr.s_addr)) & 0xFF000000U) ==
+	  0x7F000000U)
+	  return EPMD_TRUE;
+
+  if (getsockname(fd,(struct sockaddr*) &di,&st))
+	  return EPMD_FALSE;
+
+#if defined(EPMD6)
+  if (si.ss_family == AF_INET6)
+      return IN6_ARE_ADDR_EQUAL( &(si6->sin6_addr), &(di6->sin6_addr));
+  if (si.ss_family == AF_INET)
+#endif
+  return si4->sin_addr.s_addr == di4->sin_addr.s_addr;
+#if defined(EPMD6)
+  return EPMD_FALSE;
+#endif
+}
+
 static int conn_close_fd(EpmdVars *g,int fd)
 {
   int i;
diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl
index cc24a55..8dfc21f 100644
--- a/erts/epmd/test/epmd_SUITE.erl
+++ b/erts/epmd/test/epmd_SUITE.erl
@@ -42,6 +42,7 @@
 -export(
    [
     register_name/1,
+    register_name_ipv6/1,
     register_names_1/1,
     register_names_2/1,
     register_duplicate_name/1,
@@ -108,7 +109,8 @@
 suite() -> [{ct_hooks,[ts_install_cth]}].
 
 all() -> 
-    [register_name, register_names_1, register_names_2,
+    [register_name, register_name_ipv6,
+     register_names_1, register_names_2,
      register_duplicate_name, unicode_name, long_unicode_name,
      get_port_nr, slow_get_port_nr,
      unregister_others_name_1, unregister_others_name_2,
@@ -165,6 +167,24 @@ register_name(Config) when is_list(Config) ->
     ?line ok = close(Sock),			% Unregister
     ok.
 
+register_name_ipv6(doc) ->
+    ["Register a name over IPv6"];
+register_name_ipv6(suite) ->
+    [];
+register_name_ipv6(Config) when is_list(Config) ->
+    % Test if the host has an IPv6 loopback address
+    Res = gen_tcp:listen(0, [inet6, {ip, {0,0,0,0,0,0,0,1}}]),
+    case Res of
+    {ok,LSock} ->
+	    gen_tcp:close(LSock),
+	    ?line ok = epmdrun(),
+	    ?line {ok,Sock} = register_node6("foobar6"),
+	    ?line ok = close(Sock),         % Unregister
+	    ok;
+    _Error ->
+	    {skip, "Host does not have an IPv6 loopback address"}
+    end.
+
 register_names_1(doc) ->
     ["Register and unregister two nodes"];
 register_names_1(suite) ->
@@ -238,13 +258,14 @@ register_node(Name) ->
 register_node(Name,Port) ->
     register_node_v2(Port,$M,0,5,5,Name,"").
 
+register_node6(Name) ->
+    register_node_v2({0,0,0,0,0,0,0,1},?DUMMY_PORT,$M,0,5,5,Name,"").
+
 register_node_v2(Port, NodeType, Prot, HVsn, LVsn, Name, Extra) ->
-    Utf8Name = unicode:characters_to_binary(Name),
-    Req = [?EPMD_ALIVE2_REQ, put16(Port), NodeType, Prot,
-	   put16(HVsn), put16(LVsn),
-	   put16(size(Utf8Name)), binary_to_list(Utf8Name),
-	   size16(Extra), Extra],
-    case send_req(Req) of
+    register_node_v2("localhost", Port, NodeType, Prot, HVsn, LVsn, Name, Extra).
+register_node_v2(Addr, Port, NodeType, Prot, HVsn, LVsn, Name, Extra) ->
+    Req = alive2_req(Port, NodeType, Prot, HVsn, LVsn, Name, Extra),
+    case send_req(Req, Addr) of
 	{ok,Sock} ->
 	    case recv(Sock,4) of
 		{ok, [?EPMD_ALIVE2_RESP,_Res=0,_C0,_C1]} ->
@@ -1129,7 +1150,9 @@ send_direct(Sock, Bytes) ->
     end.
 
 send_req(Req) ->
-    case connect() of
+    send_req(Req, "localhost").
+send_req(Req, Addr) ->
+    case connect(Addr) of
 	{ok,Sock} ->
 	    case send(Sock, [size16(Req), Req]) of
 		ok ->
diff --git a/lib/erl_interface/configure.in b/lib/erl_interface/configure.in
index d511f2e..99ee635 100644
--- a/lib/erl_interface/configure.in
+++ b/lib/erl_interface/configure.in
@@ -250,7 +250,7 @@ case "$threads_disabled" in
 	        ;;
 	    win32_threads)
 		EI_THREADS="true"	
-		THR_DEFS="$THR_DEFS -D_WIN32_WINNT=0x0500 -DWINVER=0x0500"
+		THR_DEFS="$THR_DEFS -D_WIN32_WINNT=0x0600 -DWINVER=0x0600"
 		;;
 	    pthread)	
 		EI_THREADS="true"
diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl
index 91af49f..21a3dec 100644
--- a/lib/kernel/src/erl_epmd.erl
+++ b/lib/kernel/src/erl_epmd.erl
@@ -31,7 +31,7 @@
 %% External exports
 -export([start/0, start_link/0, stop/0, port_please/2, 
 	 port_please/3, names/0, names/1,
-	 register_node/2, open/0, open/1, open/2]).
+	 register_node/2, register_node/3, open/0, open/1, open/2]).
 
 %% gen_server callbacks
 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 
@@ -106,7 +106,9 @@ names1(HostName) ->
 
 
 register_node(Name, PortNo) ->
-    gen_server:call(erl_epmd, {register, Name, PortNo}, infinity).
+    register_node(Name, PortNo, inet).
+register_node(Name, PortNo, Family) ->
+    gen_server:call(erl_epmd, {register, Name, PortNo, Family}, infinity).
 
 %%%----------------------------------------------------------------------
 %%% Callback functions from gen_server
@@ -124,10 +126,10 @@ init(_) ->
 -spec handle_call(calls(), term(), state()) ->
         {'reply', term(), state()} | {'stop', 'shutdown', 'ok', state()}.
 
-handle_call({register, Name, PortNo}, _From, State) ->
+handle_call({register, Name, PortNo, Family}, _From, State) ->
     case State#state.socket of
 	P when P < 0 ->
-	    case do_register_node(Name, PortNo) of
+	    case do_register_node(Name, PortNo, Family) of
 		{alive, Socket, Creation} ->
 		    S = State#state{socket = Socket,
 				    port_no = PortNo,
@@ -210,8 +212,12 @@ open({A,B,C,D,E,F,G,H}=EpmdAddr, Timeout) when ?ip6(A,B,C,D,E,F,G,H) ->
 close(Socket) ->
     gen_tcp:close(Socket).
 
-do_register_node(NodeName, TcpPort) ->
-    case open() of
+do_register_node(NodeName, TcpPort, Family) ->
+    Localhost = case Family of
+        inet -> open({127,0,0,1});
+        inet6 -> open({0,0,0,0,0,0,0,1})
+    end,
+    case Localhost of
 	{ok, Socket} ->
 	    Name = to_string(NodeName),
 	    Extra = "",
diff --git a/lib/wx/configure.in b/lib/wx/configure.in
index 3756786..be73888 100755
--- a/lib/wx/configure.in
+++ b/lib/wx/configure.in
@@ -163,14 +163,14 @@ case $host_os in
 	CPPFLAGS="$CPPFLAGS -D_MACOSX $PTHR_CFLAGS"
 	;;
     mingw32)
-	CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0500 -D_WINDOWS -D_UNICODE -DUNICODE"
-	CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0500"	
+	CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0600 -D_WINDOWS -D_UNICODE -DUNICODE"
+	CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600"
 	AC_MSG_WARN([Reverting to 32-bit time_t])
 	CPPFLAGS="$CPPFLAGS -D_USE_32BIT_TIME_T"
 	;;
     win32)
-	CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0500 -D_WINDOWS -D_UNICODE -DUNICODE"
-	CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0500"
+	CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0600 -D_WINDOWS -D_UNICODE -DUNICODE"
+	CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600"
 	;;
     *)
 	CFLAGS="$CFLAGS -Wno-deprecated-declarations"