Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 1 commit
  • 9 files changed
  • 0 comments
  • 1 contributor
May 03, 2013
Michael Santos 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

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.
d3d6846
2  erts/doc/src/epmd.xml
@@ -36,7 +36,7 @@
36 36
   <comsummary>
37 37
   <p>Erlang Port Mapper Daemon</p>
38 38
   <taglist>
39  
-  <tag><c><![CDATA[epmd [-d|-debug] [DbgExtra...] [-port No] [-daemon] [-relaxed_command_check]]]></c></tag>
  39
+  <tag><c><![CDATA[epmd [-d|-debug] [DbgExtra...] [-address Addresses] [-port No] [-daemon] [-relaxed_command_check]]]></c></tag>
40 40
   <item>
41 41
   <p>Starts the port mapper daemon</p>
42 42
   </item>
22  erts/doc/src/erl.xml
@@ -381,6 +381,28 @@
381 381
           similar to <c><![CDATA[code:add_pathsz/1]]></c>. See
382 382
           <seealso marker="kernel:code">code(3)</seealso>.</p>
383 383
       </item>
  384
+      <tag><c><![CDATA[-proto_dist Proto]]></c></tag>
  385
+      <item>
  386
+        <p>Specify a protocol for Erlang distribution.</p>
  387
+	    <taglist>
  388
+	      <tag><c>inet_tcp</c></tag>
  389
+              <item>
  390
+                  <p>TCP over IPv4 (the default)</p>
  391
+              </item>
  392
+	      <tag><c>inet_ssl</c></tag>
  393
+              <item>
  394
+                  <p>distribution over SSL</p>
  395
+              </item>
  396
+	      <tag><c>inet6_tcp</c></tag>
  397
+              <item>
  398
+                  <p>TCP over IPv6</p>
  399
+              </item>
  400
+        </taglist>
  401
+        <p>For example, to start up IPv6 distributed nodes:</p>
  402
+<pre>
  403
+% <input>erl -name test@ipv6node.example.com -proto_dist inet6_tcp</input>
  404
+</pre>
  405
+      </item>
384 406
       <tag><c><![CDATA[-remsh Node]]></c></tag>
385 407
       <item>
386 408
         <p>Starts Erlang with a remote shell connected to <c><![CDATA[Node]]></c>.</p>
6  erts/epmd/src/epmd.c
@@ -335,10 +335,10 @@ static void run_daemon(EpmdVars *g)
335 335
     for (fd = 0; fd < g->max_conn ; fd++) /* close all files ... */
336 336
         close(fd);
337 337
     /* Syslog on linux will try to write to whatever if we dont
338  
-       inform it of that the log is closed. */
  338
+       inform it that the log is closed. */
339 339
     closelog();
340 340
 
341  
-    /* These chouldn't be needed but for safety... */
  341
+    /* These shouldn't be needed but for safety... */
342 342
 
343 343
     open("/dev/null", O_RDONLY); /* Order is important! */
344 344
     open("/dev/null", O_WRONLY);
@@ -379,7 +379,7 @@ static void run_daemon(EpmdVars *g)
379 379
     close(1);
380 380
     close(2);
381 381
 
382  
-    /* These chouldn't be needed but for safety... */
  382
+    /* These shouldn't be needed but for safety... */
383 383
 
384 384
     open("nul", O_RDONLY);
385 385
     open("nul", O_WRONLY);
11  erts/epmd/src/epmd_cli.c
@@ -135,6 +135,7 @@ void epmd_call(EpmdVars *g,int what)
135 135
 static int conn_to_epmd(EpmdVars *g)
136 136
 {
137 137
     struct EPMD_SOCKADDR_IN address;
  138
+    size_t salen = 0;
138 139
     int connect_sock;
139 140
     
140 141
     connect_sock = socket(FAMILY, SOCK_STREAM, 0);
@@ -143,10 +144,16 @@ static int conn_to_epmd(EpmdVars *g)
143 144
 
144 145
     { /* store port number in unsigned short */
145 146
       unsigned short sport = g->port;
146  
-      SET_ADDR(address, EPMD_ADDR_LOOPBACK, sport);
  147
+#if defined(HAVE_IN6) && defined(AF_INET6)
  148
+      SET_ADDR6(address, in6addr_loopback, sport);
  149
+      salen = sizeof(struct sockaddr_in6);
  150
+#else
  151
+      SET_ADDR(address, htonl(INADDR_LOOPBACK), sport);
  152
+      salen = sizeof(struct sockaddr_in);
  153
+#endif
147 154
     }
148 155
 
149  
-    if (connect(connect_sock, (struct sockaddr*)&address, sizeof address) < 0) 
  156
+    if (connect(connect_sock, (struct sockaddr*)&address, salen) < 0)
150 157
 	goto error;
151 158
     return connect_sock;
152 159
 
63  erts/epmd/src/epmd_int.h
@@ -46,9 +46,14 @@
46 46
 #include <string.h>
47 47
 
48 48
 #ifdef __WIN32__
  49
+#  ifdef _WIN32_WINVER
  50
+#    undef _WIN32_WINVER
  51
+#  endif
  52
+#  define _WIN32_WINVER 0x0501
49 53
 #  ifndef WINDOWS_H_INCLUDES_WINSOCK2_H
50 54
 #    include <winsock2.h>
51 55
 #  endif
  56
+#  include <ws2tcpip.h>
52 57
 #  include <windows.h>
53 58
 #  include <process.h>
54 59
 #endif
@@ -165,33 +170,53 @@
165 170
 /* ************************************************************************ */
166 171
 /* Macros that let us use IPv6                                              */
167 172
 
168  
-#if defined(HAVE_IN6) && defined(AF_INET6) && defined(EPMD6)
  173
+#if HAVE_IN6
  174
+#  if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY
  175
+#    if HAVE_DECL_IN6ADDR_ANY_INIT
  176
+static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } };
  177
+#    else
  178
+static const struct in6_addr in6addr_any =
  179
+    { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } };
  180
+#    endif /* HAVE_IN6ADDR_ANY_INIT */
  181
+#  endif /* ! HAVE_DECL_IN6ADDR_ANY */
  182
+
  183
+#  if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK
  184
+#    if HAVE_DECL_IN6ADDR_LOOPBACK_INIT
  185
+static const struct in6_addr in6addr_loopback =
  186
+    { { IN6ADDR_LOOPBACK_INIT } };
  187
+#    else
  188
+static const struct in6_addr in6addr_loopback =
  189
+    { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } };
  190
+#    endif /* HAVE_IN6ADDR_LOOPBACK_INIT */
  191
+#  endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */
  192
+#endif /* HAVE_IN6 */
  193
+
  194
+#define IS_ADDR_LOOPBACK(addr) ((addr).s_addr == htonl(INADDR_LOOPBACK))
  195
+
  196
+#if defined(HAVE_IN6) && defined(AF_INET6)
169 197
 
170  
-#define EPMD_SOCKADDR_IN sockaddr_in6
171  
-#define EPMD_IN_ADDR in6_addr
172  
-#define EPMD_S_ADDR s6_addr
173  
-#define EPMD_ADDR_LOOPBACK in6addr_loopback.s6_addr
174  
-#define EPMD_ADDR_ANY in6addr_any.s6_addr
  198
+#define EPMD_SOCKADDR_IN sockaddr_storage
175 199
 #define FAMILY AF_INET6
176 200
 
177  
-#define SET_ADDR(dst, addr, port) do { \
178  
-    memset((char*)&(dst), 0, sizeof(dst)); \
179  
-    memcpy((char*)&(dst).sin6_addr.s6_addr, (char*)&(addr), 16); \
180  
-    (dst).sin6_family = AF_INET6; \
181  
-    (dst).sin6_flowinfo = 0; \
182  
-    (dst).sin6_port = htons(port); \
  201
+#define SET_ADDR6(dst, addr, port) do { \
  202
+    struct sockaddr_in6 *sa = (struct sockaddr_in6 *)&(dst); \
  203
+    memset(sa, 0, sizeof(dst)); \
  204
+    sa->sin6_family = AF_INET6; \
  205
+    sa->sin6_addr = (addr); \
  206
+    sa->sin6_port = htons(port); \
183 207
  } while(0)
184 208
 
185  
-#define IS_ADDR_LOOPBACK(addr) \
186  
-    (memcmp((addr).s6_addr, in6addr_loopback.s6_addr, 16) == 0)
  209
+#define SET_ADDR(dst, addr, port) do { \
  210
+    struct sockaddr_in *sa = (struct sockaddr_in *)&(dst); \
  211
+    memset(sa, 0, sizeof(dst)); \
  212
+    sa->sin_family = AF_INET; \
  213
+    sa->sin_addr.s_addr = (addr); \
  214
+    sa->sin_port = htons(port); \
  215
+ } while(0)
187 216
 
188 217
 #else /* Not IP v6 */
189 218
 
190 219
 #define EPMD_SOCKADDR_IN sockaddr_in
191  
-#define EPMD_IN_ADDR in_addr
192  
-#define EPMD_S_ADDR s_addr
193  
-#define EPMD_ADDR_LOOPBACK htonl(INADDR_LOOPBACK)
194  
-#define EPMD_ADDR_ANY htonl(INADDR_ANY)
195 220
 #define FAMILY AF_INET
196 221
 
197 222
 #define SET_ADDR(dst, addr, port) do { \
@@ -201,8 +226,6 @@
201 226
     (dst).sin_port = htons(port); \
202 227
  } while(0)
203 228
 
204  
-#define IS_ADDR_LOOPBACK(addr) ((addr).s_addr == htonl(INADDR_LOOPBACK))
205  
-
206 229
 #endif /* Not IP v6 */
207 230
 
208 231
 /* ************************************************************************ */
184  erts/epmd/src/epmd_srv.c
@@ -70,6 +70,7 @@ static time_t current_time(EpmdVars*);
70 70
 
71 71
 static Connection *conn_init(EpmdVars*);
72 72
 static int conn_open(EpmdVars*,int);
  73
+static int conn_local_peer_check(EpmdVars*, int);
73 74
 static int conn_close_fd(EpmdVars*,int);
74 75
 
75 76
 static void node_init(EpmdVars*);
@@ -200,7 +201,7 @@ void run(EpmdVars *g)
200 201
 {
201 202
   struct EPMD_SOCKADDR_IN iserv_addr[MAX_LISTEN_SOCKETS];
202 203
   int listensock[MAX_LISTEN_SOCKETS];
203  
-  int num_sockets;
  204
+  int num_sockets = 0;
204 205
   int i;
205 206
   int opt;
206 207
   unsigned short sport = g->port;
@@ -213,64 +214,82 @@ void run(EpmdVars *g)
213 214
   if (g->addresses != NULL && /* String contains non-separator characters if: */
214 215
       g->addresses[strspn(g->addresses," ,")] != '\000')
215 216
     {
216  
-      char *tmp;
217  
-      char *token;
218  
-      int loopback_ok = 0;
  217
+      char *tmp = NULL;
  218
+      char *token = NULL;
  219
+
  220
+      /* Always listen on the loopback. */
  221
+      SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_LOOPBACK),sport);
  222
+      num_sockets++;
  223
+#if defined(HAVE_IN6) && defined(AF_INET6)
  224
+      SET_ADDR6(iserv_addr[num_sockets],in6addr_loopback,sport);
  225
+      num_sockets++;
  226
+#endif
219 227
 
220  
-      if ((tmp = (char *)malloc(strlen(g->addresses) + 1)) == NULL)
  228
+	  if ((tmp = strdup(g->addresses)) == NULL)
221 229
 	{
222 230
 	  dbg_perror(g,"cannot allocate memory");
223 231
 	  epmd_cleanup_exit(g,1);
224 232
 	}
225  
-      strcpy(tmp,g->addresses);
226 233
 
227  
-      for(token = strtok(tmp,", "), num_sockets = 0;
  234
+      for(token = strtok(tmp,", ");
228 235
 	  token != NULL;
229  
-	  token = strtok(NULL,", "), num_sockets++)
  236
+	  token = strtok(NULL,", "))
230 237
 	{
231  
-	  struct EPMD_IN_ADDR addr;
232  
-#ifdef HAVE_INET_PTON
233  
-	  int ret;
  238
+	  struct in_addr addr;
  239
+#if defined(HAVE_IN6) && defined(AF_INET6)
  240
+	  struct in6_addr addr6;
  241
+	  struct sockaddr_storage *sa = &iserv_addr[num_sockets];
234 242
 
235  
-	  if ((ret = inet_pton(FAMILY,token,&addr)) == -1)
  243
+	  if (inet_pton(AF_INET,token,&addr) == 1)
236 244
 	    {
237  
-	      dbg_perror(g,"cannot convert IP address to network format");
238  
-	      epmd_cleanup_exit(g,1);
  245
+	      SET_ADDR(iserv_addr[num_sockets],addr.s_addr,sport);
  246
+	    }
  247
+	  else if (inet_pton(AF_INET6,token,&addr6) == 1)
  248
+	    {
  249
+	      SET_ADDR6(iserv_addr[num_sockets],addr6,sport);
  250
+	    }
  251
+	  else
  252
+#else
  253
+	  if ((addr.s_addr = inet_addr(token)) != INADDR_NONE)
  254
+	    {
  255
+	      SET_ADDR(iserv_addr[num_sockets],addr.s_addr,sport);
239 256
 	    }
240  
-	  else if (ret == 0)
241  
-#elif !defined(EPMD6)
242  
-	  if ((addr.EPMD_S_ADDR = inet_addr(token)) == INADDR_NONE)
  257
+	  else
243 258
 #endif
244 259
 	    {
245 260
 	      dbg_tty_printf(g,0,"cannot parse IP address \"%s\"",token);
246 261
 	      epmd_cleanup_exit(g,1);
247 262
 	    }
248 263
 
  264
+#if defined(HAVE_IN6) && defined(AF_INET6)
  265
+	  if (sa->ss_family == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&addr6))
  266
+	      continue;
  267
+
  268
+	  if (sa->ss_family == AF_INET)
  269
+#endif
249 270
 	  if (IS_ADDR_LOOPBACK(addr))
250  
-	    loopback_ok = 1;
  271
+	    continue;
251 272
 
252  
-	  if (num_sockets - loopback_ok == MAX_LISTEN_SOCKETS - 1)
  273
+	  num_sockets++;
  274
+
  275
+	  if (num_sockets >= MAX_LISTEN_SOCKETS)
253 276
 	    {
254 277
 	      dbg_tty_printf(g,0,"cannot listen on more than %d IP addresses",
255 278
 			     MAX_LISTEN_SOCKETS);
256 279
 	      epmd_cleanup_exit(g,1);
257 280
 	    }
258  
-
259  
-	  SET_ADDR(iserv_addr[num_sockets],addr.EPMD_S_ADDR,sport);
260 281
 	}
261 282
 
262 283
       free(tmp);
263  
-
264  
-      if (!loopback_ok)
265  
-	{
266  
-	  SET_ADDR(iserv_addr[num_sockets],EPMD_ADDR_LOOPBACK,sport);
267  
-	  num_sockets++;
268  
-	}
269 284
     }
270 285
   else
271 286
     {
272  
-      SET_ADDR(iserv_addr[0],EPMD_ADDR_ANY,sport);
273  
-      num_sockets = 1;
  287
+      SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_ANY),sport);
  288
+      num_sockets++;
  289
+#if defined(HAVE_IN6) && defined(AF_INET6)
  290
+      SET_ADDR6(iserv_addr[num_sockets],in6addr_any,sport);
  291
+      num_sockets++;
  292
+#endif
274 293
     }
275 294
 
276 295
 #if !defined(__WIN32__)
@@ -291,13 +310,33 @@ void run(EpmdVars *g)
291 310
 
292 311
   for (i = 0; i < num_sockets; i++)
293 312
     {
294  
-      if ((listensock[i] = socket(FAMILY,SOCK_STREAM,0)) < 0)
  313
+      struct sockaddr *sa = (struct sockaddr *)&iserv_addr[i];
  314
+#if defined(HAVE_IN6) && defined(AF_INET6)
  315
+      size_t salen = (sa->sa_family == AF_INET6 ?
  316
+              sizeof(struct sockaddr_in6) :
  317
+              sizeof(struct sockaddr_in));
  318
+#else
  319
+      size_t salen = sizeof(struct sockaddr_in);
  320
+#endif
  321
+
  322
+      if ((listensock[i] = socket(sa->sa_family,SOCK_STREAM,0)) < 0)
295 323
 	{
296 324
 	  dbg_perror(g,"error opening stream socket");
297 325
 	  epmd_cleanup_exit(g,1);
298 326
 	}
299 327
       g->listenfd[i] = listensock[i];
300  
-  
  328
+
  329
+#if HAVE_DECL_IPV6_V6ONLY
  330
+      opt = 1;
  331
+      if (sa->sa_family == AF_INET6 &&
  332
+          setsockopt(listensock[i],IPPROTO_IPV6,IPV6_V6ONLY,&opt,
  333
+              sizeof(opt)) <0)
  334
+	{
  335
+	  dbg_perror(g,"can't set IPv6 only socket option");
  336
+	  epmd_cleanup_exit(g,1);
  337
+	}
  338
+#endif
  339
+
301 340
       /*
302 341
        * Note that we must not enable the SO_REUSEADDR on Windows,
303 342
        * because addresses will be reused even if they are still in use.
@@ -329,8 +368,7 @@ void run(EpmdVars *g)
329 368
 	dbg_perror(g,"failed to set non-blocking mode of listening socket %d",
330 369
 		   listensock[i]);
331 370
 
332  
-      if (bind(listensock[i], (struct sockaddr*) &iserv_addr[i],
333  
-	  sizeof(iserv_addr[i])) < 0)
  371
+      if (bind(listensock[i], (struct sockaddr*) &iserv_addr[i], salen) < 0)
334 372
 	{
335 373
 	  if (errno == EADDRINUSE)
336 374
 	    {
@@ -952,15 +990,6 @@ static int conn_open(EpmdVars *g,int fd)
952 990
 
953 991
   for (i = 0; i < g->max_conn; i++) {
954 992
     if (g->conn[i].open == EPMD_FALSE) {
955  
-      struct sockaddr_in si;
956  
-      struct sockaddr_in di;
957  
-#ifdef HAVE_SOCKLEN_T
958  
-      socklen_t st;
959  
-#else
960  
-      int st;
961  
-#endif
962  
-      st = sizeof(si);
963  
-
964 993
       g->active_conn++;
965 994
       s = &g->conn[i];
966 995
      
@@ -971,20 +1000,7 @@ static int conn_open(EpmdVars *g,int fd)
971 1000
       s->open = EPMD_TRUE;
972 1001
       s->keep = EPMD_FALSE;
973 1002
 
974  
-      /* Determine if connection is from localhost */
975  
-      if (getpeername(s->fd,(struct sockaddr*) &si,&st) ||
976  
-	  st < sizeof(si)) {
977  
-	  /* Failure to get peername is regarded as non local host */
978  
-	  s->local_peer = EPMD_FALSE;
979  
-      } else {
980  
-	  /* Only 127.x.x.x and connections from the host's IP address
981  
-	     allowed, no false positives */
982  
-	  s->local_peer =
983  
-	      (((((unsigned) ntohl(si.sin_addr.s_addr)) & 0xFF000000U) ==
984  
-	       0x7F000000U) ||
985  
-	       (getsockname(s->fd,(struct sockaddr*) &di,&st) ?
986  
-	       EPMD_FALSE : si.sin_addr.s_addr == di.sin_addr.s_addr));
987  
-      }
  1003
+      s->local_peer = conn_local_peer_check(g, s->fd);
988 1004
       dbg_tty_printf(g,2,(s->local_peer) ? "Local peer connected" :
989 1005
 		     "Non-local peer connected");
990 1006
 
@@ -992,7 +1008,7 @@ static int conn_open(EpmdVars *g,int fd)
992 1008
       s->got  = 0;
993 1009
       s->mod_time = current_time(g); /* Note activity */
994 1010
 
995  
-      s->buf = (char *)malloc(INBUF_SIZE);
  1011
+      s->buf = malloc(INBUF_SIZE);
996 1012
 
997 1013
       if (s->buf == NULL) {
998 1014
 	dbg_printf(g,0,"epmd: Insufficient memory");
@@ -1010,6 +1026,60 @@ static int conn_open(EpmdVars *g,int fd)
1010 1026
   return EPMD_FALSE;
1011 1027
 }
1012 1028
 
  1029
+static int conn_local_peer_check(EpmdVars *g, int fd)
  1030
+{
  1031
+  struct EPMD_SOCKADDR_IN si;
  1032
+  struct EPMD_SOCKADDR_IN di;
  1033
+
  1034
+  struct sockaddr_in *si4 = (struct sockaddr_in *)&si;
  1035
+  struct sockaddr_in *di4 = (struct sockaddr_in *)&di;
  1036
+
  1037
+#if defined(HAVE_IN6) && defined(AF_INET6)
  1038
+  struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&si;
  1039
+  struct sockaddr_in6 *di6 = (struct sockaddr_in6 *)&di;
  1040
+#endif
  1041
+
  1042
+#ifdef HAVE_SOCKLEN_T
  1043
+  socklen_t st;
  1044
+#else
  1045
+  int st;
  1046
+#endif
  1047
+
  1048
+  st = sizeof(si);
  1049
+
  1050
+  /* Determine if connection is from localhost */
  1051
+  if (getpeername(fd,(struct sockaddr*) &si,&st) ||
  1052
+	  st > sizeof(si)) {
  1053
+	  /* Failure to get peername is regarded as non local host */
  1054
+	  return EPMD_FALSE;
  1055
+  }
  1056
+
  1057
+  /* Only 127.x.x.x and connections from the host's IP address
  1058
+	 allowed, no false positives */
  1059
+#if defined(HAVE_IN6) && defined(AF_INET6)
  1060
+  if (si.ss_family == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&(si6->sin6_addr)))
  1061
+	  return EPMD_TRUE;
  1062
+
  1063
+  if (si.ss_family == AF_INET)
  1064
+#endif
  1065
+  if ((((unsigned) ntohl(si4->sin_addr.s_addr)) & 0xFF000000U) ==
  1066
+	  0x7F000000U)
  1067
+	  return EPMD_TRUE;
  1068
+
  1069
+  if (getsockname(fd,(struct sockaddr*) &di,&st))
  1070
+	  return EPMD_FALSE;
  1071
+
  1072
+#if defined(HAVE_IN6) && defined(AF_INET6)
  1073
+  if (si.ss_family == AF_INET6)
  1074
+      return IN6_ARE_ADDR_EQUAL( &(si6->sin6_addr), &(di6->sin6_addr));
  1075
+  if (si.ss_family == AF_INET)
  1076
+#endif
  1077
+  return si4->sin_addr.s_addr == di4->sin_addr.s_addr;
  1078
+#if defined(HAVE_IN6) && defined(AF_INET6)
  1079
+  return EPMD_FALSE;
  1080
+#endif
  1081
+}
  1082
+
1013 1083
 static int conn_close_fd(EpmdVars *g,int fd)
1014 1084
 {
1015 1085
   int i;
33  erts/epmd/test/epmd_SUITE.erl
@@ -42,6 +42,7 @@
42 42
 -export(
43 43
    [
44 44
     register_name/1,
  45
+    register_name_ipv6/1,
45 46
     register_names_1/1,
46 47
     register_names_2/1,
47 48
     register_duplicate_name/1,
@@ -108,7 +109,8 @@
108 109
 suite() -> [{ct_hooks,[ts_install_cth]}].
109 110
 
110 111
 all() -> 
111  
-    [register_name, register_names_1, register_names_2,
  112
+    [register_name, register_name_ipv6,
  113
+     register_names_1, register_names_2,
112 114
      register_duplicate_name, unicode_name, long_unicode_name,
113 115
      get_port_nr, slow_get_port_nr,
114 116
      unregister_others_name_1, unregister_others_name_2,
@@ -165,6 +167,24 @@ register_name(Config) when is_list(Config) ->
165 167
     ?line ok = close(Sock),			% Unregister
166 168
     ok.
167 169
 
  170
+register_name_ipv6(doc) ->
  171
+    ["Register a name over IPv6"];
  172
+register_name_ipv6(suite) ->
  173
+    [];
  174
+register_name_ipv6(Config) when is_list(Config) ->
  175
+    % Test if the host has an IPv6 loopback address
  176
+    Res = gen_tcp:listen(0, [inet6, {ip, {0,0,0,0,0,0,0,1}}]),
  177
+    case Res of
  178
+    {ok,LSock} ->
  179
+	    gen_tcp:close(LSock),
  180
+	    ?line ok = epmdrun(),
  181
+	    ?line {ok,Sock} = register_node6("foobar6"),
  182
+	    ?line ok = close(Sock),         % Unregister
  183
+	    ok;
  184
+    _Error ->
  185
+	    {skip, "Host does not have an IPv6 loopback address"}
  186
+    end.
  187
+
168 188
 register_names_1(doc) ->
169 189
     ["Register and unregister two nodes"];
170 190
 register_names_1(suite) ->
@@ -238,13 +258,18 @@ register_node(Name) ->
238 258
 register_node(Name,Port) ->
239 259
     register_node_v2(Port,$M,0,5,5,Name,"").
240 260
 
  261
+register_node6(Name) ->
  262
+    register_node_v2({0,0,0,0,0,0,0,1},?DUMMY_PORT,$M,0,5,5,Name,"").
  263
+
241 264
 register_node_v2(Port, NodeType, Prot, HVsn, LVsn, Name, Extra) ->
  265
+    register_node_v2("localhost", Port, NodeType, Prot, HVsn, LVsn, Name, Extra).
  266
+register_node_v2(Addr, Port, NodeType, Prot, HVsn, LVsn, Name, Extra) ->
242 267
     Utf8Name = unicode:characters_to_binary(Name),
243 268
     Req = [?EPMD_ALIVE2_REQ, put16(Port), NodeType, Prot,
244 269
 	   put16(HVsn), put16(LVsn),
245 270
 	   put16(size(Utf8Name)), binary_to_list(Utf8Name),
246 271
 	   size16(Extra), Extra],
247  
-    case send_req(Req) of
  272
+    case send_req(Req, Addr) of
248 273
 	{ok,Sock} ->
249 274
 	    case recv(Sock,4) of
250 275
 		{ok, [?EPMD_ALIVE2_RESP,_Res=0,_C0,_C1]} ->
@@ -1129,7 +1154,9 @@ send_direct(Sock, Bytes) ->
1129 1154
     end.
1130 1155
 
1131 1156
 send_req(Req) ->
1132  
-    case connect() of
  1157
+    send_req(Req, "localhost").
  1158
+send_req(Req, Addr) ->
  1159
+    case connect(Addr) of
1133 1160
 	{ok,Sock} ->
1134 1161
 	    case send(Sock, [size16(Req), Req]) of
1135 1162
 		ok ->
18  lib/kernel/src/erl_epmd.erl
@@ -31,7 +31,7 @@
31 31
 %% External exports
32 32
 -export([start/0, start_link/0, stop/0, port_please/2, 
33 33
 	 port_please/3, names/0, names/1,
34  
-	 register_node/2, open/0, open/1, open/2]).
  34
+	 register_node/2, register_node/3, open/0, open/1, open/2]).
35 35
 
36 36
 %% gen_server callbacks
37 37
 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 
@@ -106,7 +106,9 @@ names1(HostName) ->
106 106
 
107 107
 
108 108
 register_node(Name, PortNo) ->
109  
-    gen_server:call(erl_epmd, {register, Name, PortNo}, infinity).
  109
+    register_node(Name, PortNo, inet).
  110
+register_node(Name, PortNo, Family) ->
  111
+    gen_server:call(erl_epmd, {register, Name, PortNo, Family}, infinity).
110 112
 
111 113
 %%%----------------------------------------------------------------------
112 114
 %%% Callback functions from gen_server
@@ -124,10 +126,10 @@ init(_) ->
124 126
 -spec handle_call(calls(), term(), state()) ->
125 127
         {'reply', term(), state()} | {'stop', 'shutdown', 'ok', state()}.
126 128
 
127  
-handle_call({register, Name, PortNo}, _From, State) ->
  129
+handle_call({register, Name, PortNo, Family}, _From, State) ->
128 130
     case State#state.socket of
129 131
 	P when P < 0 ->
130  
-	    case do_register_node(Name, PortNo) of
  132
+	    case do_register_node(Name, PortNo, Family) of
131 133
 		{alive, Socket, Creation} ->
132 134
 		    S = State#state{socket = Socket,
133 135
 				    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) ->
210 212
 close(Socket) ->
211 213
     gen_tcp:close(Socket).
212 214
 
213  
-do_register_node(NodeName, TcpPort) ->
214  
-    case open() of
  215
+do_register_node(NodeName, TcpPort, Family) ->
  216
+    Localhost = case Family of
  217
+        inet -> open({127,0,0,1});
  218
+        inet6 -> open({0,0,0,0,0,0,0,1})
  219
+    end,
  220
+    case Localhost of
215 221
 	{ok, Socket} ->
216 222
 	    Name = to_string(NodeName),
217 223
 	    Extra = "",
2  lib/kernel/src/inet6_tcp_dist.erl
@@ -71,7 +71,7 @@ listen(Name) ->
71 71
         {ok, Socket} ->
72 72
             TcpAddress = get_tcp_address(Socket),
73 73
             {_,Port} = TcpAddress#net_address.address,
74  
-            case erl_epmd:register_node(Name, Port) of
  74
+            case erl_epmd:register_node(Name, Port, inet6) of
75 75
                 {ok, Creation} ->
76 76
                     {ok, {Socket, TcpAddress, Creation}};
77 77
                 Error ->

No commit comments for this range

Something went wrong with that request. Please try again.