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

Michael Santos <>
Wed Aug 8 01:13:59 CEST 2012


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

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

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

* distributed nodes started with "-proto_dist inet6_tcp" will register
  with epmd over IPv6
---
 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          |   37 ++++----
 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, 222 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 6221a23..09dc81e 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  -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 2267f9b..e2bba09 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 74408e3..e46a700 100644
--- a/erts/epmd/src/epmd_cli.c
+++ b/erts/epmd/src/epmd_cli.c
@@ -132,6 +132,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);
@@ -140,10 +141,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 14d05c3..64973b6 100644
--- a/erts/epmd/src/epmd_int.h
+++ b/erts/epmd/src/epmd_int.h
@@ -165,33 +165,32 @@
 /* ************************************************************************ */
 /* Macros that let us use IPv6                                              */
 
-#if defined(HAVE_IN6) && defined(AF_INET6) && defined(EPMD6)
+#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 +200,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 da575af..538706b 100644
--- a/erts/epmd/src/epmd_srv.c
+++ b/erts/epmd/src/epmd_srv.c
@@ -69,6 +69,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*);
@@ -92,7 +93,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;
@@ -105,64 +106,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__)
@@ -183,13 +202,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 defined(HAVE_IN6) && defined(AF_INET6)
+      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.
@@ -221,8 +260,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)
 	    {
@@ -818,15 +856,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];
      
@@ -837,20 +866,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");
 
@@ -858,7 +874,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");
@@ -876,6 +892,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 3f31cd9..5821cd8 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,
@@ -106,7 +107,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, get_port_nr, slow_get_port_nr,
      unregister_others_name_1, unregister_others_name_2,
      register_overflow, name_with_null_inside,
@@ -162,6 +164,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) ->
@@ -204,12 +224,17 @@ 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) ->
     Req = [?EPMD_ALIVE2_REQ, put16(Port), NodeType, Prot,
 	   put16(HVsn), put16(LVsn),
 	   size16(Name), Name,
 	   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]} ->
@@ -1092,7 +1117,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 b9c4fa6..0b5fb44 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,
-            {ok, Creation} = erl_epmd:register_node(Name, Port),
+            {ok, Creation} = erl_epmd:register_node(Name, Port, inet6),
             {ok, {Socket, TcpAddress, Creation}};
         Error ->
             Error
-- 
1.7.9.5



More information about the erlang-patches mailing list