Skip to content

Commit

Permalink
epmd: support IPv6 node registration
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
msantos committed May 3, 2013
1 parent d08b977 commit d3d6846
Show file tree
Hide file tree
Showing 9 changed files with 248 additions and 93 deletions.
2 changes: 1 addition & 1 deletion erts/doc/src/epmd.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
22 changes: 22 additions & 0 deletions erts/doc/src/erl.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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@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>
Expand Down
6 changes: 3 additions & 3 deletions erts/epmd/src/epmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
11 changes: 9 additions & 2 deletions erts/epmd/src/epmd_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;

Expand Down
63 changes: 43 additions & 20 deletions erts/epmd/src/epmd_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,14 @@
#include <string.h>

#ifdef __WIN32__
# ifdef _WIN32_WINVER
# undef _WIN32_WINVER
# endif
# define _WIN32_WINVER 0x0501
# ifndef WINDOWS_H_INCLUDES_WINSOCK2_H
# include <winsock2.h>
# endif
# include <ws2tcpip.h>
# include <windows.h>
# include <process.h>
#endif
Expand Down Expand Up @@ -165,33 +170,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 { \
Expand All @@ -201,8 +226,6 @@
(dst).sin_port = htons(port); \
} while(0)

#define IS_ADDR_LOOPBACK(addr) ((addr).s_addr == htonl(INADDR_LOOPBACK))

#endif /* Not IP v6 */

/* ************************************************************************ */
Expand Down
Loading

0 comments on commit d3d6846

Please sign in to comment.