github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/socket_generic_stress.cc (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include <poll.h>
    16  #include <stdio.h>
    17  #include <sys/ioctl.h>
    18  #include <sys/socket.h>
    19  #include <sys/un.h>
    20  #include <unistd.h>
    21  
    22  #include <array>
    23  #include <string>
    24  
    25  #include "gtest/gtest.h"
    26  #include "absl/strings/numbers.h"
    27  #include "absl/strings/str_split.h"
    28  #include "absl/strings/string_view.h"
    29  #include "absl/time/clock.h"
    30  #include "absl/time/time.h"
    31  #include "test/syscalls/linux/ip_socket_test_util.h"
    32  #include "test/syscalls/linux/socket_test_util.h"
    33  #include "test/util/file_descriptor.h"
    34  #include "test/util/test_util.h"
    35  #include "test/util/thread_util.h"
    36  
    37  namespace gvisor {
    38  namespace testing {
    39  
    40  // Test fixture for tests that apply to pairs of connected sockets.
    41  using ConnectStressTest = SocketPairTest;
    42  
    43  TEST_P(ConnectStressTest, Reset) {
    44    const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
    45    for (int i = 0; i < nports * 2; i++) {
    46      const std::unique_ptr<SocketPair> sockets =
    47          ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
    48  
    49      // Send some data to ensure that the connection gets reset and the port gets
    50      // released immediately. This avoids either end entering TIME-WAIT.
    51      char sent_data[100] = {};
    52      ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)),
    53                  SyscallSucceedsWithValue(sizeof(sent_data)));
    54      // Poll the other FD to make sure that the data is in the receive buffer
    55      // before closing it to ensure a RST is triggered.
    56      const int kTimeout = 10000;
    57      struct pollfd pfd = {
    58          .fd = sockets->second_fd(),
    59          .events = POLL_IN,
    60      };
    61      ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
    62    }
    63  }
    64  
    65  // Tests that opening too many connections -- without closing them -- does lead
    66  // to port exhaustion.
    67  TEST_P(ConnectStressTest, TooManyOpen) {
    68    const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
    69    int err_num = 0;
    70    std::vector<std::unique_ptr<SocketPair>> sockets =
    71        std::vector<std::unique_ptr<SocketPair>>(nports);
    72    for (int i = 0; i < nports * 2; i++) {
    73      PosixErrorOr<std::unique_ptr<SocketPair>> socks = NewSocketPair();
    74      if (!socks.ok()) {
    75        err_num = socks.error().errno_value();
    76        break;
    77      }
    78      sockets.push_back(std::move(socks).ValueOrDie());
    79    }
    80    ASSERT_EQ(err_num, EADDRINUSE);
    81  }
    82  
    83  INSTANTIATE_TEST_SUITE_P(
    84      AllConnectedSockets, ConnectStressTest,
    85      ::testing::Values(IPv6UDPBidirectionalBindSocketPair(0),
    86                        IPv4UDPBidirectionalBindSocketPair(0),
    87                        DualStackUDPBidirectionalBindSocketPair(0),
    88  
    89                        // Without REUSEADDR, we get port exhaustion on Linux.
    90                        SetSockOpt(SOL_SOCKET, SO_REUSEADDR,
    91                                   &kSockOptOn)(IPv6TCPAcceptBindSocketPair(0)),
    92                        SetSockOpt(SOL_SOCKET, SO_REUSEADDR,
    93                                   &kSockOptOn)(IPv4TCPAcceptBindSocketPair(0)),
    94                        SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
    95                            DualStackTCPAcceptBindSocketPair(0))));
    96  
    97  // Test fixture for tests that apply to pairs of connected sockets created with
    98  // a persistent listener (if applicable).
    99  class PersistentListenerConnectStressTest : public SocketPairTest {
   100   protected:
   101    PersistentListenerConnectStressTest() : slept_{false} {}
   102  
   103    // NewSocketSleep is the same as NewSocketPair, but will sleep once (over the
   104    // lifetime of the fixture) and retry if creation fails due to EADDRNOTAVAIL.
   105    PosixErrorOr<std::unique_ptr<SocketPair>> NewSocketSleep() {
   106      // We can't reuse a connection too close in time to its last use, as TCP
   107      // uses the timestamp difference to disambiguate connections. With a
   108      // sufficiently small port range, we'll cycle through too quickly, and TCP
   109      // won't allow for connection reuse. Thus, we sleep the first time
   110      // encountering EADDRINUSE to allow for that difference (1 second in
   111      // gVisor).
   112      PosixErrorOr<std::unique_ptr<SocketPair>> socks = NewSocketPair();
   113      if (socks.ok()) {
   114        return socks;
   115      }
   116      if (!slept_ && socks.error().errno_value() == EADDRNOTAVAIL) {
   117        absl::SleepFor(absl::Milliseconds(1500));
   118        slept_ = true;
   119        return NewSocketPair();
   120      }
   121      return socks;
   122    }
   123  
   124   private:
   125    bool slept_;
   126  };
   127  
   128  TEST_P(PersistentListenerConnectStressTest, ShutdownCloseFirst) {
   129    const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
   130    for (int i = 0; i < nports * 2; i++) {
   131      std::unique_ptr<SocketPair> sockets =
   132          ASSERT_NO_ERRNO_AND_VALUE(NewSocketSleep());
   133      ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds());
   134      if (GetParam().type == SOCK_STREAM) {
   135        // Poll the other FD to make sure that we see the FIN from the other
   136        // side before closing the second_fd. This ensures that the first_fd
   137        // enters TIME-WAIT and not second_fd.
   138        const int kTimeout = 10000;
   139        struct pollfd pfd = {
   140            .fd = sockets->second_fd(),
   141            .events = POLL_IN,
   142        };
   143        ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
   144      }
   145      ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds());
   146    }
   147  }
   148  
   149  TEST_P(PersistentListenerConnectStressTest, ShutdownCloseSecond) {
   150    const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
   151    for (int i = 0; i < nports * 2; i++) {
   152      const std::unique_ptr<SocketPair> sockets =
   153          ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
   154      ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds());
   155      if (GetParam().type == SOCK_STREAM) {
   156        // Poll the other FD to make sure that we see the FIN from the other
   157        // side before closing the first_fd. This ensures that the second_fd
   158        // enters TIME-WAIT and not first_fd.
   159        const int kTimeout = 10000;
   160        struct pollfd pfd = {
   161            .fd = sockets->first_fd(),
   162            .events = POLL_IN,
   163        };
   164        ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
   165      }
   166      ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds());
   167    }
   168  }
   169  
   170  TEST_P(PersistentListenerConnectStressTest, Close) {
   171    const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
   172    for (int i = 0; i < nports * 2; i++) {
   173      std::unique_ptr<SocketPair> sockets =
   174          ASSERT_NO_ERRNO_AND_VALUE(NewSocketSleep());
   175    }
   176  }
   177  
   178  INSTANTIATE_TEST_SUITE_P(
   179      AllConnectedSockets, PersistentListenerConnectStressTest,
   180      ::testing::Values(
   181          IPv6UDPBidirectionalBindSocketPair(0),
   182          IPv4UDPBidirectionalBindSocketPair(0),
   183          DualStackUDPBidirectionalBindSocketPair(0),
   184  
   185          // Without REUSEADDR, we get port exhaustion on Linux.
   186          SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
   187              IPv6TCPAcceptBindPersistentListenerSocketPair(0)),
   188          SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
   189              IPv4TCPAcceptBindPersistentListenerSocketPair(0)),
   190          SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
   191              DualStackTCPAcceptBindPersistentListenerSocketPair(0))));
   192  
   193  using DataTransferStressTest = SocketPairTest;
   194  
   195  TEST_P(DataTransferStressTest, BigDataTransfer) {
   196    // TODO(b/165912341): These are too slow on KVM platform with nested virt.
   197    SKIP_IF(GvisorPlatform() == Platform::kKVM);
   198  
   199    const std::unique_ptr<SocketPair> sockets =
   200        ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
   201    int client_fd = sockets->first_fd();
   202    int server_fd = sockets->second_fd();
   203  
   204    ScopedThread echo([server_fd]() {
   205      std::array<uint8_t, 1024> buf;
   206      for (;;) {
   207        ssize_t r = read(server_fd, buf.data(), buf.size());
   208        ASSERT_THAT(r, SyscallSucceeds());
   209        if (r == 0) {
   210          break;
   211        }
   212        for (size_t i = 0; i < r;) {
   213          ssize_t w = write(server_fd, buf.data() + i, r - i);
   214          ASSERT_GE(w, 0);
   215          i += w;
   216        }
   217      }
   218      ASSERT_THAT(shutdown(server_fd, SHUT_WR), SyscallSucceeds());
   219    });
   220  
   221    const std::string chunk = "Though this upload be but little, it is fierce.";
   222    std::string big_string;
   223    while (big_string.size() < 31 << 20) {
   224      big_string += chunk;
   225    }
   226    absl::string_view data = big_string;
   227  
   228    ScopedThread writer([client_fd, data]() {
   229      absl::string_view view = data;
   230      while (!view.empty()) {
   231        ssize_t n = write(client_fd, view.data(), view.size());
   232        ASSERT_GE(n, 0);
   233        view = view.substr(n);
   234      }
   235      ASSERT_THAT(shutdown(client_fd, SHUT_WR), SyscallSucceeds());
   236    });
   237  
   238    std::string buf;
   239    buf.resize(1 << 20);
   240    while (!data.empty()) {
   241      ssize_t n = read(client_fd, buf.data(), buf.size());
   242      ASSERT_GE(n, 0);
   243      for (size_t i = 0; i < n; i += chunk.size()) {
   244        size_t c = std::min(chunk.size(), n - i);
   245        ASSERT_EQ(buf.substr(i, c), data.substr(i, c)) << "offset " << i;
   246      }
   247      data = data.substr(n);
   248    }
   249    // Should read EOF now.
   250    ASSERT_THAT(read(client_fd, buf.data(), buf.size()),
   251                SyscallSucceedsWithValue(0));
   252  }
   253  
   254  INSTANTIATE_TEST_SUITE_P(
   255      AllConnectedSockets, DataTransferStressTest,
   256      ::testing::Values(IPv6TCPAcceptBindPersistentListenerSocketPair(0),
   257                        IPv4TCPAcceptBindPersistentListenerSocketPair(0),
   258                        DualStackTCPAcceptBindPersistentListenerSocketPair(0)));
   259  
   260  }  // namespace testing
   261  }  // namespace gvisor