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

     1  // Copyright 2018 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 <arpa/inet.h>
    16  #include <errno.h>
    17  #include <netinet/in.h>
    18  #include <poll.h>
    19  #include <sys/socket.h>
    20  #include <sys/syscall.h>
    21  #include <sys/types.h>
    22  
    23  #include <vector>
    24  
    25  #include "gtest/gtest.h"
    26  #include "absl/strings/numbers.h"
    27  #include "absl/strings/str_cat.h"
    28  #include "absl/strings/str_split.h"
    29  #include "absl/strings/string_view.h"
    30  #include "absl/time/clock.h"
    31  #include "test/syscalls/linux/socket_test_util.h"
    32  #include "test/util/capability_util.h"
    33  #include "test/util/file_descriptor.h"
    34  #include "test/util/fs_util.h"
    35  #include "test/util/test_util.h"
    36  
    37  namespace gvisor {
    38  namespace testing {
    39  namespace {
    40  
    41  constexpr const char kProcNet[] = "/proc/net";
    42  constexpr const char kIpForward[] = "/proc/sys/net/ipv4/ip_forward";
    43  constexpr const char kRangeFile[] = "/proc/sys/net/ipv4/ip_local_port_range";
    44  
    45  TEST(ProcNetSymlinkTarget, FileMode) {
    46    struct stat s;
    47    ASSERT_THAT(stat(kProcNet, &s), SyscallSucceeds());
    48    EXPECT_EQ(s.st_mode & S_IFMT, S_IFDIR);
    49    EXPECT_EQ(s.st_mode & 0777, 0555);
    50  }
    51  
    52  TEST(ProcNetSymlink, FileMode) {
    53    struct stat s;
    54    ASSERT_THAT(lstat(kProcNet, &s), SyscallSucceeds());
    55    EXPECT_EQ(s.st_mode & S_IFMT, S_IFLNK);
    56    EXPECT_EQ(s.st_mode & 0777, 0777);
    57  }
    58  
    59  TEST(ProcNetSymlink, Contents) {
    60    char buf[40] = {};
    61    int n = readlink(kProcNet, buf, sizeof(buf));
    62    ASSERT_THAT(n, SyscallSucceeds());
    63  
    64    buf[n] = 0;
    65    EXPECT_STREQ(buf, "self/net");
    66  }
    67  
    68  TEST(ProcNetIfInet6, Format) {
    69    auto ifinet6 = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/if_inet6"));
    70    EXPECT_THAT(ifinet6,
    71                ::testing::MatchesRegex(
    72                    // Ex: "00000000000000000000000000000001 01 80 10 80 lo\n"
    73                    "^([a-f0-9]{32}( [a-f0-9]{2}){4} +[a-z][a-z0-9]*\n)+$"));
    74  }
    75  
    76  TEST(ProcSysNetIpv4Sack, Exists) {
    77    EXPECT_THAT(open("/proc/sys/net/ipv4/tcp_sack", O_RDONLY), SyscallSucceeds());
    78  }
    79  
    80  TEST(ProcSysNetIpv4Sack, CanReadAndWrite) {
    81    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_DAC_OVERRIDE))));
    82  
    83    auto const fd =
    84        ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/sys/net/ipv4/tcp_sack", O_RDWR));
    85  
    86    char buf;
    87    EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
    88                SyscallSucceedsWithValue(sizeof(buf)));
    89  
    90    EXPECT_TRUE(buf == '0' || buf == '1') << "unexpected tcp_sack: " << buf;
    91  
    92    char to_write = (buf == '1') ? '0' : '1';
    93    EXPECT_THAT(PwriteFd(fd.get(), &to_write, sizeof(to_write), 0),
    94                SyscallSucceedsWithValue(sizeof(to_write)));
    95  
    96    buf = 0;
    97    EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
    98                SyscallSucceedsWithValue(sizeof(buf)));
    99    EXPECT_EQ(buf, to_write);
   100  }
   101  
   102  // DeviceEntry is an entry in /proc/net/dev
   103  struct DeviceEntry {
   104    std::string name;
   105    uint64_t stats[16];
   106  };
   107  
   108  PosixErrorOr<std::vector<DeviceEntry>> GetDeviceMetricsFromProc(
   109      const std::string dev) {
   110    std::vector<std::string> lines = absl::StrSplit(dev, '\n');
   111    std::vector<DeviceEntry> entries;
   112  
   113    // /proc/net/dev prints 2 lines of headers followed by a line of metrics for
   114    // each network interface.
   115    for (unsigned i = 2; i < lines.size(); i++) {
   116      // Ignore empty lines.
   117      if (lines[i].empty()) {
   118        continue;
   119      }
   120  
   121      std::vector<std::string> values =
   122          absl::StrSplit(lines[i], ' ', absl::SkipWhitespace());
   123  
   124      // Interface name + 16 values.
   125      if (values.size() != 17) {
   126        return PosixError(EINVAL, "invalid line: " + lines[i]);
   127      }
   128  
   129      DeviceEntry entry;
   130      entry.name = values[0];
   131      // Skip the interface name and read only the values.
   132      for (unsigned j = 1; j < 17; j++) {
   133        uint64_t num;
   134        if (!absl::SimpleAtoi(values[j], &num)) {
   135          return PosixError(EINVAL, "invalid value: " + values[j]);
   136        }
   137        entry.stats[j - 1] = num;
   138      }
   139  
   140      entries.push_back(entry);
   141    }
   142  
   143    return entries;
   144  }
   145  
   146  // TEST(ProcNetDev, Format) tests that /proc/net/dev is parsable and
   147  // contains at least one entry.
   148  TEST(ProcNetDev, Format) {
   149    auto dev = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/dev"));
   150    auto entries = ASSERT_NO_ERRNO_AND_VALUE(GetDeviceMetricsFromProc(dev));
   151  
   152    EXPECT_GT(entries.size(), 0);
   153  }
   154  
   155  PosixErrorOr<uint64_t> GetSNMPMetricFromProc(const std::string snmp,
   156                                               const std::string& type,
   157                                               const std::string& item) {
   158    std::vector<std::string> snmp_vec = absl::StrSplit(snmp, '\n');
   159  
   160    // /proc/net/snmp prints a line of headers followed by a line of metrics.
   161    // Only search the headers.
   162    for (unsigned i = 0; i < snmp_vec.size(); i = i + 2) {
   163      if (!absl::StartsWith(snmp_vec[i], type)) continue;
   164  
   165      std::vector<std::string> fields =
   166          absl::StrSplit(snmp_vec[i], ' ', absl::SkipWhitespace());
   167  
   168      EXPECT_TRUE((i + 1) < snmp_vec.size());
   169      std::vector<std::string> values =
   170          absl::StrSplit(snmp_vec[i + 1], ' ', absl::SkipWhitespace());
   171  
   172      EXPECT_TRUE(!fields.empty() && fields.size() == values.size());
   173  
   174      // Metrics start at the first index.
   175      for (unsigned j = 1; j < fields.size(); j++) {
   176        if (fields[j] == item) {
   177          uint64_t val;
   178          if (!absl::SimpleAtoi(values[j], &val)) {
   179            return PosixError(EINVAL,
   180                              absl::StrCat("field is not a number: ", values[j]));
   181          }
   182  
   183          return val;
   184        }
   185      }
   186    }
   187    // We should never get here.
   188    return PosixError(
   189        EINVAL, absl::StrCat("failed to find ", type, "/", item, " in:", snmp));
   190  }
   191  
   192  TEST(ProcNetSnmp, TcpReset) {
   193    // TODO(gvisor.dev/issue/866): epsocket metrics are not savable.
   194    DisableSave ds;
   195  
   196    uint64_t oldAttemptFails;
   197    uint64_t oldActiveOpens;
   198    uint64_t oldOutRsts;
   199    auto snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   200    oldActiveOpens = ASSERT_NO_ERRNO_AND_VALUE(
   201        GetSNMPMetricFromProc(snmp, "Tcp", "ActiveOpens"));
   202    oldOutRsts =
   203        ASSERT_NO_ERRNO_AND_VALUE(GetSNMPMetricFromProc(snmp, "Tcp", "OutRsts"));
   204    oldAttemptFails = ASSERT_NO_ERRNO_AND_VALUE(
   205        GetSNMPMetricFromProc(snmp, "Tcp", "AttemptFails"));
   206  
   207    FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, 0));
   208  
   209    struct sockaddr_in sin = {
   210        .sin_family = AF_INET,
   211        .sin_port = htons(1234),
   212    };
   213  
   214    ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1);
   215    ASSERT_THAT(connect(s.get(), (struct sockaddr*)&sin, sizeof(sin)),
   216                SyscallFailsWithErrno(ECONNREFUSED));
   217  
   218    uint64_t newAttemptFails;
   219    uint64_t newActiveOpens;
   220    uint64_t newOutRsts;
   221    snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   222    newActiveOpens = ASSERT_NO_ERRNO_AND_VALUE(
   223        GetSNMPMetricFromProc(snmp, "Tcp", "ActiveOpens"));
   224    newOutRsts =
   225        ASSERT_NO_ERRNO_AND_VALUE(GetSNMPMetricFromProc(snmp, "Tcp", "OutRsts"));
   226    newAttemptFails = ASSERT_NO_ERRNO_AND_VALUE(
   227        GetSNMPMetricFromProc(snmp, "Tcp", "AttemptFails"));
   228  
   229    EXPECT_EQ(oldActiveOpens, newActiveOpens - 1);
   230    EXPECT_EQ(oldOutRsts, newOutRsts - 1);
   231    EXPECT_EQ(oldAttemptFails, newAttemptFails - 1);
   232  }
   233  
   234  TEST(ProcNetSnmp, TcpEstab) {
   235    // TODO(gvisor.dev/issue/866): epsocket metrics are not savable.
   236    DisableSave ds;
   237  
   238    uint64_t oldEstabResets;
   239    uint64_t oldActiveOpens;
   240    uint64_t oldPassiveOpens;
   241    uint64_t oldCurrEstab;
   242    auto snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   243    oldActiveOpens = ASSERT_NO_ERRNO_AND_VALUE(
   244        GetSNMPMetricFromProc(snmp, "Tcp", "ActiveOpens"));
   245    oldPassiveOpens = ASSERT_NO_ERRNO_AND_VALUE(
   246        GetSNMPMetricFromProc(snmp, "Tcp", "PassiveOpens"));
   247    oldCurrEstab = ASSERT_NO_ERRNO_AND_VALUE(
   248        GetSNMPMetricFromProc(snmp, "Tcp", "CurrEstab"));
   249    oldEstabResets = ASSERT_NO_ERRNO_AND_VALUE(
   250        GetSNMPMetricFromProc(snmp, "Tcp", "EstabResets"));
   251  
   252    FileDescriptor s_listen =
   253        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, 0));
   254    struct sockaddr_in sin = {
   255        .sin_family = AF_INET,
   256        .sin_port = 0,
   257    };
   258  
   259    ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1);
   260    ASSERT_THAT(bind(s_listen.get(), (struct sockaddr*)&sin, sizeof(sin)),
   261                SyscallSucceeds());
   262    ASSERT_THAT(listen(s_listen.get(), 1), SyscallSucceeds());
   263  
   264    // Get the port bound by the listening socket.
   265    socklen_t addrlen = sizeof(sin);
   266    ASSERT_THAT(getsockname(s_listen.get(), AsSockAddr(&sin), &addrlen),
   267                SyscallSucceeds());
   268  
   269    FileDescriptor s_connect =
   270        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, 0));
   271    ASSERT_THAT(connect(s_connect.get(), (struct sockaddr*)&sin, sizeof(sin)),
   272                SyscallSucceeds());
   273  
   274    auto s_accept =
   275        ASSERT_NO_ERRNO_AND_VALUE(Accept(s_listen.get(), nullptr, nullptr));
   276  
   277    uint64_t newEstabResets;
   278    uint64_t newActiveOpens;
   279    uint64_t newPassiveOpens;
   280    uint64_t newCurrEstab;
   281    snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   282    newActiveOpens = ASSERT_NO_ERRNO_AND_VALUE(
   283        GetSNMPMetricFromProc(snmp, "Tcp", "ActiveOpens"));
   284    newPassiveOpens = ASSERT_NO_ERRNO_AND_VALUE(
   285        GetSNMPMetricFromProc(snmp, "Tcp", "PassiveOpens"));
   286    newCurrEstab = ASSERT_NO_ERRNO_AND_VALUE(
   287        GetSNMPMetricFromProc(snmp, "Tcp", "CurrEstab"));
   288  
   289    EXPECT_EQ(oldActiveOpens, newActiveOpens - 1);
   290    EXPECT_EQ(oldPassiveOpens, newPassiveOpens - 1);
   291    EXPECT_EQ(oldCurrEstab, newCurrEstab - 2);
   292  
   293    // Send 1 byte from client to server.
   294    ASSERT_THAT(send(s_connect.get(), "a", 1, 0), SyscallSucceedsWithValue(1));
   295  
   296    constexpr int kPollTimeoutMs = 20000;  // Wait up to 20 seconds for the data.
   297  
   298    // Wait until server-side fd sees the data on its side but don't read it.
   299    struct pollfd poll_fd = {s_accept.get(), POLLIN, 0};
   300    ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs),
   301                SyscallSucceedsWithValue(1));
   302  
   303    // Now close server-side fd without reading the data which leads to a RST
   304    // packet sent to client side.
   305    s_accept.reset(-1);
   306  
   307    // Wait until client-side fd sees RST packet.
   308    struct pollfd poll_fd1 = {s_connect.get(), POLLIN, 0};
   309    ASSERT_THAT(RetryEINTR(poll)(&poll_fd1, 1, kPollTimeoutMs),
   310                SyscallSucceedsWithValue(1));
   311  
   312    // Now close client-side fd.
   313    s_connect.reset(-1);
   314  
   315    // Wait until the process of the netstack.
   316    absl::SleepFor(absl::Seconds(1));
   317  
   318    snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   319    newCurrEstab = ASSERT_NO_ERRNO_AND_VALUE(
   320        GetSNMPMetricFromProc(snmp, "Tcp", "CurrEstab"));
   321    newEstabResets = ASSERT_NO_ERRNO_AND_VALUE(
   322        GetSNMPMetricFromProc(snmp, "Tcp", "EstabResets"));
   323  
   324    EXPECT_EQ(oldCurrEstab, newCurrEstab);
   325    EXPECT_EQ(oldEstabResets, newEstabResets - 2);
   326  }
   327  
   328  TEST(ProcNetSnmp, UdpNoPorts) {
   329    // TODO(gvisor.dev/issue/866): epsocket metrics are not savable.
   330    DisableSave ds;
   331  
   332    uint64_t oldOutDatagrams;
   333    uint64_t oldNoPorts;
   334    auto snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   335    oldOutDatagrams = ASSERT_NO_ERRNO_AND_VALUE(
   336        GetSNMPMetricFromProc(snmp, "Udp", "OutDatagrams"));
   337    oldNoPorts =
   338        ASSERT_NO_ERRNO_AND_VALUE(GetSNMPMetricFromProc(snmp, "Udp", "NoPorts"));
   339  
   340    FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
   341  
   342    struct sockaddr_in sin = {
   343        .sin_family = AF_INET,
   344        .sin_port = htons(4444),
   345    };
   346    ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1);
   347    ASSERT_THAT(sendto(s.get(), "a", 1, 0, (struct sockaddr*)&sin, sizeof(sin)),
   348                SyscallSucceedsWithValue(1));
   349  
   350    uint64_t newOutDatagrams;
   351    uint64_t newNoPorts;
   352    snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   353    newOutDatagrams = ASSERT_NO_ERRNO_AND_VALUE(
   354        GetSNMPMetricFromProc(snmp, "Udp", "OutDatagrams"));
   355    newNoPorts =
   356        ASSERT_NO_ERRNO_AND_VALUE(GetSNMPMetricFromProc(snmp, "Udp", "NoPorts"));
   357  
   358    EXPECT_EQ(oldOutDatagrams, newOutDatagrams - 1);
   359    EXPECT_EQ(oldNoPorts, newNoPorts - 1);
   360  }
   361  
   362  TEST(ProcNetSnmp, UdpIn) {
   363    // TODO(gvisor.dev/issue/866): epsocket metrics are not savable.
   364    const DisableSave ds;
   365  
   366    uint64_t oldOutDatagrams;
   367    uint64_t oldInDatagrams;
   368    auto snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   369    oldOutDatagrams = ASSERT_NO_ERRNO_AND_VALUE(
   370        GetSNMPMetricFromProc(snmp, "Udp", "OutDatagrams"));
   371    oldInDatagrams = ASSERT_NO_ERRNO_AND_VALUE(
   372        GetSNMPMetricFromProc(snmp, "Udp", "InDatagrams"));
   373  
   374    std::cerr << "snmp: " << std::endl << snmp << std::endl;
   375    FileDescriptor server =
   376        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
   377    struct sockaddr_in sin = {
   378        .sin_family = AF_INET,
   379        .sin_port = htons(0),
   380    };
   381    ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1);
   382    ASSERT_THAT(bind(server.get(), (struct sockaddr*)&sin, sizeof(sin)),
   383                SyscallSucceeds());
   384    // Get the port bound by the server socket.
   385    socklen_t addrlen = sizeof(sin);
   386    ASSERT_THAT(getsockname(server.get(), AsSockAddr(&sin), &addrlen),
   387                SyscallSucceeds());
   388  
   389    FileDescriptor client =
   390        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
   391    ASSERT_THAT(
   392        sendto(client.get(), "a", 1, 0, (struct sockaddr*)&sin, sizeof(sin)),
   393        SyscallSucceedsWithValue(1));
   394  
   395    char buf[128];
   396    ASSERT_THAT(recvfrom(server.get(), buf, sizeof(buf), 0, NULL, NULL),
   397                SyscallSucceedsWithValue(1));
   398  
   399    uint64_t newOutDatagrams;
   400    uint64_t newInDatagrams;
   401    snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   402    std::cerr << "new snmp: " << std::endl << snmp << std::endl;
   403    newOutDatagrams = ASSERT_NO_ERRNO_AND_VALUE(
   404        GetSNMPMetricFromProc(snmp, "Udp", "OutDatagrams"));
   405    newInDatagrams = ASSERT_NO_ERRNO_AND_VALUE(
   406        GetSNMPMetricFromProc(snmp, "Udp", "InDatagrams"));
   407  
   408    EXPECT_EQ(oldOutDatagrams, newOutDatagrams - 1);
   409    EXPECT_EQ(oldInDatagrams, newInDatagrams - 1);
   410  }
   411  
   412  TEST(ProcNetSnmp, CheckNetStat) {
   413    // TODO(b/155123175): SNMP and netstat don't work on gVisor.
   414    SKIP_IF(IsRunningOnGvisor());
   415  
   416    std::string contents =
   417        ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/netstat"));
   418  
   419    int name_count = 0;
   420    int value_count = 0;
   421    std::vector<absl::string_view> lines = absl::StrSplit(contents, '\n');
   422    for (size_t i = 0; i + 1 < lines.size(); i += 2) {
   423      std::vector<absl::string_view> names =
   424          absl::StrSplit(lines[i], absl::ByAnyChar("\t "));
   425      std::vector<absl::string_view> values =
   426          absl::StrSplit(lines[i + 1], absl::ByAnyChar("\t "));
   427      EXPECT_EQ(names.size(), values.size()) << " mismatch in lines '" << lines[i]
   428                                             << "' and '" << lines[i + 1] << "'";
   429      for (size_t j = 0; j < names.size() && j < values.size(); ++j) {
   430        if (names[j] == "TCPOrigDataSent" || names[j] == "TCPSynRetrans" ||
   431            names[j] == "TCPDSACKRecv" || names[j] == "TCPDSACKOfoRecv") {
   432          ++name_count;
   433          int64_t val;
   434          if (absl::SimpleAtoi(values[j], &val)) {
   435            ++value_count;
   436          }
   437        }
   438      }
   439    }
   440    EXPECT_EQ(name_count, 4);
   441    EXPECT_EQ(value_count, 4);
   442  }
   443  
   444  TEST(ProcNetSnmp, Stat) {
   445    struct stat st = {};
   446    ASSERT_THAT(stat("/proc/net/snmp", &st), SyscallSucceeds());
   447  }
   448  
   449  TEST(ProcNetSnmp, CheckSnmp) {
   450    // TODO(b/155123175): SNMP and netstat don't work on gVisor.
   451    SKIP_IF(IsRunningOnGvisor());
   452  
   453    std::string contents =
   454        ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   455  
   456    int name_count = 0;
   457    int value_count = 0;
   458    std::vector<absl::string_view> lines = absl::StrSplit(contents, '\n');
   459    for (size_t i = 0; i + 1 < lines.size(); i += 2) {
   460      std::vector<absl::string_view> names =
   461          absl::StrSplit(lines[i], absl::ByAnyChar("\t "));
   462      std::vector<absl::string_view> values =
   463          absl::StrSplit(lines[i + 1], absl::ByAnyChar("\t "));
   464      EXPECT_EQ(names.size(), values.size()) << " mismatch in lines '" << lines[i]
   465                                             << "' and '" << lines[i + 1] << "'";
   466      for (size_t j = 0; j < names.size() && j < values.size(); ++j) {
   467        if (names[j] == "RetransSegs") {
   468          ++name_count;
   469          int64_t val;
   470          if (absl::SimpleAtoi(values[j], &val)) {
   471            ++value_count;
   472          }
   473        }
   474      }
   475    }
   476    EXPECT_EQ(name_count, 1);
   477    EXPECT_EQ(value_count, 1);
   478  }
   479  
   480  TEST(ProcSysNetIpv4Recovery, Exists) {
   481    EXPECT_THAT(open("/proc/sys/net/ipv4/tcp_recovery", O_RDONLY),
   482                SyscallSucceeds());
   483  }
   484  
   485  TEST(ProcSysNetIpv4Recovery, CanReadAndWrite) {
   486    // TODO(b/162988252): Enable save/restore for this test after the bug is
   487    // fixed.
   488    DisableSave ds;
   489  
   490    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_DAC_OVERRIDE))));
   491  
   492    auto const fd = ASSERT_NO_ERRNO_AND_VALUE(
   493        Open("/proc/sys/net/ipv4/tcp_recovery", O_RDWR));
   494  
   495    char buf[10] = {'\0'};
   496    char to_write = '2';
   497  
   498    // Check initial value is set to 1.
   499    EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
   500                SyscallSucceedsWithValue(sizeof(to_write) + 1));
   501    if (IsRunningOnGvisor()) {
   502      // TODO(gvisor.dev/issue/5243): TCPRACKLossDetection = 1 should be turned on
   503      // by default.
   504      EXPECT_EQ(strcmp(buf, "0\n"), 0);
   505    } else {
   506      EXPECT_EQ(strcmp(buf, "1\n"), 0);
   507    }
   508  
   509    // Set tcp_recovery to one of the allowed constants.
   510    EXPECT_THAT(PwriteFd(fd.get(), &to_write, sizeof(to_write), 0),
   511                SyscallSucceedsWithValue(sizeof(to_write)));
   512    EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
   513                SyscallSucceedsWithValue(sizeof(to_write) + 1));
   514    EXPECT_EQ(strcmp(buf, "2\n"), 0);
   515  
   516    // Set tcp_recovery to any random value.
   517    char kMessage[] = "100";
   518    EXPECT_THAT(PwriteFd(fd.get(), kMessage, strlen(kMessage), 0),
   519                SyscallSucceedsWithValue(strlen(kMessage)));
   520    EXPECT_THAT(PreadFd(fd.get(), buf, sizeof(kMessage), 0),
   521                SyscallSucceedsWithValue(sizeof(kMessage)));
   522    EXPECT_EQ(strcmp(buf, "100\n"), 0);
   523  }
   524  
   525  TEST(ProcSysNetIpv4IpForward, Exists) {
   526    auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kIpForward, O_RDONLY));
   527  }
   528  
   529  TEST(ProcSysNetIpv4IpForward, DefaultValueEqZero) {
   530    // Test is only valid in sandbox. Not hermetic in native tests
   531    // running on a arbitrary machine.
   532    SKIP_IF(!IsRunningOnGvisor());
   533    auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kIpForward, O_RDONLY));
   534  
   535    char buf = 101;
   536    EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
   537                SyscallSucceedsWithValue(sizeof(buf)));
   538  
   539    EXPECT_EQ(buf, '0') << "unexpected ip_forward: " << buf;
   540  }
   541  
   542  TEST(ProcSysNetIpv4IpForward, CanReadAndWrite) {
   543    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_DAC_OVERRIDE))));
   544  
   545    auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kIpForward, O_RDWR));
   546  
   547    char buf;
   548    EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
   549                SyscallSucceedsWithValue(sizeof(buf)));
   550  
   551    EXPECT_TRUE(buf == '0' || buf == '1') << "unexpected ip_forward: " << buf;
   552  
   553    // constexpr char to_write = '1';
   554    char to_write = (buf == '1') ? '0' : '1';
   555    EXPECT_THAT(PwriteFd(fd.get(), &to_write, sizeof(to_write), 0),
   556                SyscallSucceedsWithValue(sizeof(to_write)));
   557  
   558    buf = 0;
   559    EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
   560                SyscallSucceedsWithValue(sizeof(buf)));
   561    EXPECT_EQ(buf, to_write);
   562  }
   563  
   564  TEST(ProcSysNetPortRange, CanReadAndWrite) {
   565    int min;
   566    int max;
   567    std::string rangefile = ASSERT_NO_ERRNO_AND_VALUE(GetContents(kRangeFile));
   568    ASSERT_EQ(rangefile.back(), '\n');
   569    rangefile.pop_back();
   570    std::vector<std::string> range =
   571        absl::StrSplit(rangefile, absl::ByAnyChar("\t "));
   572    ASSERT_GT(range.size(), 1);
   573    ASSERT_TRUE(absl::SimpleAtoi(range.front(), &min));
   574    ASSERT_TRUE(absl::SimpleAtoi(range.back(), &max));
   575    EXPECT_LE(min, max);
   576  
   577    // If the file isn't writable, there's nothing else to do here.
   578    if (access(kRangeFile, W_OK)) {
   579      return;
   580    }
   581  
   582    constexpr int kSize = 77;
   583    FileDescriptor fd =
   584        ASSERT_NO_ERRNO_AND_VALUE(Open(kRangeFile, O_WRONLY | O_TRUNC, 0));
   585    max = min + kSize;
   586    const std::string small_range = absl::StrFormat("%d %d", min, max);
   587    ASSERT_THAT(write(fd.get(), small_range.c_str(), small_range.size()),
   588                SyscallSucceedsWithValue(small_range.size()));
   589  
   590    rangefile = ASSERT_NO_ERRNO_AND_VALUE(GetContents(kRangeFile));
   591    ASSERT_EQ(rangefile.back(), '\n');
   592    rangefile.pop_back();
   593    range = absl::StrSplit(rangefile, absl::ByAnyChar("\t "));
   594    ASSERT_GT(range.size(), 1);
   595    ASSERT_TRUE(absl::SimpleAtoi(range.front(), &min));
   596    ASSERT_TRUE(absl::SimpleAtoi(range.back(), &max));
   597    EXPECT_EQ(min + kSize, max);
   598  }
   599  
   600  }  // namespace
   601  }  // namespace testing
   602  }  // namespace gvisor