uv_so_reuseport.patch 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. --- ./src/unix/udp.c 2013-05-14 19:50:19.000000000 -0400
  2. +++ ./src/unix/udp.c 2013-08-24 10:19:11.446792000 -0400
  3. @@ -286,6 +286,53 @@
  4. }
  5. +/* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but it also lets you share
  6. + * the address and port with other processes.
  7. + *
  8. + * Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that
  9. + * are different from the BSDs. The address:port sharing part is taken care
  10. + * of by SO_REUSEADDR while SO_REUSEPORT enables fair load distribution. (If
  11. + * you wonder why you need to explicitly enable that, well, it's complicated.)
  12. + *
  13. + * Because we cannot rely on SO_REUSEPORT being available on Linux, it's not
  14. + * considered an error when the setsockopt() system call fails. Worst case,
  15. + * the program has sub-optimal load distribution characteristics but should
  16. + * otherwise run fine.
  17. + */
  18. +static int uv__set_reuse(int fd) {
  19. + int yes;
  20. +#if defined(__linux__)
  21. + static int no_so_reuseport;
  22. +
  23. + if (no_so_reuseport)
  24. + goto no_so_reuseport;
  25. +
  26. + yes = 1;
  27. + if (setsockopt(fd, SOL_SOCKET, 15 /* SO_REUSEPORT */, &yes, sizeof(yes))) {
  28. + if (errno != EINVAL && errno != ENOPROTOOPT)
  29. + return -errno;
  30. + no_so_reuseport = 1;
  31. + }
  32. +
  33. +no_so_reuseport:
  34. +
  35. + yes = 1;
  36. + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
  37. + return -errno;
  38. +#elif defined(SO_REUSEPORT)
  39. + yes = 1;
  40. + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
  41. + return -errno;
  42. +#else
  43. + yes = 1;
  44. + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
  45. + return -errno;
  46. +#endif
  47. +
  48. + return 0;
  49. +}
  50. +
  51. +
  52. static int uv__bind(uv_udp_t* handle,
  53. int domain,
  54. struct sockaddr* addr,
  55. @@ -295,6 +342,7 @@
  56. int status;
  57. int yes;
  58. int fd;
  59. + int err;
  60. saved_errno = errno;
  61. status = -1;
  62. @@ -321,28 +369,12 @@
  63. }
  64. fd = handle->io_watcher.fd;
  65. - yes = 1;
  66. - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) {
  67. - uv__set_sys_error(handle->loop, errno);
  68. + err = uv__set_reuse(fd);
  69. + if (err) {
  70. + uv__set_sys_error(handle->loop, -err);
  71. goto out;
  72. }
  73. - /* On the BSDs, SO_REUSEADDR lets you reuse an address that's in the TIME_WAIT
  74. - * state (i.e. was until recently tied to a socket) while SO_REUSEPORT lets
  75. - * multiple processes bind to the same address. Yes, it's something of a
  76. - * misnomer but then again, SO_REUSEADDR was already taken.
  77. - *
  78. - * None of the above applies to Linux: SO_REUSEADDR implies SO_REUSEPORT on
  79. - * Linux and hence it does not have SO_REUSEPORT at all.
  80. - */
  81. -#ifdef SO_REUSEPORT
  82. - yes = 1;
  83. - if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof yes) == -1) {
  84. - uv__set_sys_error(handle->loop, errno);
  85. - goto out;
  86. - }
  87. -#endif
  88. -
  89. if (flags & UV_UDP_IPV6ONLY) {
  90. #ifdef IPV6_V6ONLY
  91. yes = 1;
  92. @@ -481,7 +513,7 @@
  93. int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
  94. int saved_errno;
  95. int status;
  96. - int yes;
  97. + int err;
  98. saved_errno = errno;
  99. status = -1;
  100. @@ -492,27 +524,11 @@
  101. goto out;
  102. }
  103. - yes = 1;
  104. - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) {
  105. - uv__set_sys_error(handle->loop, errno);
  106. - goto out;
  107. - }
  108. -
  109. - /* On the BSDs, SO_REUSEADDR lets you reuse an address that's in the TIME_WAIT
  110. - * state (i.e. was until recently tied to a socket) while SO_REUSEPORT lets
  111. - * multiple processes bind to the same address. Yes, it's something of a
  112. - * misnomer but then again, SO_REUSEADDR was already taken.
  113. - *
  114. - * None of the above applies to Linux: SO_REUSEADDR implies SO_REUSEPORT on
  115. - * Linux and hence it does not have SO_REUSEPORT at all.
  116. - */
  117. -#ifdef SO_REUSEPORT
  118. - yes = 1;
  119. - if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof yes) == -1) {
  120. - uv__set_sys_error(handle->loop, errno);
  121. + err = uv__set_reuse(sock);
  122. + if (err) {
  123. + uv__set_sys_error(handle->loop, -err);
  124. goto out;
  125. }
  126. -#endif
  127. handle->io_watcher.fd = sock;
  128. status = 0;