Skip to content
This repository has been archived by the owner on May 4, 2018. It is now read-only.

Commit

Permalink
linux: don't turn on SO_REUSEPORT socket option
Browse files Browse the repository at this point in the history
On the BSDs, SO_REUSEPORT is pretty much SO_REUSEADDR with some special
casing for IP multicast.  When two processes (that don't do multicast)
bind to the same address, only the last one receives traffic.  It allows
one to "steal" the bound address from another process.  (Both processes
have to enable SO_REUSEPORT though, so it only works in a cooperative
setting.)

On Linux however, it enables port sharing, not stealing - both processes
receive a share of the traffic.  This is a desirable trait but pre-3.9
kernels don't support the socket option and a libuv program therefore
behaves differently with older kernels or on another platform.

This is a back-port of commit 9d60f1e from the master branch.

Fixes nodejs/node-v0.x-archive#6454.
  • Loading branch information
bnoordhuis committed Oct 30, 2013
1 parent 3780e12 commit 3d2c820
Showing 1 changed file with 29 additions and 19 deletions.
48 changes: 29 additions & 19 deletions src/unix/udp.c
Expand Up @@ -286,13 +286,39 @@ static void uv__udp_sendmsg(uv_loop_t* loop,
}


/* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional
* refinements for programs that use multicast.
*
* Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that
* are different from the BSDs: it _shares_ the port rather than steal it
* from the current listener. While useful, it's not something we can emulate
* on other platforms so we don't enable it.
*/
static int uv__set_reuse(int fd) {
int yes;

#if defined(SO_REUSEPORT) && !defined(__linux__)
yes = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
return -errno;
#else
yes = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
return -errno;
#endif

return 0;
}


static int uv__bind(uv_udp_t* handle,
int domain,
struct sockaddr* addr,
socklen_t len,
unsigned flags) {
int saved_errno;
int status;
int err;
int yes;
int fd;

Expand Down Expand Up @@ -321,28 +347,12 @@ static int uv__bind(uv_udp_t* handle,
}

fd = handle->io_watcher.fd;
yes = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) {
uv__set_sys_error(handle->loop, errno);
err = uv__set_reuse(fd);
if (err) {
uv__set_sys_error(handle->loop, -err);
goto out;
}

/* On the BSDs, SO_REUSEADDR lets you reuse an address that's in the TIME_WAIT
* state (i.e. was until recently tied to a socket) while SO_REUSEPORT lets
* multiple processes bind to the same address. Yes, it's something of a
* misnomer but then again, SO_REUSEADDR was already taken.
*
* None of the above applies to Linux: SO_REUSEADDR implies SO_REUSEPORT on
* Linux and hence it does not have SO_REUSEPORT at all.
*/
#ifdef SO_REUSEPORT
yes = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof yes) == -1) {
uv__set_sys_error(handle->loop, errno);
goto out;
}
#endif

if (flags & UV_UDP_IPV6ONLY) {
#ifdef IPV6_V6ONLY
yes = 1;
Expand Down

0 comments on commit 3d2c820

Please sign in to comment.