MTUDPSocket.hpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /*
  2. * ZeroTier One - Network Virtualization Everywhere
  3. * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. * --
  19. *
  20. * You can be released from the requirements of the license by purchasing
  21. * a commercial license. Buying such a license is mandatory as soon as you
  22. * develop commercial closed-source software that incorporates or links
  23. * directly against ZeroTier software without disclosing the source code
  24. * of your own application.
  25. */
  26. #ifndef ZT_MTUDPSOCKET_HPP
  27. #define ZT_MTUDPSOCKET_HPP
  28. #ifndef __WINDOWS__
  29. #include "../node/Constants.hpp"
  30. #include "../include/ZeroTierOne.h"
  31. #include "../osdep/OSUtils.hpp"
  32. #include "../osdep/Thread.hpp"
  33. #include <vector>
  34. #include <sys/types.h>
  35. #include <sys/socket.h>
  36. #include <unistd.h>
  37. namespace ZeroTier {
  38. /**
  39. * MTUDPSocket is a multithreaded UDP socket using multiple binds and SO_REUSEPORT
  40. *
  41. * On Mac and Linux this is the most efficient way to implement a multithreaded UDP
  42. * I/O path. On Windows it's probably not necessary to optimize this much. If it ever
  43. * is, we will have to implement a version of this the Windows way.
  44. */
  45. class MTUDPSocket
  46. {
  47. public:
  48. inline MTUDPSocket(ZT_Node *n,volatile int64_t *dptr,const struct sockaddr *bindAddr)
  49. {
  50. const int ncores = std::max(1,(int)sysconf(_SC_NPROCESSORS_CONF));
  51. for(int t=0;t<ncores;t++) {
  52. int s = socket(bindAddr->sa_family,SOCK_DGRAM,0);
  53. if (s < 0) {
  54. for(auto i=_sockets.begin();i!=_sockets.end();++i)
  55. close(*i);
  56. throw std::runtime_error("unable to allocate socket");
  57. }
  58. int f = 131072;
  59. setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char *)&f,sizeof(f));
  60. f = 131072;
  61. setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char *)&f,sizeof(f));
  62. if (bindAddr->sa_family == AF_INET6) {
  63. f = 1; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f));
  64. #ifdef IPV6_MTU_DISCOVER
  65. f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_MTU_DISCOVER,&f,sizeof(f));
  66. #endif
  67. #ifdef IPV6_DONTFRAG
  68. f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_DONTFRAG,&f,sizeof(f));
  69. #endif
  70. }
  71. f = 1; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f));
  72. f = 1; setsockopt(s,SOL_SOCKET,SO_REUSEPORT,(void *)&f,sizeof(f));
  73. f = 1; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(void *)&f,sizeof(f));
  74. #ifdef IP_DONTFRAG
  75. f = 0; setsockopt(s,IPPROTO_IP,IP_DONTFRAG,&f,sizeof(f));
  76. #endif
  77. #ifdef IP_MTU_DISCOVER
  78. f = 0; setsockopt(s,IPPROTO_IP,IP_MTU_DISCOVER,&f,sizeof(f));
  79. #endif
  80. #ifdef SO_NO_CHECK
  81. if (bindAddr->sa_family == AF_INET) {
  82. f = 1; setsockopt(s,SOL_SOCKET,SO_NO_CHECK,(void *)&f,sizeof(f));
  83. }
  84. #endif
  85. if (bind(s,bindAddr,(bindAddr->sa_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))) {
  86. for(auto i=_sockets.begin();i!=_sockets.end();++i)
  87. close(*i);
  88. throw std::runtime_error("unable to bind to address");
  89. }
  90. _sockets.push_back(s);
  91. }
  92. for(auto s=_sockets.begin();s!=_sockets.end();++s) {
  93. try {
  94. new MTUDPThread(n,dptr,*s);
  95. } catch ( ... ) {
  96. for(auto i=_sockets.begin();i!=_sockets.end();++i)
  97. close(*i);
  98. throw;
  99. }
  100. }
  101. }
  102. inline ~MTUDPSocket()
  103. {
  104. for(auto i=_sockets.begin();i!=_sockets.end();++i)
  105. close(*i);
  106. }
  107. private:
  108. class MTUDPThread
  109. {
  110. public:
  111. inline MTUDPThread(ZT_Node *n,volatile int64_t *dptr,int s) :
  112. node(n),
  113. deadlinePtr(dptr),
  114. sock(s),
  115. thread(Thread::start(this))
  116. {
  117. }
  118. inline void threadMain()
  119. {
  120. struct sockaddr_storage from;
  121. for(;;) {
  122. socklen_t fromLen = sizeof(from);
  123. const int nr = recvfrom(this->sock,this->buf,sizeof(this->buf),0,(struct sockaddr *)&from,&fromLen);
  124. if (nr > 0) {
  125. ZT_Node_processWirePacket(this->node,nullptr,OSUtils::now(),(int64_t)this->sock,&from,this->buf,(unsigned int)nr,this->deadlinePtr);
  126. } else {
  127. close(this->sock);
  128. break;
  129. }
  130. }
  131. delete this; // closing the socket causes this to exit and delete itself
  132. }
  133. ZT_Node *const node;
  134. volatile int64_t *const deadlinePtr;
  135. const int sock;
  136. Thread thread;
  137. char buf[10000];
  138. };
  139. std::vector<int> _sockets;
  140. };
  141. } // namespace ZeroTier
  142. #endif // !__WINDOWS__
  143. #endif