gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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/util/capability_util.h"
    32  #include "test/util/file_descriptor.h"
    33  #include "test/util/fs_util.h"
    34  #include "test/util/socket_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_NET_ADMIN))) ||
    82            IsRunningWithHostinet());
    83  
    84    auto const fd =
    85        ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/sys/net/ipv4/tcp_sack", O_RDWR));
    86  
    87    char buf;
    88    EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
    89                SyscallSucceedsWithValue(sizeof(buf)));
    90  
    91    EXPECT_TRUE(buf == '0' || buf == '1') << "unexpected tcp_sack: " << buf;
    92  
    93    char to_write = (buf == '1') ? '0' : '1';
    94    EXPECT_THAT(PwriteFd(fd.get(), &to_write, sizeof(to_write), 0),
    95                SyscallSucceedsWithValue(sizeof(to_write)));
    96  
    97    buf = 0;
    98    EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
    99                SyscallSucceedsWithValue(sizeof(buf)));
   100    EXPECT_EQ(buf, to_write);
   101  }
   102  
   103  // DeviceEntry is an entry in /proc/net/dev
   104  struct DeviceEntry {
   105    std::string name;
   106    uint64_t stats[16];
   107  };
   108  
   109  PosixErrorOr<std::vector<DeviceEntry>> GetDeviceMetricsFromProc(
   110      const std::string dev) {
   111    std::vector<std::string> lines = absl::StrSplit(dev, '\n');
   112    std::vector<DeviceEntry> entries;
   113  
   114    // /proc/net/dev prints 2 lines of headers followed by a line of metrics for
   115    // each network interface.
   116    for (unsigned i = 2; i < lines.size(); i++) {
   117      // Ignore empty lines.
   118      if (lines[i].empty()) {
   119        continue;
   120      }
   121  
   122      std::vector<std::string> values =
   123          absl::StrSplit(lines[i], ' ', absl::SkipWhitespace());
   124  
   125      // Interface name + 16 values.
   126      if (values.size() != 17) {
   127        return PosixError(EINVAL, "invalid line: " + lines[i]);
   128      }
   129  
   130      DeviceEntry entry;
   131      entry.name = values[0];
   132      // Skip the interface name and read only the values.
   133      for (unsigned j = 1; j < 17; j++) {
   134        uint64_t num;
   135        if (!absl::SimpleAtoi(values[j], &num)) {
   136          return PosixError(EINVAL, "invalid value: " + values[j]);
   137        }
   138        entry.stats[j - 1] = num;
   139      }
   140  
   141      entries.push_back(entry);
   142    }
   143  
   144    return entries;
   145  }
   146  
   147  // TEST(ProcNetDev, Format) tests that /proc/net/dev is parsable and
   148  // contains at least one entry.
   149  TEST(ProcNetDev, Format) {
   150    auto dev = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/dev"));
   151    auto entries = ASSERT_NO_ERRNO_AND_VALUE(GetDeviceMetricsFromProc(dev));
   152  
   153    EXPECT_GT(entries.size(), 0);
   154  }
   155  
   156  // GetMibsAllocationSysctl retuns a value of the net.core.mibs_allocation
   157  // sysctl./proc/sys/net/core/mibs_allocation
   158  //
   159  // When mibs_allocation is unset, a netns creation inherits MIB from init
   160  // network namespace. Otherwise, MIBS is allocated for each namespace.
   161  int GetMibsAllocationSysctl() {
   162    auto ret = GetContents("/proc/sys/net/core/mibs_allocation");
   163    if (!ret.ok()) {
   164      // The current kernel doesn't support mibs_allocation.
   165      return 1;
   166    }
   167    int32_t val;
   168    EXPECT_TRUE(absl::SimpleAtoi(ret.ValueOrDie(), &val));
   169    return val;
   170  }
   171  
   172  // GetSNMPMetricFromProc retrieves the metric named `item` of `type` from the
   173  // `snmp` string which represents the file contents of /proc/net/snmp.
   174  //
   175  // Note to test writers: If you are writing tests to check the change in value
   176  // of SNMP metrics, note that if GetMibsAllocationSysctl() == 0
   177  // (net.core.mibs_allocation isn't set), MIB is from the init network namespace
   178  // and hence these metrics can have system-wide noise.
   179  //
   180  // A feasible way of testing is the following:
   181  // * Consult RFC 1213 to learn the "SYNTAX" of the metric.
   182  // * If net.core.mibs_allocation is set:
   183  //   - Can test any metric while checking for hard equality.
   184  // * If net.core.mibs_allocation is NOT set (system-wide noise is present):
   185  //   - Only test "SYNTAX  Counter" metrics.
   186  //   - Counter metrics are increasing in nature, so use LE/GE equality checks.
   187  PosixErrorOr<uint64_t> GetSNMPMetricFromProc(const std::string snmp,
   188                                               const std::string& type,
   189                                               const std::string& item) {
   190    std::vector<std::string> snmp_vec = absl::StrSplit(snmp, '\n');
   191  
   192    // /proc/net/snmp prints a line of headers followed by a line of metrics.
   193    // Only search the headers.
   194    for (unsigned i = 0; i < snmp_vec.size(); i = i + 2) {
   195      if (!absl::StartsWith(snmp_vec[i], type)) continue;
   196  
   197      std::vector<std::string> fields =
   198          absl::StrSplit(snmp_vec[i], ' ', absl::SkipWhitespace());
   199  
   200      EXPECT_TRUE((i + 1) < snmp_vec.size());
   201      std::vector<std::string> values =
   202          absl::StrSplit(snmp_vec[i + 1], ' ', absl::SkipWhitespace());
   203  
   204      EXPECT_TRUE(!fields.empty() && fields.size() == values.size());
   205  
   206      // Metrics start at the first index.
   207      for (unsigned j = 1; j < fields.size(); j++) {
   208        if (fields[j] == item) {
   209          uint64_t val;
   210          if (!absl::SimpleAtoi(values[j], &val)) {
   211            return PosixError(EINVAL,
   212                              absl::StrCat("field is not a number: ", values[j]));
   213          }
   214  
   215          return val;
   216        }
   217      }
   218    }
   219    // We should never get here.
   220    return PosixError(
   221        EINVAL, absl::StrCat("failed to find ", type, "/", item, " in:", snmp));
   222  }
   223  
   224  TEST(ProcNetSnmp, TcpReset) {
   225    SKIP_IF(IsRunningWithHostinet());
   226  
   227    // TODO(gvisor.dev/issue/866): epsocket metrics are not savable.
   228    DisableSave ds;
   229  
   230    uint64_t oldAttemptFails;
   231    uint64_t oldActiveOpens;
   232    uint64_t oldOutRsts;
   233    auto snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   234    oldActiveOpens = ASSERT_NO_ERRNO_AND_VALUE(
   235        GetSNMPMetricFromProc(snmp, "Tcp", "ActiveOpens"));
   236    oldOutRsts =
   237        ASSERT_NO_ERRNO_AND_VALUE(GetSNMPMetricFromProc(snmp, "Tcp", "OutRsts"));
   238    oldAttemptFails = ASSERT_NO_ERRNO_AND_VALUE(
   239        GetSNMPMetricFromProc(snmp, "Tcp", "AttemptFails"));
   240  
   241    FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, 0));
   242  
   243    struct sockaddr_in sin = {
   244        .sin_family = AF_INET,
   245        .sin_port = htons(1234),
   246    };
   247  
   248    ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1);
   249    ASSERT_THAT(connect(s.get(), (struct sockaddr*)&sin, sizeof(sin)),
   250                SyscallFailsWithErrno(ECONNREFUSED));
   251  
   252    uint64_t newAttemptFails;
   253    uint64_t newActiveOpens;
   254    uint64_t newOutRsts;
   255    snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   256    newActiveOpens = ASSERT_NO_ERRNO_AND_VALUE(
   257        GetSNMPMetricFromProc(snmp, "Tcp", "ActiveOpens"));
   258    newOutRsts =
   259        ASSERT_NO_ERRNO_AND_VALUE(GetSNMPMetricFromProc(snmp, "Tcp", "OutRsts"));
   260    newAttemptFails = ASSERT_NO_ERRNO_AND_VALUE(
   261        GetSNMPMetricFromProc(snmp, "Tcp", "AttemptFails"));
   262  
   263    if (GetMibsAllocationSysctl()) {
   264      EXPECT_EQ(oldActiveOpens + 1, newActiveOpens);
   265      EXPECT_EQ(oldOutRsts + 1, newOutRsts);
   266      EXPECT_EQ(oldAttemptFails + 1, newAttemptFails);
   267    } else {
   268      // System-wide statistics can have some noise. These metrics should have
   269      // increased by at least 1.
   270      EXPECT_LE(oldActiveOpens + 1, newActiveOpens);
   271      EXPECT_LE(oldOutRsts + 1, newOutRsts);
   272      EXPECT_LE(oldAttemptFails + 1, newAttemptFails);
   273    }
   274  }
   275  
   276  TEST(ProcNetSnmp, TcpEstab) {
   277    SKIP_IF(IsRunningWithHostinet());
   278  
   279    // This test aims to test the tcpCurrEstab metric which has "SYNTAX  Gauge" as
   280    // per RFC 1213. Hence, it becomes infeasible to test this when system-wide
   281    // statistics have noise.
   282    SKIP_IF(GetMibsAllocationSysctl() == 0);
   283  
   284    // TODO(gvisor.dev/issue/866): epsocket metrics are not savable.
   285    DisableSave ds;
   286  
   287    uint64_t oldEstabResets;
   288    uint64_t oldActiveOpens;
   289    uint64_t oldPassiveOpens;
   290    uint64_t oldCurrEstab;
   291    auto snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   292    oldActiveOpens = ASSERT_NO_ERRNO_AND_VALUE(
   293        GetSNMPMetricFromProc(snmp, "Tcp", "ActiveOpens"));
   294    oldPassiveOpens = ASSERT_NO_ERRNO_AND_VALUE(
   295        GetSNMPMetricFromProc(snmp, "Tcp", "PassiveOpens"));
   296    oldCurrEstab = ASSERT_NO_ERRNO_AND_VALUE(
   297        GetSNMPMetricFromProc(snmp, "Tcp", "CurrEstab"));
   298    oldEstabResets = ASSERT_NO_ERRNO_AND_VALUE(
   299        GetSNMPMetricFromProc(snmp, "Tcp", "EstabResets"));
   300  
   301    FileDescriptor s_listen =
   302        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, 0));
   303    struct sockaddr_in sin = {
   304        .sin_family = AF_INET,
   305        .sin_port = 0,
   306    };
   307  
   308    ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1);
   309    ASSERT_THAT(bind(s_listen.get(), (struct sockaddr*)&sin, sizeof(sin)),
   310                SyscallSucceeds());
   311    ASSERT_THAT(listen(s_listen.get(), 1), SyscallSucceeds());
   312  
   313    // Get the port bound by the listening socket.
   314    socklen_t addrlen = sizeof(sin);
   315    ASSERT_THAT(getsockname(s_listen.get(), AsSockAddr(&sin), &addrlen),
   316                SyscallSucceeds());
   317  
   318    FileDescriptor s_connect =
   319        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, 0));
   320    ASSERT_THAT(connect(s_connect.get(), (struct sockaddr*)&sin, sizeof(sin)),
   321                SyscallSucceeds());
   322  
   323    auto s_accept =
   324        ASSERT_NO_ERRNO_AND_VALUE(Accept(s_listen.get(), nullptr, nullptr));
   325  
   326    uint64_t newEstabResets;
   327    uint64_t newActiveOpens;
   328    uint64_t newPassiveOpens;
   329    uint64_t newCurrEstab;
   330    snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   331    newActiveOpens = ASSERT_NO_ERRNO_AND_VALUE(
   332        GetSNMPMetricFromProc(snmp, "Tcp", "ActiveOpens"));
   333    newPassiveOpens = ASSERT_NO_ERRNO_AND_VALUE(
   334        GetSNMPMetricFromProc(snmp, "Tcp", "PassiveOpens"));
   335    newCurrEstab = ASSERT_NO_ERRNO_AND_VALUE(
   336        GetSNMPMetricFromProc(snmp, "Tcp", "CurrEstab"));
   337  
   338    EXPECT_EQ(oldActiveOpens + 1, newActiveOpens);
   339    EXPECT_EQ(oldPassiveOpens + 1, newPassiveOpens);
   340    EXPECT_EQ(oldCurrEstab + 2, newCurrEstab);
   341  
   342    // Send 1 byte from client to server.
   343    ASSERT_THAT(send(s_connect.get(), "a", 1, 0), SyscallSucceedsWithValue(1));
   344  
   345    constexpr int kPollTimeoutMs = 20000;  // Wait up to 20 seconds for the data.
   346  
   347    // Wait until server-side fd sees the data on its side but don't read it.
   348    struct pollfd poll_fd = {s_accept.get(), POLLIN, 0};
   349    ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs),
   350                SyscallSucceedsWithValue(1));
   351  
   352    // Now close server-side fd without reading the data which leads to a RST
   353    // packet sent to client side.
   354    s_accept.reset(-1);
   355  
   356    // Wait until client-side fd sees RST packet.
   357    struct pollfd poll_fd1 = {s_connect.get(), POLLIN, 0};
   358    ASSERT_THAT(RetryEINTR(poll)(&poll_fd1, 1, kPollTimeoutMs),
   359                SyscallSucceedsWithValue(1));
   360  
   361    // Now close client-side fd.
   362    s_connect.reset(-1);
   363  
   364    // Wait until the process of the netstack.
   365    absl::SleepFor(absl::Seconds(1));
   366  
   367    snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   368    newCurrEstab = ASSERT_NO_ERRNO_AND_VALUE(
   369        GetSNMPMetricFromProc(snmp, "Tcp", "CurrEstab"));
   370    newEstabResets = ASSERT_NO_ERRNO_AND_VALUE(
   371        GetSNMPMetricFromProc(snmp, "Tcp", "EstabResets"));
   372  
   373    EXPECT_EQ(oldCurrEstab, newCurrEstab);
   374    EXPECT_EQ(oldEstabResets, newEstabResets - 2);
   375  }
   376  
   377  TEST(ProcNetSnmp, UdpNoPorts) {
   378    SKIP_IF(IsRunningWithHostinet());
   379  
   380    // TODO(gvisor.dev/issue/866): epsocket metrics are not savable.
   381    DisableSave ds;
   382  
   383    uint64_t oldOutDatagrams;
   384    uint64_t oldNoPorts;
   385    auto snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   386    oldOutDatagrams = ASSERT_NO_ERRNO_AND_VALUE(
   387        GetSNMPMetricFromProc(snmp, "Udp", "OutDatagrams"));
   388    oldNoPorts =
   389        ASSERT_NO_ERRNO_AND_VALUE(GetSNMPMetricFromProc(snmp, "Udp", "NoPorts"));
   390  
   391    FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
   392  
   393    struct sockaddr_in sin = {
   394        .sin_family = AF_INET,
   395        .sin_port = htons(4444),
   396    };
   397    ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1);
   398    ASSERT_THAT(sendto(s.get(), "a", 1, 0, (struct sockaddr*)&sin, sizeof(sin)),
   399                SyscallSucceedsWithValue(1));
   400  
   401    uint64_t newOutDatagrams;
   402    uint64_t newNoPorts;
   403    snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   404    newOutDatagrams = ASSERT_NO_ERRNO_AND_VALUE(
   405        GetSNMPMetricFromProc(snmp, "Udp", "OutDatagrams"));
   406    newNoPorts =
   407        ASSERT_NO_ERRNO_AND_VALUE(GetSNMPMetricFromProc(snmp, "Udp", "NoPorts"));
   408  
   409    if (GetMibsAllocationSysctl()) {
   410      EXPECT_EQ(oldOutDatagrams + 1, newOutDatagrams);
   411      EXPECT_EQ(oldNoPorts + 1, newNoPorts);
   412    } else {
   413      // System-wide statistics can have some noise. These metrics should have
   414      // increased by at least 1.
   415      EXPECT_LE(oldOutDatagrams + 1, newOutDatagrams);
   416      EXPECT_LE(oldNoPorts + 1, newNoPorts);
   417    }
   418  }
   419  
   420  TEST(ProcNetSnmp, UdpIn) {
   421    SKIP_IF(IsRunningWithHostinet());
   422  
   423    // TODO(gvisor.dev/issue/866): epsocket metrics are not savable.
   424    const DisableSave ds;
   425  
   426    uint64_t oldOutDatagrams;
   427    uint64_t oldInDatagrams;
   428    auto snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   429    oldOutDatagrams = ASSERT_NO_ERRNO_AND_VALUE(
   430        GetSNMPMetricFromProc(snmp, "Udp", "OutDatagrams"));
   431    oldInDatagrams = ASSERT_NO_ERRNO_AND_VALUE(
   432        GetSNMPMetricFromProc(snmp, "Udp", "InDatagrams"));
   433  
   434    std::cerr << "snmp: " << std::endl << snmp << std::endl;
   435    FileDescriptor server =
   436        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
   437    struct sockaddr_in sin = {
   438        .sin_family = AF_INET,
   439        .sin_port = htons(0),
   440    };
   441    ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1);
   442    ASSERT_THAT(bind(server.get(), (struct sockaddr*)&sin, sizeof(sin)),
   443                SyscallSucceeds());
   444    // Get the port bound by the server socket.
   445    socklen_t addrlen = sizeof(sin);
   446    ASSERT_THAT(getsockname(server.get(), AsSockAddr(&sin), &addrlen),
   447                SyscallSucceeds());
   448  
   449    FileDescriptor client =
   450        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
   451    ASSERT_THAT(
   452        sendto(client.get(), "a", 1, 0, (struct sockaddr*)&sin, sizeof(sin)),
   453        SyscallSucceedsWithValue(1));
   454  
   455    char buf[128];
   456    ASSERT_THAT(recvfrom(server.get(), buf, sizeof(buf), 0, NULL, NULL),
   457                SyscallSucceedsWithValue(1));
   458  
   459    uint64_t newOutDatagrams;
   460    uint64_t newInDatagrams;
   461    snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   462    std::cerr << "new snmp: " << std::endl << snmp << std::endl;
   463    newOutDatagrams = ASSERT_NO_ERRNO_AND_VALUE(
   464        GetSNMPMetricFromProc(snmp, "Udp", "OutDatagrams"));
   465    newInDatagrams = ASSERT_NO_ERRNO_AND_VALUE(
   466        GetSNMPMetricFromProc(snmp, "Udp", "InDatagrams"));
   467  
   468    if (GetMibsAllocationSysctl()) {
   469      EXPECT_EQ(oldOutDatagrams + 1, newOutDatagrams);
   470      EXPECT_EQ(oldInDatagrams + 1, newInDatagrams);
   471    } else {
   472      // System-wide statistics can have some noise. These metrics should have
   473      // increased by at least 1.
   474      EXPECT_LE(oldOutDatagrams + 1, newOutDatagrams);
   475      EXPECT_LE(oldInDatagrams + 1, newInDatagrams);
   476    }
   477  }
   478  
   479  TEST(ProcNetSnmp, CheckNetStat) {
   480    // TODO(b/155123175): SNMP and netstat don't work on gVisor.
   481    SKIP_IF(IsRunningOnGvisor());
   482  
   483    std::string contents =
   484        ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/netstat"));
   485  
   486    int name_count = 0;
   487    int value_count = 0;
   488    std::vector<absl::string_view> lines = absl::StrSplit(contents, '\n');
   489    for (size_t i = 0; i + 1 < lines.size(); i += 2) {
   490      std::vector<absl::string_view> names =
   491          absl::StrSplit(lines[i], absl::ByAnyChar("\t "));
   492      std::vector<absl::string_view> values =
   493          absl::StrSplit(lines[i + 1], absl::ByAnyChar("\t "));
   494      EXPECT_EQ(names.size(), values.size()) << " mismatch in lines '" << lines[i]
   495                                             << "' and '" << lines[i + 1] << "'";
   496      for (size_t j = 0; j < names.size() && j < values.size(); ++j) {
   497        if (names[j] == "TCPOrigDataSent" || names[j] == "TCPSynRetrans" ||
   498            names[j] == "TCPDSACKRecv" || names[j] == "TCPDSACKOfoRecv") {
   499          ++name_count;
   500          int64_t val;
   501          if (absl::SimpleAtoi(values[j], &val)) {
   502            ++value_count;
   503          }
   504        }
   505      }
   506    }
   507    EXPECT_EQ(name_count, 4);
   508    EXPECT_EQ(value_count, 4);
   509  }
   510  
   511  TEST(ProcNetSnmp, Stat) {
   512    struct stat st = {};
   513    ASSERT_THAT(stat("/proc/net/snmp", &st), SyscallSucceeds());
   514  }
   515  
   516  TEST(ProcNetSnmp, CheckSnmp) {
   517    // TODO(b/155123175): SNMP and netstat don't work on gVisor.
   518    SKIP_IF(IsRunningOnGvisor());
   519  
   520    std::string contents =
   521        ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp"));
   522  
   523    int name_count = 0;
   524    int value_count = 0;
   525    std::vector<absl::string_view> lines = absl::StrSplit(contents, '\n');
   526    for (size_t i = 0; i + 1 < lines.size(); i += 2) {
   527      std::vector<absl::string_view> names =
   528          absl::StrSplit(lines[i], absl::ByAnyChar("\t "));
   529      std::vector<absl::string_view> values =
   530          absl::StrSplit(lines[i + 1], absl::ByAnyChar("\t "));
   531      EXPECT_EQ(names.size(), values.size()) << " mismatch in lines '" << lines[i]
   532                                             << "' and '" << lines[i + 1] << "'";
   533      for (size_t j = 0; j < names.size() && j < values.size(); ++j) {
   534        if (names[j] == "RetransSegs") {
   535          ++name_count;
   536          int64_t val;
   537          if (absl::SimpleAtoi(values[j], &val)) {
   538            ++value_count;
   539          }
   540        }
   541      }
   542    }
   543    EXPECT_EQ(name_count, 1);
   544    EXPECT_EQ(value_count, 1);
   545  }
   546  
   547  TEST(ProcSysNetIpv4Recovery, Exists) {
   548    EXPECT_THAT(open("/proc/sys/net/ipv4/tcp_recovery", O_RDONLY),
   549                SyscallSucceeds());
   550  }
   551  
   552  TEST(ProcSysNetIpv4Recovery, CanReadAndWrite) {
   553    // TODO(b/162988252): Enable save/restore for this test after the bug is
   554    // fixed.
   555    DisableSave ds;
   556  
   557    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_NET_ADMIN))) ||
   558            IsRunningWithHostinet());
   559  
   560    auto const fd = ASSERT_NO_ERRNO_AND_VALUE(
   561        Open("/proc/sys/net/ipv4/tcp_recovery", O_RDWR));
   562  
   563    char buf[10] = {'\0'};
   564    char to_write = '2';
   565  
   566    // Check initial value is set to 1.
   567    EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
   568                SyscallSucceedsWithValue(sizeof(to_write) + 1));
   569    EXPECT_EQ(strcmp(buf, "1\n"), 0);
   570  
   571    // Set tcp_recovery to one of the allowed constants.
   572    EXPECT_THAT(PwriteFd(fd.get(), &to_write, sizeof(to_write), 0),
   573                SyscallSucceedsWithValue(sizeof(to_write)));
   574    EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
   575                SyscallSucceedsWithValue(sizeof(to_write) + 1));
   576    EXPECT_EQ(strcmp(buf, "2\n"), 0);
   577  
   578    // Set tcp_recovery to any random value.
   579    char kMessage[] = "100";
   580    EXPECT_THAT(PwriteFd(fd.get(), kMessage, strlen(kMessage), 0),
   581                SyscallSucceedsWithValue(strlen(kMessage)));
   582    EXPECT_THAT(PreadFd(fd.get(), buf, sizeof(kMessage), 0),
   583                SyscallSucceedsWithValue(sizeof(kMessage)));
   584    EXPECT_EQ(strcmp(buf, "100\n"), 0);
   585  }
   586  
   587  TEST(ProcSysNetIpv4IpForward, Exists) {
   588    auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kIpForward, O_RDONLY));
   589  }
   590  
   591  TEST(ProcSysNetIpv4IpForward, DefaultValueEqZero) {
   592    // Test is only valid in sandbox. Not hermetic in native tests
   593    // running on a arbitrary machine.
   594    SKIP_IF(!IsRunningOnGvisor());
   595    auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kIpForward, O_RDONLY));
   596  
   597    char buf = 101;
   598    EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
   599                SyscallSucceedsWithValue(sizeof(buf)));
   600  
   601    EXPECT_EQ(buf, '0') << "unexpected ip_forward: " << buf;
   602  }
   603  
   604  TEST(ProcSysNetIpv4IpForward, CanReadAndWrite) {
   605    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_NET_ADMIN))) ||
   606            IsRunningWithHostinet());
   607  
   608    auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kIpForward, O_RDWR));
   609  
   610    char buf;
   611    EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
   612                SyscallSucceedsWithValue(sizeof(buf)));
   613  
   614    EXPECT_TRUE(buf == '0' || buf == '1') << "unexpected ip_forward: " << buf;
   615  
   616    // constexpr char to_write = '1';
   617    char to_write = (buf == '1') ? '0' : '1';
   618    EXPECT_THAT(PwriteFd(fd.get(), &to_write, sizeof(to_write), 0),
   619                SyscallSucceedsWithValue(sizeof(to_write)));
   620  
   621    buf = 0;
   622    EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
   623                SyscallSucceedsWithValue(sizeof(buf)));
   624    EXPECT_EQ(buf, to_write);
   625  }
   626  
   627  TEST(ProcSysNetPortRange, CanReadAndWrite) {
   628    SKIP_IF(IsRunningWithHostinet());
   629  
   630    int min;
   631    int max;
   632    std::string rangefile = ASSERT_NO_ERRNO_AND_VALUE(GetContents(kRangeFile));
   633    ASSERT_EQ(rangefile.back(), '\n');
   634    rangefile.pop_back();
   635    std::vector<std::string> range =
   636        absl::StrSplit(rangefile, absl::ByAnyChar("\t "));
   637    ASSERT_GT(range.size(), 1);
   638    ASSERT_TRUE(absl::SimpleAtoi(range.front(), &min));
   639    ASSERT_TRUE(absl::SimpleAtoi(range.back(), &max));
   640    EXPECT_LE(min, max);
   641  
   642    // If the file isn't writable, there's nothing else to do here.
   643    if (access(kRangeFile, W_OK)) {
   644      return;
   645    }
   646  
   647    constexpr int kSize = 77;
   648    FileDescriptor fd =
   649        ASSERT_NO_ERRNO_AND_VALUE(Open(kRangeFile, O_WRONLY | O_TRUNC, 0));
   650    max = min + kSize;
   651    const std::string small_range = absl::StrFormat("%d %d", min, max);
   652    ASSERT_THAT(write(fd.get(), small_range.c_str(), small_range.size()),
   653                SyscallSucceedsWithValue(small_range.size()));
   654  
   655    rangefile = ASSERT_NO_ERRNO_AND_VALUE(GetContents(kRangeFile));
   656    ASSERT_EQ(rangefile.back(), '\n');
   657    rangefile.pop_back();
   658    range = absl::StrSplit(rangefile, absl::ByAnyChar("\t "));
   659    ASSERT_GT(range.size(), 1);
   660    ASSERT_TRUE(absl::SimpleAtoi(range.front(), &min));
   661    ASSERT_TRUE(absl::SimpleAtoi(range.back(), &max));
   662    EXPECT_EQ(min + kSize, max);
   663  }
   664  
   665  }  // namespace
   666  }  // namespace testing
   667  }  // namespace gvisor