--- ./src/unix/udp.c 2013-05-14 19:50:19.000000000 -0400 +++ ./src/unix/udp.c 2013-08-24 10:19:11.446792000 -0400 @@ -286,6 +286,53 @@ } +/* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but it also lets you share + * the address and port with other processes. + * + * Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that + * are different from the BSDs. The address:port sharing part is taken care + * of by SO_REUSEADDR while SO_REUSEPORT enables fair load distribution. (If + * you wonder why you need to explicitly enable that, well, it's complicated.) + * + * Because we cannot rely on SO_REUSEPORT being available on Linux, it's not + * considered an error when the setsockopt() system call fails. Worst case, + * the program has sub-optimal load distribution characteristics but should + * otherwise run fine. + */ +static int uv__set_reuse(int fd) { + int yes; +#if defined(__linux__) + static int no_so_reuseport; + + if (no_so_reuseport) + goto no_so_reuseport; + + yes = 1; + if (setsockopt(fd, SOL_SOCKET, 15 /* SO_REUSEPORT */, &yes, sizeof(yes))) { + if (errno != EINVAL && errno != ENOPROTOOPT) + return -errno; + no_so_reuseport = 1; + } + +no_so_reuseport: + + yes = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) + return -errno; +#elif defined(SO_REUSEPORT) + 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, @@ -295,6 +342,7 @@ int status; int yes; int fd; + int err; saved_errno = errno; status = -1; @@ -321,28 +369,12 @@ } 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; @@ -481,7 +513,7 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { int saved_errno; int status; - int yes; + int err; saved_errno = errno; status = -1; @@ -492,27 +524,11 @@ goto out; } - yes = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) { - uv__set_sys_error(handle->loop, errno); - 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(sock, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof yes) == -1) { - uv__set_sys_error(handle->loop, errno); + err = uv__set_reuse(sock); + if (err) { + uv__set_sys_error(handle->loop, -err); goto out; } -#endif handle->io_watcher.fd = sock; status = 0;