network.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. #include "network.h"
  2. #include <util/folder/dirut.h>
  3. #include <util/folder/path.h>
  4. #include <util/generic/singleton.h>
  5. #include <util/generic/utility.h>
  6. #include <util/generic/vector.h>
  7. #include <util/generic/ylimits.h>
  8. #include <util/network/address.h>
  9. #include <util/network/sock.h>
  10. #include <util/random/random.h>
  11. #include <util/stream/file.h>
  12. #include <util/string/split.h>
  13. #include <util/system/env.h>
  14. #include <util/system/error.h>
  15. #include <util/system/file_lock.h>
  16. #include <util/system/fs.h>
  17. #ifdef _darwin_
  18. #include <sys/types.h>
  19. #include <sys/sysctl.h>
  20. #endif
  21. namespace {
  22. #define Y_VERIFY_SYSERROR(expr) \
  23. do { \
  24. if (!(expr)) { \
  25. Y_ABORT(#expr ", errno=%d", LastSystemError()); \
  26. } \
  27. } while (false)
  28. class TPortGuard : public NTesting::IPort {
  29. public:
  30. TPortGuard(ui16 port, THolder<TFileLock> lock)
  31. : Lock_(std::move(lock))
  32. , Port_(port)
  33. {
  34. }
  35. ~TPortGuard() override {
  36. Y_VERIFY_SYSERROR(NFs::Remove(Lock_->GetName()));
  37. }
  38. ui16 Get() override {
  39. return Port_;
  40. }
  41. private:
  42. THolder<TFileLock> Lock_;
  43. ui16 Port_;
  44. };
  45. std::pair<ui16, ui16> GetEphemeralRange() {
  46. // IANA suggestion
  47. std::pair<ui16, ui16> pair{(1 << 15) + (1 << 14), (1 << 16) - 1};
  48. #ifdef _linux_
  49. if (NFs::Exists("/proc/sys/net/ipv4/ip_local_port_range")) {
  50. TIFStream fileStream("/proc/sys/net/ipv4/ip_local_port_range");
  51. fileStream >> pair.first >> pair.second;
  52. }
  53. #endif
  54. #ifdef _darwin_
  55. ui32 first, last;
  56. size_t size;
  57. sysctlbyname("net.inet.ip.portrange.first", &first, &size, NULL, 0);
  58. sysctlbyname("net.inet.ip.portrange.last", &last, &size, NULL, 0);
  59. pair.first = first;
  60. pair.second = last;
  61. #endif
  62. return pair;
  63. }
  64. TVector<std::pair<ui16, ui16>> GetPortRanges() {
  65. TString givenRange = GetEnv("VALID_PORT_RANGE");
  66. TVector<std::pair<ui16, ui16>> ranges;
  67. if (givenRange.Contains(':')) {
  68. auto res = StringSplitter(givenRange).Split(':').Limit(2).ToList<TString>();
  69. ranges.emplace_back(FromString<ui16>(res.front()), FromString<ui16>(res.back()));
  70. } else {
  71. const ui16 firstValid = 1025;
  72. const ui16 lastValid = Max<ui16>();
  73. auto [firstEphemeral, lastEphemeral] = GetEphemeralRange();
  74. const ui16 firstInvalid = Max(firstEphemeral, firstValid);
  75. const ui16 lastInvalid = Min(lastEphemeral, lastValid);
  76. if (firstInvalid > firstValid)
  77. ranges.emplace_back(firstValid, firstInvalid - 1);
  78. if (lastInvalid < lastValid)
  79. ranges.emplace_back(lastInvalid + 1, lastValid);
  80. }
  81. return ranges;
  82. }
  83. class TPortManager {
  84. static constexpr size_t Retries = 20;
  85. public:
  86. TPortManager()
  87. {
  88. InitFromEnv();
  89. }
  90. void InitFromEnv() {
  91. SyncDir_ = TFsPath(GetEnv("PORT_SYNC_PATH"));
  92. if (!SyncDir_.IsDefined()) {
  93. SyncDir_ = TFsPath(GetSystemTempDir()) / "testing_port_locks";
  94. }
  95. Y_ABORT_UNLESS(SyncDir_.IsDefined());
  96. NFs::MakeDirectoryRecursive(SyncDir_);
  97. Ranges_ = GetPortRanges();
  98. TotalCount_ = 0;
  99. for (auto [left, right] : Ranges_) {
  100. TotalCount_ += right - left;
  101. }
  102. Y_ABORT_UNLESS(0 != TotalCount_);
  103. DisableRandomPorts_ = !GetEnv("NO_RANDOM_PORTS").empty();
  104. }
  105. NTesting::TPortHolder GetFreePort() const {
  106. ui16 salt = RandomNumber<ui16>();
  107. for (ui16 attempt = 0; attempt < TotalCount_; ++attempt) {
  108. ui16 probe = (salt + attempt) % TotalCount_;
  109. for (auto [left, right] : Ranges_) {
  110. if (probe >= right - left)
  111. probe -= right - left;
  112. else {
  113. probe += left;
  114. break;
  115. }
  116. }
  117. auto port = TryAcquirePort(probe);
  118. if (port) {
  119. return NTesting::TPortHolder{std::move(port)};
  120. }
  121. }
  122. Y_ABORT("Cannot get free port!");
  123. }
  124. TVector<NTesting::TPortHolder> GetFreePortsRange(size_t count) const {
  125. Y_ABORT_UNLESS(count > 0);
  126. TVector<NTesting::TPortHolder> ports(Reserve(count));
  127. for (size_t i = 0; i < Retries; ++i) {
  128. for (auto[left, right] : Ranges_) {
  129. if (right - left < count) {
  130. continue;
  131. }
  132. ui16 start = left + RandomNumber<ui16>((right - left) / 2);
  133. if (right - start < count) {
  134. continue;
  135. }
  136. for (ui16 probe = start; probe < right; ++probe) {
  137. auto port = TryAcquirePort(probe);
  138. if (port) {
  139. ports.emplace_back(std::move(port));
  140. } else {
  141. ports.clear();
  142. }
  143. if (ports.size() == count) {
  144. return ports;
  145. }
  146. }
  147. // Can't find required number of ports without gap in the current range
  148. ports.clear();
  149. }
  150. }
  151. Y_ABORT("Cannot get range of %zu ports!", count);
  152. }
  153. NTesting::TPortHolder GetPort(ui16 port) const {
  154. if (port && DisableRandomPorts_) {
  155. auto ackport = TryAcquirePort(port);
  156. if (ackport) {
  157. return NTesting::TPortHolder{std::move(ackport)};
  158. }
  159. Y_ABORT("Cannot acquire port %hu!", port);
  160. }
  161. return GetFreePort();
  162. }
  163. private:
  164. THolder<NTesting::IPort> TryAcquirePort(ui16 port) const {
  165. auto lock = MakeHolder<TFileLock>(TString(SyncDir_ / ::ToString(port)));
  166. if (!lock->TryAcquire()) {
  167. return nullptr;
  168. }
  169. TInet6StreamSocket sock;
  170. Y_VERIFY_SYSERROR(INVALID_SOCKET != static_cast<SOCKET>(sock));
  171. TSockAddrInet6 addr("::", port);
  172. if (sock.Bind(&addr) != 0) {
  173. lock->Release();
  174. Y_ABORT_UNLESS(EADDRINUSE == LastSystemError(), "unexpected error: %d, port: %d", LastSystemError(), port);
  175. return nullptr;
  176. }
  177. return MakeHolder<TPortGuard>(port, std::move(lock));
  178. }
  179. private:
  180. TFsPath SyncDir_;
  181. TVector<std::pair<ui16, ui16>> Ranges_;
  182. size_t TotalCount_;
  183. bool DisableRandomPorts_;
  184. };
  185. }
  186. namespace NTesting {
  187. void InitPortManagerFromEnv() {
  188. Singleton<TPortManager>()->InitFromEnv();
  189. }
  190. TPortHolder GetFreePort() {
  191. return Singleton<TPortManager>()->GetFreePort();
  192. }
  193. namespace NLegacy {
  194. TPortHolder GetPort( ui16 port ) {
  195. return Singleton<TPortManager>()->GetPort(port);
  196. }
  197. TVector<TPortHolder> GetFreePortsRange(size_t count) {
  198. return Singleton<TPortManager>()->GetFreePortsRange(count);
  199. }
  200. }
  201. IOutputStream& operator<<(IOutputStream& out, const TPortHolder& port) {
  202. return out << static_cast<ui16>(port);
  203. }
  204. }