gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/proc_net_udp.cc (about)

     1  // Copyright 2022 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 <netinet/tcp.h>
    16  #include <sys/socket.h>
    17  #include <sys/stat.h>
    18  #include <sys/types.h>
    19  #include <unistd.h>
    20  
    21  #include "gtest/gtest.h"
    22  #include "absl/strings/match.h"
    23  #include "absl/strings/numbers.h"
    24  #include "absl/strings/str_join.h"
    25  #include "absl/strings/str_split.h"
    26  #include "test/syscalls/linux/ip_socket_test_util.h"
    27  #include "test/util/file_descriptor.h"
    28  #include "test/util/test_util.h"
    29  
    30  namespace gvisor {
    31  namespace testing {
    32  namespace {
    33  
    34  using absl::StrCat;
    35  using absl::StrFormat;
    36  using absl::StrSplit;
    37  
    38  constexpr char kProcNetUDPHeaderSubStr[] =
    39      "sl  local_address rem_address   st tx_queue rx_queue tr tm->when "
    40      "retrnsmt   uid  timeout inode ref pointer drops";
    41  
    42  // UDPEntry represents a single entry from /proc/net/udp.
    43  struct UDPEntry {
    44    uint32_t local_addr;
    45    uint16_t local_port;
    46  
    47    uint32_t remote_addr;
    48    uint16_t remote_port;
    49  
    50    uint64_t state;
    51    uint64_t uid;
    52    uint64_t inode;
    53  };
    54  
    55  std::string DescribeFirstInetSocket(const SocketPair& sockets) {
    56    const struct sockaddr* addr = sockets.first_addr();
    57    return StrFormat("First test socket: fd:%d %8X:%4X", sockets.first_fd(),
    58                     IPFromInetSockaddr(addr), PortFromInetSockaddr(addr));
    59  }
    60  
    61  std::string DescribeSecondInetSocket(const SocketPair& sockets) {
    62    const struct sockaddr* addr = sockets.second_addr();
    63    return StrFormat("Second test socket fd:%d %8X:%4X", sockets.second_fd(),
    64                     IPFromInetSockaddr(addr), PortFromInetSockaddr(addr));
    65  }
    66  
    67  // Finds the first entry in 'entries' for which 'predicate' returns true.
    68  // Returns true on match, and set 'match' to a copy of the matching entry. If
    69  // 'match' is null, it's ignored.
    70  bool FindBy(const std::vector<UDPEntry>& entries, UDPEntry* match,
    71              std::function<bool(const UDPEntry&)> predicate) {
    72    for (const UDPEntry& entry : entries) {
    73      if (predicate(entry)) {
    74        if (match != nullptr) {
    75          *match = entry;
    76        }
    77        return true;
    78      }
    79    }
    80    return false;
    81  }
    82  
    83  bool FindByLocalAddr(const std::vector<UDPEntry>& entries, UDPEntry* match,
    84                       const struct sockaddr* addr) {
    85    uint32_t host = IPFromInetSockaddr(addr);
    86    uint16_t port = PortFromInetSockaddr(addr);
    87    return FindBy(entries, match, [host, port](const UDPEntry& e) {
    88      return (e.local_addr == host && e.local_port == port);
    89    });
    90  }
    91  
    92  bool FindByRemoteAddr(const std::vector<UDPEntry>& entries, UDPEntry* match,
    93                        const struct sockaddr* addr) {
    94    uint32_t host = IPFromInetSockaddr(addr);
    95    uint16_t port = PortFromInetSockaddr(addr);
    96    return FindBy(entries, match, [host, port](const UDPEntry& e) {
    97      return (e.remote_addr == host && e.remote_port == port);
    98    });
    99  }
   100  
   101  PosixErrorOr<uint64_t> InodeFromSocketFD(int fd) {
   102    ASSIGN_OR_RETURN_ERRNO(struct stat s, Fstat(fd));
   103    if (!S_ISSOCK(s.st_mode)) {
   104      return PosixError(EINVAL, StrFormat("FD %d is not a socket", fd));
   105    }
   106    return s.st_ino;
   107  }
   108  
   109  PosixErrorOr<bool> FindByFD(const std::vector<UDPEntry>& entries,
   110                              UDPEntry* match, int fd) {
   111    ASSIGN_OR_RETURN_ERRNO(uint64_t inode, InodeFromSocketFD(fd));
   112    return FindBy(entries, match,
   113                  [inode](const UDPEntry& e) { return (e.inode == inode); });
   114  }
   115  
   116  // Returns a parsed representation of /proc/net/udp entries.
   117  PosixErrorOr<std::vector<UDPEntry>> ProcNetUDPEntries() {
   118    std::string content;
   119    RETURN_IF_ERRNO(GetContents("/proc/net/udp", &content));
   120  
   121    bool found_header = false;
   122    std::vector<UDPEntry> entries;
   123    std::vector<std::string> lines = StrSplit(content, '\n');
   124    for (const std::string& line : lines) {
   125      if (!found_header) {
   126        EXPECT_TRUE(absl::StrContains(line, kProcNetUDPHeaderSubStr));
   127        found_header = true;
   128        continue;
   129      }
   130      if (line.empty()) {
   131        continue;
   132      }
   133  
   134      // Parse a single entry from /proc/net/udp.
   135      //
   136      // Example entries:
   137      //
   138      // clang-format off
   139      //
   140      //  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode ref pointer drops
   141      // 3503: 0100007F:0035 00000000:0000 07 00000000:00000000 00:00000000 00000000     0        0 33317 2 0000000000000000 0
   142      // 3518: 00000000:0044 00000000:0000 07 00000000:00000000 00:00000000 00000000     0        0 40394 2 0000000000000000 0
   143      //   ^     ^       ^     ^       ^   ^     ^       ^      ^     ^        ^         ^        ^   ^   ^      ^           ^
   144      //   0     1       2     3       4   5     6       7      8     9       10        11       12  13  14     15           16
   145      //
   146      // clang-format on
   147  
   148      UDPEntry entry;
   149      std::vector<std::string> fields =
   150          StrSplit(line, absl::ByAnyChar(": "), absl::SkipEmpty());
   151  
   152      ASSIGN_OR_RETURN_ERRNO(entry.local_addr, AtoiBase(fields[1], 16));
   153      ASSIGN_OR_RETURN_ERRNO(entry.local_port, AtoiBase(fields[2], 16));
   154  
   155      ASSIGN_OR_RETURN_ERRNO(entry.remote_addr, AtoiBase(fields[3], 16));
   156      ASSIGN_OR_RETURN_ERRNO(entry.remote_port, AtoiBase(fields[4], 16));
   157  
   158      ASSIGN_OR_RETURN_ERRNO(entry.state, AtoiBase(fields[5], 16));
   159      ASSIGN_OR_RETURN_ERRNO(entry.uid, Atoi<uint64_t>(fields[11]));
   160      ASSIGN_OR_RETURN_ERRNO(entry.inode, Atoi<uint64_t>(fields[13]));
   161  
   162      // Linux shares internal data structures between TCP and UDP sockets. The
   163      // proc entries for UDP sockets share some fields with TCP sockets, but
   164      // these fields should always be zero as they're not meaningful for UDP
   165      // sockets.
   166      EXPECT_EQ(fields[8], "00") << StrFormat("sl:%s, tr", fields[0]);
   167      EXPECT_EQ(fields[9], "00000000") << StrFormat("sl:%s, tm->when", fields[0]);
   168      EXPECT_EQ(fields[10], "00000000")
   169          << StrFormat("sl:%s, retrnsmt", fields[0]);
   170      EXPECT_EQ(fields[12], "0") << StrFormat("sl:%s, timeout", fields[0]);
   171  
   172      entries.push_back(entry);
   173    }
   174    std::cerr << "<end of /proc/net/udp>" << std::endl;
   175  
   176    return entries;
   177  }
   178  
   179  TEST(ProcNetUDP, Exists) {
   180    const std::string content =
   181        ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/udp"));
   182    EXPECT_TRUE(absl::StrContains(content, kProcNetUDPHeaderSubStr));
   183  }
   184  
   185  TEST(ProcNetUDP, EntryUID) {
   186    auto sockets =
   187        ASSERT_NO_ERRNO_AND_VALUE(IPv4UDPBidirectionalBindSocketPair(0).Create());
   188    std::vector<UDPEntry> entries =
   189        ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries());
   190    UDPEntry e;
   191    ASSERT_TRUE(FindByLocalAddr(entries, &e, sockets->first_addr()))
   192        << DescribeFirstInetSocket(*sockets);
   193    EXPECT_EQ(e.uid, geteuid());
   194    ASSERT_TRUE(FindByRemoteAddr(entries, &e, sockets->first_addr()))
   195        << DescribeSecondInetSocket(*sockets);
   196    EXPECT_EQ(e.uid, geteuid());
   197  }
   198  
   199  TEST(ProcNetUDP, FindMutualEntries) {
   200    auto sockets =
   201        ASSERT_NO_ERRNO_AND_VALUE(IPv4UDPBidirectionalBindSocketPair(0).Create());
   202    std::vector<UDPEntry> entries =
   203        ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries());
   204  
   205    EXPECT_TRUE(FindByLocalAddr(entries, nullptr, sockets->first_addr()))
   206        << DescribeFirstInetSocket(*sockets);
   207    EXPECT_TRUE(FindByRemoteAddr(entries, nullptr, sockets->first_addr()))
   208        << DescribeSecondInetSocket(*sockets);
   209  
   210    EXPECT_TRUE(FindByLocalAddr(entries, nullptr, sockets->second_addr()))
   211        << DescribeSecondInetSocket(*sockets);
   212    EXPECT_TRUE(FindByRemoteAddr(entries, nullptr, sockets->second_addr()))
   213        << DescribeFirstInetSocket(*sockets);
   214  }
   215  
   216  TEST(ProcNetUDP, EntriesRemovedOnClose) {
   217    auto sockets =
   218        ASSERT_NO_ERRNO_AND_VALUE(IPv4UDPBidirectionalBindSocketPair(0).Create());
   219    std::vector<UDPEntry> entries =
   220        ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries());
   221  
   222    EXPECT_TRUE(FindByLocalAddr(entries, nullptr, sockets->first_addr()))
   223        << DescribeFirstInetSocket(*sockets);
   224    EXPECT_TRUE(FindByLocalAddr(entries, nullptr, sockets->second_addr()))
   225        << DescribeSecondInetSocket(*sockets);
   226  
   227    EXPECT_THAT(close(sockets->release_first_fd()), SyscallSucceeds());
   228    entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries());
   229    // First socket's entry should be gone, but the second socket's entry should
   230    // still exist.
   231    EXPECT_FALSE(FindByLocalAddr(entries, nullptr, sockets->first_addr()))
   232        << DescribeFirstInetSocket(*sockets);
   233    EXPECT_TRUE(FindByLocalAddr(entries, nullptr, sockets->second_addr()))
   234        << DescribeSecondInetSocket(*sockets);
   235  
   236    EXPECT_THAT(close(sockets->release_second_fd()), SyscallSucceeds());
   237    entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries());
   238    // Both entries should be gone.
   239    EXPECT_FALSE(FindByLocalAddr(entries, nullptr, sockets->first_addr()))
   240        << DescribeFirstInetSocket(*sockets);
   241    EXPECT_FALSE(FindByLocalAddr(entries, nullptr, sockets->second_addr()))
   242        << DescribeSecondInetSocket(*sockets);
   243  }
   244  
   245  PosixErrorOr<std::unique_ptr<FileDescriptor>> BoundUDPSocket() {
   246    ASSIGN_OR_RETURN_ERRNO(std::unique_ptr<FileDescriptor> socket,
   247                           IPv4UDPUnboundSocket(0).Create());
   248    struct sockaddr_in addr;
   249    addr.sin_family = AF_INET;
   250    addr.sin_addr.s_addr = htonl(INADDR_ANY);
   251    addr.sin_port = 0;
   252  
   253    int res = bind(socket->get(), reinterpret_cast<const struct sockaddr*>(&addr),
   254                   sizeof(addr));
   255    if (res) {
   256      return PosixError(errno, "bind()");
   257    }
   258    return socket;
   259  }
   260  
   261  TEST(ProcNetUDP, BoundEntry) {
   262    std::unique_ptr<FileDescriptor> socket =
   263        ASSERT_NO_ERRNO_AND_VALUE(BoundUDPSocket());
   264    struct sockaddr addr;
   265    socklen_t len = sizeof(addr);
   266    ASSERT_THAT(getsockname(socket->get(), &addr, &len), SyscallSucceeds());
   267    uint16_t port = PortFromInetSockaddr(&addr);
   268  
   269    std::vector<UDPEntry> entries =
   270        ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries());
   271    UDPEntry e;
   272    ASSERT_TRUE(ASSERT_NO_ERRNO_AND_VALUE(FindByFD(entries, &e, socket->get())));
   273    EXPECT_EQ(e.local_port, port);
   274    EXPECT_EQ(e.remote_addr, 0);
   275    EXPECT_EQ(e.remote_port, 0);
   276  }
   277  
   278  TEST(ProcNetUDP, BoundSocketStateClosed) {
   279    std::unique_ptr<FileDescriptor> socket =
   280        ASSERT_NO_ERRNO_AND_VALUE(BoundUDPSocket());
   281    std::vector<UDPEntry> entries =
   282        ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries());
   283    UDPEntry e;
   284    ASSERT_TRUE(ASSERT_NO_ERRNO_AND_VALUE(FindByFD(entries, &e, socket->get())));
   285    EXPECT_EQ(e.state, TCP_CLOSE);
   286  }
   287  
   288  TEST(ProcNetUDP, ConnectedSocketStateEstablished) {
   289    auto sockets =
   290        ASSERT_NO_ERRNO_AND_VALUE(IPv4UDPBidirectionalBindSocketPair(0).Create());
   291    std::vector<UDPEntry> entries =
   292        ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries());
   293  
   294    UDPEntry e;
   295    ASSERT_TRUE(FindByLocalAddr(entries, &e, sockets->first_addr()))
   296        << DescribeFirstInetSocket(*sockets);
   297    EXPECT_EQ(e.state, TCP_ESTABLISHED);
   298  
   299    ASSERT_TRUE(FindByLocalAddr(entries, &e, sockets->second_addr()))
   300        << DescribeSecondInetSocket(*sockets);
   301    EXPECT_EQ(e.state, TCP_ESTABLISHED);
   302  }
   303  
   304  }  // namespace
   305  }  // namespace testing
   306  }  // namespace gvisor