github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/ping_socket.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 <errno.h>
    16  #include <netinet/in.h>
    17  #include <netinet/ip.h>
    18  #include <netinet/ip_icmp.h>
    19  #include <sys/socket.h>
    20  #include <sys/types.h>
    21  #include <unistd.h>
    22  
    23  #include <cctype>
    24  #include <cstring>
    25  #include <vector>
    26  
    27  #include "gtest/gtest.h"
    28  #include "absl/algorithm/container.h"
    29  #include "absl/strings/str_join.h"
    30  #include "absl/types/optional.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  
    36  // Note: These tests require /proc/sys/net/ipv4/ping_group_range to be
    37  // configured to allow the tester to create ping sockets (see icmp(7)).
    38  
    39  namespace gvisor {
    40  namespace testing {
    41  namespace {
    42  
    43  // Test ICMP port exhaustion returns EAGAIN.
    44  //
    45  // We disable both random/cooperative S/R for this test as it makes way too many
    46  // syscalls.
    47  TEST(PingSocket, ICMPPortExhaustion) {
    48    DisableSave ds;
    49  
    50    {
    51      auto s = Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
    52      if (!s.ok()) {
    53        ASSERT_EQ(s.error().errno_value(), EACCES);
    54        GTEST_SKIP() << "TODO(gvisor.dev/issue/6126): Buildkite does not allow "
    55                        "creation of ICMP or ICMPv6 sockets";
    56      }
    57    }
    58  
    59    const struct sockaddr_in addr = {
    60        .sin_family = AF_INET,
    61        .sin_addr =
    62            {
    63                .s_addr = htonl(INADDR_LOOPBACK),
    64            },
    65    };
    66  
    67    std::vector<FileDescriptor> sockets;
    68    constexpr int kSockets = 65536;
    69    for (int i = 0; i < kSockets; i++) {
    70      auto s =
    71          ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP));
    72      int ret = connect(s.get(), reinterpret_cast<const struct sockaddr*>(&addr),
    73                        sizeof(addr));
    74      if (ret == 0) {
    75        sockets.push_back(std::move(s));
    76        continue;
    77      }
    78      ASSERT_THAT(ret, SyscallFailsWithErrno(EAGAIN));
    79      break;
    80    }
    81  }
    82  
    83  struct BindTestCase {
    84    TestAddress bind_to;
    85    int want = 0;
    86    absl::optional<int> want_gvisor;
    87  };
    88  
    89  // Test fixture for socket binding.
    90  class Fixture
    91      : public ::testing::TestWithParam<std::tuple<SocketKind, BindTestCase>> {};
    92  
    93  TEST_P(Fixture, Bind) {
    94    auto [socket_factory, test_case] = GetParam();
    95    auto socket = socket_factory.Create();
    96    if (!socket.ok()) {
    97      ASSERT_EQ(socket.error().errno_value(), EACCES);
    98      GTEST_SKIP() << "TODO(gvisor.dev/issue/6126): Buildkite does not allow "
    99                      "creation of ICMP or ICMPv6 sockets";
   100    }
   101    auto socket_fd = std::move(socket).ValueOrDie();
   102  
   103    const int want = test_case.want_gvisor.has_value() && IsRunningOnGvisor()
   104                         ? *test_case.want_gvisor
   105                         : test_case.want;
   106    if (want == 0) {
   107      EXPECT_THAT(bind(socket_fd->get(), AsSockAddr(&test_case.bind_to.addr),
   108                       test_case.bind_to.addr_len),
   109                  SyscallSucceeds());
   110    } else {
   111      EXPECT_THAT(bind(socket_fd->get(), AsSockAddr(&test_case.bind_to.addr),
   112                       test_case.bind_to.addr_len),
   113                  SyscallFailsWithErrno(want));
   114    }
   115  }
   116  
   117  std::vector<std::tuple<SocketKind, BindTestCase>> ICMPTestCases() {
   118    return ApplyVec<std::tuple<SocketKind, BindTestCase>>(
   119        [](const BindTestCase& test_case) {
   120          return std::make_tuple(ICMPUnboundSocket(0), test_case);
   121        },
   122        std::vector<BindTestCase>{
   123            {
   124                .bind_to = V4Any(),
   125                .want = 0,
   126                .want_gvisor = 0,
   127            },
   128            {
   129                .bind_to = V4Broadcast(),
   130                .want = EADDRNOTAVAIL,
   131                // TODO(gvisor.dev/issue/5711): Remove want_gvisor once ICMP
   132                // sockets are no longer allowed to bind to broadcast addresses.
   133                .want_gvisor = 0,
   134            },
   135            {
   136                .bind_to = V4Loopback(),
   137                .want = 0,
   138            },
   139            {
   140                .bind_to = V4LoopbackSubnetBroadcast(),
   141                .want = EADDRNOTAVAIL,
   142                // TODO(gvisor.dev/issue/5711): Remove want_gvisor once ICMP
   143                // sockets are no longer allowed to bind to broadcast addresses.
   144                .want_gvisor = 0,
   145            },
   146            {
   147                .bind_to = V4Multicast(),
   148                .want = EADDRNOTAVAIL,
   149            },
   150            {
   151                .bind_to = V4MulticastAllHosts(),
   152                .want = EADDRNOTAVAIL,
   153            },
   154            {
   155                .bind_to = V4AddrStr("IPv4UnknownUnicast", "192.168.1.1"),
   156                .want = EADDRNOTAVAIL,
   157            },
   158            // TODO(gvisor.dev/issue/6021): Remove want_gvisor from all the test
   159            // cases below once ICMP sockets return EAFNOSUPPORT when binding to
   160            // IPv6 addresses.
   161            {
   162                .bind_to = V6Any(),
   163                .want = EAFNOSUPPORT,
   164                .want_gvisor = EINVAL,
   165            },
   166            {
   167                .bind_to = V6Loopback(),
   168                .want = EAFNOSUPPORT,
   169                .want_gvisor = EINVAL,
   170            },
   171            {
   172                .bind_to = V6Multicast(),
   173                .want = EAFNOSUPPORT,
   174                .want_gvisor = EINVAL,
   175            },
   176            {
   177                .bind_to = V6MulticastInterfaceLocalAllNodes(),
   178                .want = EAFNOSUPPORT,
   179                .want_gvisor = EINVAL,
   180            },
   181            {
   182                .bind_to = V6MulticastLinkLocalAllNodes(),
   183                .want = EAFNOSUPPORT,
   184                .want_gvisor = EINVAL,
   185            },
   186            {
   187                .bind_to = V6MulticastLinkLocalAllRouters(),
   188                .want = EAFNOSUPPORT,
   189                .want_gvisor = EINVAL,
   190            },
   191            {
   192                .bind_to = V6AddrStr("IPv6UnknownUnicast", "fc00::1"),
   193                .want = EAFNOSUPPORT,
   194                .want_gvisor = EINVAL,
   195            },
   196        });
   197  }
   198  
   199  std::vector<std::tuple<SocketKind, BindTestCase>> ICMPv6TestCases() {
   200    return ApplyVec<std::tuple<SocketKind, BindTestCase>>(
   201        [](const BindTestCase& test_case) {
   202          return std::make_tuple(ICMPv6UnboundSocket(0), test_case);
   203        },
   204        std::vector<BindTestCase>{
   205            {
   206                .bind_to = V4Any(),
   207                .want = EINVAL,
   208            },
   209            {
   210                .bind_to = V4Broadcast(),
   211                .want = EINVAL,
   212            },
   213            {
   214                .bind_to = V4Loopback(),
   215                .want = EINVAL,
   216            },
   217            {
   218                .bind_to = V4LoopbackSubnetBroadcast(),
   219                .want = EINVAL,
   220            },
   221            {
   222                .bind_to = V4Multicast(),
   223                .want = EINVAL,
   224            },
   225            {
   226                .bind_to = V4MulticastAllHosts(),
   227                .want = EINVAL,
   228            },
   229            {
   230                .bind_to = V4AddrStr("IPv4UnknownUnicast", "192.168.1.1"),
   231                .want = EINVAL,
   232            },
   233            {
   234                .bind_to = V6Any(),
   235                .want = 0,
   236            },
   237            {
   238                .bind_to = V6Loopback(),
   239                .want = 0,
   240            },
   241            // TODO(gvisor.dev/issue/6021): Remove want_gvisor from all the
   242            // multicast test cases below once ICMPv6 sockets return EINVAL when
   243            // binding to IPv6 multicast addresses.
   244            {
   245                .bind_to = V6Multicast(),
   246                .want = EINVAL,
   247                .want_gvisor = EADDRNOTAVAIL,
   248            },
   249            {
   250                .bind_to = V6MulticastInterfaceLocalAllNodes(),
   251                .want = EINVAL,
   252                .want_gvisor = EADDRNOTAVAIL,
   253            },
   254            {
   255                .bind_to = V6MulticastLinkLocalAllNodes(),
   256                .want = EINVAL,
   257                .want_gvisor = EADDRNOTAVAIL,
   258            },
   259            {
   260                .bind_to = V6MulticastLinkLocalAllRouters(),
   261                .want = EINVAL,
   262                .want_gvisor = EADDRNOTAVAIL,
   263            },
   264            {
   265                .bind_to = V6AddrStr("IPv6UnknownUnicast", "fc00::1"),
   266                .want = EADDRNOTAVAIL,
   267            },
   268        });
   269  }
   270  
   271  std::vector<std::tuple<SocketKind, BindTestCase>> AllTestCases() {
   272    return VecCat<std::tuple<SocketKind, BindTestCase>>(ICMPTestCases(),
   273                                                        ICMPv6TestCases());
   274  }
   275  
   276  std::string TestDescription(
   277      const ::testing::TestParamInfo<Fixture::ParamType>& info) {
   278    auto [socket_factory, test_case] = info.param;
   279    std::string name = absl::StrJoin(
   280        {socket_factory.description, test_case.bind_to.description}, "_");
   281    absl::c_replace_if(
   282        name, [](char c) { return !std::isalnum(c); }, '_');
   283    return name;
   284  }
   285  
   286  INSTANTIATE_TEST_SUITE_P(PingSockets, Fixture,
   287                           ::testing::ValuesIn(AllTestCases()), TestDescription);
   288  
   289  }  // namespace
   290  }  // namespace testing
   291  }  // namespace gvisor