[erlang-patches] [PATCH] epmd: support IPv6 node registration

Michael Santos michael.santos@REDACTED
Mon Feb 18 22:01:39 CET 2013


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

The interaction between IPv4 and IPv6 sockets depends on the platform:

* FreeBSD allows multiple "specific" sockets to bind the same port (such
  as 2 sockets listening to the same port on ANY and the loopback).
  Binding port 4369 to IPv4 and IPv6 sockets simulataneously is allowed.

* Linux does not allow the same port to be bound by different sockets.
  Setting the IPV6_V6ONLY socket option is required.

* Windows

  The behaviour differs depending on the version of Windows:

  http://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx

  According to the site, sockets on Windows XP with Service Pack 1 (SP1)
  and Windows Server 2003 will only listen on either IPv4 or IPv6, so
  creating two sockets is required to service IPv4 and IPv6 traffic on
  the same port. The IPV6_V6ONLY socket option is not supported.

  For Windows Vista and later, a single socket can handle IPv4 and IPv6
  traffic for the same port. The IPV6_V6ONLY socket option is supported
  and is enabled for IPv6 sockets by default.
---
 erts/doc/src/epmd.xml             |    2 +-
 erts/doc/src/erl.xml              |   22 +++++
 erts/epmd/src/epmd.c              |    6 +-
 erts/epmd/src/epmd_cli.c          |   11 ++-
 erts/epmd/src/epmd_int.h          |   59 ++++++++----
 erts/epmd/src/epmd_srv.c          |  184 +++++++++++++++++++++++++------------
 erts/epmd/test/epmd_SUITE.erl     |   33 ++++++-
 lib/kernel/src/erl_epmd.erl       |   18 ++--
 lib/kernel/src/inet6_tcp_dist.erl |    2 +-
 9 files changed, 244 insertions(+), 93 deletions(-)

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 f354d68..aebf4ff 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -381,6 +381,28 @@
           similar to <c><![CDATA[code:add_pathsz/1]]></c>. See
           <seealso marker="kernel:code">code(3)</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_ssl</c></tag>
+              <item>
+                  <p>distribution over 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@REDACTED -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 94bb74c..0789cc3 100644
--- a/erts/epmd/src/epmd.c
+++ b/erts/epmd/src/epmd.c
@@ -335,10 +335,10 @@ 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... */
+    /* These shouldn't be needed but for safety... */
 
     open("/dev/null", O_RDONLY); /* Order is important! */
     open("/dev/null", O_WRONLY);
@@ -379,7 +379,7 @@ static void run_daemon(EpmdVars *g)
     close(1);
     close(2);
 
-    /* These chouldn't be needed but for safety... */
+    /* These shouldn't be needed but for safety... */
 
     open("nul", O_RDONLY);
     open("nul", O_WRONLY);
diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c
index 8817bde..a0dd890 100644
--- a/erts/epmd/src/epmd_cli.c
+++ b/erts/epmd/src/epmd_cli.c
@@ -135,6 +135,7 @@ 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);
@@ -143,10 +144,16 @@ static int conn_to_epmd(EpmdVars *g)
 
     { /* store port number in unsigned short */
       unsigned short sport = g->port;
-      SET_ADDR(address, EPMD_ADDR_LOOPBACK, sport);
+#if defined(HAVE_IN6) && defined(AF_INET6)
+      SET_ADDR6(address, in6addr_loopback, sport);
+      salen = sizeof(struct sockaddr_in6);
+#else
+      SET_ADDR(address, htonl(INADDR_LOOPBACK), sport);
+      salen = sizeof(struct sockaddr_in);
+#endif
     }
 
-    if (connect(connect_sock, (struct sockaddr*)&address, sizeof address) < 0) 
+    if (connect(connect_sock, (struct sockaddr*)&address, salen) < 0)
 	goto error;
     return connect_sock;
 
diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h
index ac354dc..e7974ae 100644
--- a/erts/epmd/src/epmd_int.h
+++ b/erts/epmd/src/epmd_int.h
@@ -49,6 +49,7 @@
 #  ifndef WINDOWS_H_INCLUDES_WINSOCK2_H
 #    include <winsock2.h>
 #  endif
+#  include <ws2tcpip.h>
 #  include <windows.h>
 #  include <process.h>
 #endif
@@ -165,33 +166,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(HAVE_IN6) && defined(AF_INET6)
 
-#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 { \
@@ -201,8 +222,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 90df7cc..9b7d931 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,7 +201,7 @@ 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;
@@ -213,64 +214,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(HAVE_IN6) && defined(AF_INET6)
+      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(HAVE_IN6) && defined(AF_INET6)
+	  struct in6_addr addr6;
+	  struct sockaddr_storage *sa = &iserv_addr[num_sockets];
 
-	  if ((ret = inet_pton(FAMILY,token,&addr)) == -1)
+	  if (inet_pton(AF_INET,token,&addr) == 1)
 	    {
-	      dbg_perror(g,"cannot convert IP address to network format");
-	      epmd_cleanup_exit(g,1);
+	      SET_ADDR(iserv_addr[num_sockets],addr.s_addr,sport);
+	    }
+	  else if (inet_pton(AF_INET6,token,&addr6) == 1)
+	    {
+	      SET_ADDR6(iserv_addr[num_sockets],addr6,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(HAVE_IN6) && defined(AF_INET6)
+	  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(HAVE_IN6) && defined(AF_INET6)
+      SET_ADDR6(iserv_addr[num_sockets],in6addr_any,sport);
+      num_sockets++;
+#endif
     }
 
 #if !defined(__WIN32__)
@@ -291,13 +310,33 @@ void run(EpmdVars *g)
 
   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(HAVE_IN6) && defined(AF_INET6)
+      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");
 	  epmd_cleanup_exit(g,1);
 	}
       g->listenfd[i] = 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);
+	}
+#endif
+
       /*
        * Note that we must not enable the SO_REUSEADDR on Windows,
        * because addresses will be reused even if they are still in use.
@@ -329,8 +368,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)
 	    {
@@ -952,15 +990,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];
      
@@ -971,20 +1000,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");
 
@@ -992,7 +1008,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");
@@ -1010,6 +1026,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(HAVE_IN6) && defined(AF_INET6)
+  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(HAVE_IN6) && defined(AF_INET6)
+  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(HAVE_IN6) && defined(AF_INET6)
+  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(HAVE_IN6) && defined(AF_INET6)
+  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..ddfe5ab 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,18 @@ 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) ->
+    register_node_v2("localhost", Port, NodeType, Prot, HVsn, LVsn, Name, Extra).
+register_node_v2(Addr, 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
+    case send_req(Req, Addr) of
 	{ok,Sock} ->
 	    case recv(Sock,4) of
 		{ok, [?EPMD_ALIVE2_RESP,_Res=0,_C0,_C1]} ->
@@ -1129,7 +1154,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/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/kernel/src/inet6_tcp_dist.erl b/lib/kernel/src/inet6_tcp_dist.erl
index 2315a56..bba4d87 100644
--- a/lib/kernel/src/inet6_tcp_dist.erl
+++ b/lib/kernel/src/inet6_tcp_dist.erl
@@ -71,7 +71,7 @@ listen(Name) ->
         {ok, Socket} ->
             TcpAddress = get_tcp_address(Socket),
             {_,Port} = TcpAddress#net_address.address,
-            case erl_epmd:register_node(Name, Port) of
+            case erl_epmd:register_node(Name, Port, inet6) of
                 {ok, Creation} ->
                     {ok, {Socket, TcpAddress, Creation}};
                 Error ->
-- 
1.7.9.5




More information about the erlang-patches mailing list