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