gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/proc_net_tcp.cc (about) 1 // Copyright 2019 Google LLC 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/numbers.h" 23 #include "absl/strings/str_join.h" 24 #include "absl/strings/str_split.h" 25 #include "test/syscalls/linux/ip_socket_test_util.h" 26 #include "test/util/file_descriptor.h" 27 #include "test/util/test_util.h" 28 29 namespace gvisor { 30 namespace testing { 31 namespace { 32 33 using absl::StrCat; 34 using absl::StrSplit; 35 36 constexpr char kProcNetTCPHeader[] = 37 " sl local_address rem_address st tx_queue rx_queue tr tm->when " 38 "retrnsmt uid timeout inode " 39 " "; 40 41 // TCPEntry represents a single entry from /proc/net/tcp. 42 struct TCPEntry { 43 uint32_t local_addr; 44 uint16_t local_port; 45 46 uint32_t remote_addr; 47 uint16_t remote_port; 48 49 uint64_t state; 50 uint64_t uid; 51 uint64_t inode; 52 }; 53 54 // Finds the first entry in 'entries' for which 'predicate' returns true. 55 // Returns true on match, and sets 'match' to a copy of the matching entry. If 56 // 'match' is null, it's ignored. 57 bool FindBy(const std::vector<TCPEntry>& entries, TCPEntry* match, 58 std::function<bool(const TCPEntry&)> predicate) { 59 for (const TCPEntry& entry : entries) { 60 if (predicate(entry)) { 61 if (match != nullptr) { 62 *match = entry; 63 } 64 return true; 65 } 66 } 67 return false; 68 } 69 70 bool FindByLocalAddr(const std::vector<TCPEntry>& entries, TCPEntry* match, 71 const struct sockaddr* addr) { 72 uint32_t host = IPFromInetSockaddr(addr); 73 uint16_t port = PortFromInetSockaddr(addr); 74 return FindBy(entries, match, [host, port](const TCPEntry& e) { 75 return (e.local_addr == host && e.local_port == port); 76 }); 77 } 78 79 bool FindByRemoteAddr(const std::vector<TCPEntry>& entries, TCPEntry* match, 80 const struct sockaddr* addr) { 81 uint32_t host = IPFromInetSockaddr(addr); 82 uint16_t port = PortFromInetSockaddr(addr); 83 return FindBy(entries, match, [host, port](const TCPEntry& e) { 84 return (e.remote_addr == host && e.remote_port == port); 85 }); 86 } 87 88 // Returns a parsed representation of /proc/net/tcp entries. 89 PosixErrorOr<std::vector<TCPEntry>> ProcNetTCPEntries() { 90 std::string content; 91 RETURN_IF_ERRNO(GetContents("/proc/net/tcp", &content)); 92 93 bool found_header = false; 94 std::vector<TCPEntry> entries; 95 std::vector<std::string> lines = StrSplit(content, '\n'); 96 std::cerr << "<contents of /proc/net/tcp>" << std::endl; 97 for (const std::string& line : lines) { 98 std::cerr << line << std::endl; 99 100 if (!found_header) { 101 EXPECT_EQ(line, kProcNetTCPHeader); 102 found_header = true; 103 continue; 104 } 105 if (line.empty()) { 106 continue; 107 } 108 109 // Parse a single entry from /proc/net/tcp. 110 // 111 // Example entries: 112 // 113 // clang-format off 114 // 115 // sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode 116 // 0: 00000000:006F 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 1968 1 0000000000000000 100 0 0 10 0 117 // 1: 0100007F:7533 00000000:0000 0A 00000000:00000000 00:00000000 00000000 120 0 10684 1 0000000000000000 100 0 0 10 0 118 // ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 119 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 120 // 121 // clang-format on 122 123 TCPEntry entry; 124 std::vector<std::string> fields = 125 StrSplit(line, absl::ByAnyChar(": "), absl::SkipEmpty()); 126 127 ASSIGN_OR_RETURN_ERRNO(entry.local_addr, AtoiBase(fields[1], 16)); 128 ASSIGN_OR_RETURN_ERRNO(entry.local_port, AtoiBase(fields[2], 16)); 129 130 ASSIGN_OR_RETURN_ERRNO(entry.remote_addr, AtoiBase(fields[3], 16)); 131 ASSIGN_OR_RETURN_ERRNO(entry.remote_port, AtoiBase(fields[4], 16)); 132 133 ASSIGN_OR_RETURN_ERRNO(entry.state, AtoiBase(fields[5], 16)); 134 ASSIGN_OR_RETURN_ERRNO(entry.uid, Atoi<uint64_t>(fields[11])); 135 ASSIGN_OR_RETURN_ERRNO(entry.inode, Atoi<uint64_t>(fields[13])); 136 137 entries.push_back(entry); 138 } 139 std::cerr << "<end of /proc/net/tcp>" << std::endl; 140 141 return entries; 142 } 143 144 TEST(ProcNetTCP, Exists) { 145 const std::string content = 146 ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/tcp")); 147 const std::string header_line = StrCat(kProcNetTCPHeader, "\n"); 148 if (IsRunningOnGvisor()) { 149 // Should be just the header since we don't have any tcp sockets yet. 150 EXPECT_EQ(content, header_line); 151 } else { 152 // On a general linux machine, we could have abitrary sockets on the system, 153 // so just check the header. 154 EXPECT_THAT(content, ::testing::StartsWith(header_line)); 155 } 156 } 157 158 TEST(ProcNetTCP, EntryUID) { 159 auto sockets = 160 ASSERT_NO_ERRNO_AND_VALUE(IPv4TCPAcceptBindSocketPair(0).Create()); 161 std::vector<TCPEntry> entries = 162 ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCPEntries()); 163 TCPEntry e; 164 ASSERT_TRUE(FindByLocalAddr(entries, &e, sockets->first_addr())); 165 EXPECT_EQ(e.uid, geteuid()); 166 ASSERT_TRUE(FindByRemoteAddr(entries, &e, sockets->first_addr())); 167 EXPECT_EQ(e.uid, geteuid()); 168 } 169 170 TEST(ProcNetTCP, BindAcceptConnect) { 171 auto sockets = 172 ASSERT_NO_ERRNO_AND_VALUE(IPv4TCPAcceptBindSocketPair(0).Create()); 173 std::vector<TCPEntry> entries = 174 ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCPEntries()); 175 // We can only make assertions about the total number of entries if we control 176 // the entire "machine". 177 if (IsRunningOnGvisor()) { 178 EXPECT_EQ(entries.size(), 2); 179 } 180 181 EXPECT_TRUE(FindByLocalAddr(entries, nullptr, sockets->first_addr())); 182 EXPECT_TRUE(FindByRemoteAddr(entries, nullptr, sockets->first_addr())); 183 } 184 185 TEST(ProcNetTCP, InodeReasonable) { 186 auto sockets = 187 ASSERT_NO_ERRNO_AND_VALUE(IPv4TCPAcceptBindSocketPair(0).Create()); 188 std::vector<TCPEntry> entries = 189 ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCPEntries()); 190 191 TCPEntry accepted_entry; 192 ASSERT_TRUE(FindByLocalAddr(entries, &accepted_entry, sockets->first_addr())); 193 EXPECT_NE(accepted_entry.inode, 0); 194 195 TCPEntry client_entry; 196 ASSERT_TRUE(FindByRemoteAddr(entries, &client_entry, sockets->first_addr())); 197 EXPECT_NE(client_entry.inode, 0); 198 EXPECT_NE(accepted_entry.inode, client_entry.inode); 199 } 200 201 TEST(ProcNetTCP, State) { 202 std::unique_ptr<FileDescriptor> server = 203 ASSERT_NO_ERRNO_AND_VALUE(IPv4TCPUnboundSocket(0).Create()); 204 205 auto test_addr = V4Loopback(); 206 ASSERT_THAT( 207 bind(server->get(), reinterpret_cast<struct sockaddr*>(&test_addr.addr), 208 test_addr.addr_len), 209 SyscallSucceeds()); 210 211 struct sockaddr addr; 212 socklen_t addrlen = sizeof(struct sockaddr); 213 ASSERT_THAT(getsockname(server->get(), &addr, &addrlen), SyscallSucceeds()); 214 ASSERT_EQ(addrlen, sizeof(struct sockaddr)); 215 216 ASSERT_THAT(listen(server->get(), 10), SyscallSucceeds()); 217 std::vector<TCPEntry> entries = 218 ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCPEntries()); 219 TCPEntry listen_entry; 220 ASSERT_TRUE(FindByLocalAddr(entries, &listen_entry, &addr)); 221 EXPECT_EQ(listen_entry.state, TCP_LISTEN); 222 223 std::unique_ptr<FileDescriptor> client = 224 ASSERT_NO_ERRNO_AND_VALUE(IPv4TCPUnboundSocket(0).Create()); 225 ASSERT_THAT(RetryEINTR(connect)(client->get(), &addr, addrlen), 226 SyscallSucceeds()); 227 entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCPEntries()); 228 ASSERT_TRUE(FindByLocalAddr(entries, &listen_entry, &addr)); 229 EXPECT_EQ(listen_entry.state, TCP_LISTEN); 230 TCPEntry client_entry; 231 ASSERT_TRUE(FindByRemoteAddr(entries, &client_entry, &addr)); 232 EXPECT_EQ(client_entry.state, TCP_ESTABLISHED); 233 234 FileDescriptor accepted = 235 ASSERT_NO_ERRNO_AND_VALUE(Accept(server->get(), nullptr, nullptr)); 236 237 const uint32_t accepted_local_host = IPFromInetSockaddr(&addr); 238 const uint16_t accepted_local_port = PortFromInetSockaddr(&addr); 239 240 entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCPEntries()); 241 TCPEntry accepted_entry; 242 ASSERT_TRUE(FindBy(entries, &accepted_entry, 243 [client_entry, accepted_local_host, 244 accepted_local_port](const TCPEntry& e) { 245 return e.local_addr == accepted_local_host && 246 e.local_port == accepted_local_port && 247 e.remote_addr == client_entry.local_addr && 248 e.remote_port == client_entry.local_port; 249 })); 250 EXPECT_EQ(accepted_entry.state, TCP_ESTABLISHED); 251 } 252 253 constexpr char kProcNetTCP6Header[] = 254 " sl local_address remote_address" 255 " st tx_queue rx_queue tr tm->when retrnsmt" 256 " uid timeout inode"; 257 258 // TCP6Entry represents a single entry from /proc/net/tcp6. 259 struct TCP6Entry { 260 struct in6_addr local_addr; 261 uint16_t local_port; 262 263 struct in6_addr remote_addr; 264 uint16_t remote_port; 265 266 uint64_t state; 267 uint64_t uid; 268 uint64_t inode; 269 }; 270 271 bool IPv6AddrEqual(const struct in6_addr* a1, const struct in6_addr* a2) { 272 return memcmp(a1, a2, sizeof(struct in6_addr)) == 0; 273 } 274 275 // Finds the first entry in 'entries' for which 'predicate' returns true. 276 // Returns true on match, and sets 'match' to a copy of the matching entry. If 277 // 'match' is null, it's ignored. 278 bool FindBy6(const std::vector<TCP6Entry>& entries, TCP6Entry* match, 279 std::function<bool(const TCP6Entry&)> predicate) { 280 for (const TCP6Entry& entry : entries) { 281 if (predicate(entry)) { 282 if (match != nullptr) { 283 *match = entry; 284 } 285 return true; 286 } 287 } 288 return false; 289 } 290 291 const struct in6_addr* IP6FromInetSockaddr(const struct sockaddr* addr) { 292 auto* addr6 = reinterpret_cast<const struct sockaddr_in6*>(addr); 293 return &addr6->sin6_addr; 294 } 295 296 bool FindByLocalAddr6(const std::vector<TCP6Entry>& entries, TCP6Entry* match, 297 const struct sockaddr* addr) { 298 const struct in6_addr* local = IP6FromInetSockaddr(addr); 299 uint16_t port = PortFromInetSockaddr(addr); 300 return FindBy6(entries, match, [local, port](const TCP6Entry& e) { 301 return (IPv6AddrEqual(&e.local_addr, local) && e.local_port == port); 302 }); 303 } 304 305 bool FindByRemoteAddr6(const std::vector<TCP6Entry>& entries, TCP6Entry* match, 306 const struct sockaddr* addr) { 307 const struct in6_addr* remote = IP6FromInetSockaddr(addr); 308 uint16_t port = PortFromInetSockaddr(addr); 309 return FindBy6(entries, match, [remote, port](const TCP6Entry& e) { 310 return (IPv6AddrEqual(&e.remote_addr, remote) && e.remote_port == port); 311 }); 312 } 313 314 void ReadIPv6Address(std::string s, struct in6_addr* addr) { 315 uint32_t a0, a1, a2, a3; 316 const char* fmt = "%08X%08X%08X%08X"; 317 EXPECT_EQ(sscanf(s.c_str(), fmt, &a0, &a1, &a2, &a3), 4); 318 319 uint8_t* b = addr->s6_addr; 320 *((uint32_t*)&b[0]) = a0; 321 *((uint32_t*)&b[4]) = a1; 322 *((uint32_t*)&b[8]) = a2; 323 *((uint32_t*)&b[12]) = a3; 324 } 325 326 // Returns a parsed representation of /proc/net/tcp6 entries. 327 PosixErrorOr<std::vector<TCP6Entry>> ProcNetTCP6Entries() { 328 std::string content; 329 RETURN_IF_ERRNO(GetContents("/proc/net/tcp6", &content)); 330 331 bool found_header = false; 332 std::vector<TCP6Entry> entries; 333 std::vector<std::string> lines = StrSplit(content, '\n'); 334 std::cerr << "<contents of /proc/net/tcp6>" << std::endl; 335 for (const std::string& line : lines) { 336 std::cerr << line << std::endl; 337 338 if (!found_header) { 339 EXPECT_EQ(line, kProcNetTCP6Header); 340 found_header = true; 341 continue; 342 } 343 if (line.empty()) { 344 continue; 345 } 346 347 // Parse a single entry from /proc/net/tcp6. 348 // 349 // Example entries: 350 // 351 // clang-format off 352 // 353 // sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode 354 // 0: 00000000000000000000000000000000:1F90 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 876340 1 ffff8803da9c9380 100 0 0 10 0 355 // 1: 00000000000000000000000000000000:C350 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 876987 1 ffff8803ec408000 100 0 0 10 0 356 // ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 357 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 358 // 359 // clang-format on 360 361 TCP6Entry entry; 362 std::vector<std::string> fields = 363 StrSplit(line, absl::ByAnyChar(": "), absl::SkipEmpty()); 364 365 ReadIPv6Address(fields[1], &entry.local_addr); 366 ASSIGN_OR_RETURN_ERRNO(entry.local_port, AtoiBase(fields[2], 16)); 367 ReadIPv6Address(fields[3], &entry.remote_addr); 368 ASSIGN_OR_RETURN_ERRNO(entry.remote_port, AtoiBase(fields[4], 16)); 369 ASSIGN_OR_RETURN_ERRNO(entry.state, AtoiBase(fields[5], 16)); 370 ASSIGN_OR_RETURN_ERRNO(entry.uid, Atoi<uint64_t>(fields[11])); 371 ASSIGN_OR_RETURN_ERRNO(entry.inode, Atoi<uint64_t>(fields[13])); 372 373 entries.push_back(entry); 374 } 375 std::cerr << "<end of /proc/net/tcp6>" << std::endl; 376 377 return entries; 378 } 379 380 TEST(ProcNetTCP6, Exists) { 381 const std::string content = 382 ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/tcp6")); 383 const std::string header_line = StrCat(kProcNetTCP6Header, "\n"); 384 if (IsRunningOnGvisor()) { 385 // Should be just the header since we don't have any tcp sockets yet. 386 EXPECT_EQ(content, header_line); 387 } else { 388 // On a general linux machine, we could have abitrary sockets on the system, 389 // so just check the header. 390 EXPECT_THAT(content, ::testing::StartsWith(header_line)); 391 } 392 } 393 394 TEST(ProcNetTCP6, EntryUID) { 395 auto sockets = 396 ASSERT_NO_ERRNO_AND_VALUE(IPv6TCPAcceptBindSocketPair(0).Create()); 397 std::vector<TCP6Entry> entries = 398 ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCP6Entries()); 399 TCP6Entry e; 400 401 ASSERT_TRUE(FindByLocalAddr6(entries, &e, sockets->first_addr())); 402 EXPECT_EQ(e.uid, geteuid()); 403 ASSERT_TRUE(FindByRemoteAddr6(entries, &e, sockets->first_addr())); 404 EXPECT_EQ(e.uid, geteuid()); 405 } 406 407 TEST(ProcNetTCP6, BindAcceptConnect) { 408 auto sockets = 409 ASSERT_NO_ERRNO_AND_VALUE(IPv6TCPAcceptBindSocketPair(0).Create()); 410 std::vector<TCP6Entry> entries = 411 ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCP6Entries()); 412 // We can only make assertions about the total number of entries if we control 413 // the entire "machine". 414 if (IsRunningOnGvisor()) { 415 EXPECT_EQ(entries.size(), 2); 416 } 417 418 EXPECT_TRUE(FindByLocalAddr6(entries, nullptr, sockets->first_addr())); 419 EXPECT_TRUE(FindByRemoteAddr6(entries, nullptr, sockets->first_addr())); 420 } 421 422 TEST(ProcNetTCP6, InodeReasonable) { 423 auto sockets = 424 ASSERT_NO_ERRNO_AND_VALUE(IPv6TCPAcceptBindSocketPair(0).Create()); 425 std::vector<TCP6Entry> entries = 426 ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCP6Entries()); 427 428 TCP6Entry accepted_entry; 429 430 ASSERT_TRUE( 431 FindByLocalAddr6(entries, &accepted_entry, sockets->first_addr())); 432 EXPECT_NE(accepted_entry.inode, 0); 433 434 TCP6Entry client_entry; 435 ASSERT_TRUE(FindByRemoteAddr6(entries, &client_entry, sockets->first_addr())); 436 EXPECT_NE(client_entry.inode, 0); 437 EXPECT_NE(accepted_entry.inode, client_entry.inode); 438 } 439 440 TEST(ProcNetTCP6, State) { 441 std::unique_ptr<FileDescriptor> server = 442 ASSERT_NO_ERRNO_AND_VALUE(IPv6TCPUnboundSocket(0).Create()); 443 444 auto test_addr = V6Loopback(); 445 ASSERT_THAT( 446 bind(server->get(), reinterpret_cast<struct sockaddr*>(&test_addr.addr), 447 test_addr.addr_len), 448 SyscallSucceeds()); 449 450 struct sockaddr_in6 addr6; 451 socklen_t addrlen = sizeof(struct sockaddr_in6); 452 auto* addr = reinterpret_cast<struct sockaddr*>(&addr6); 453 ASSERT_THAT(getsockname(server->get(), addr, &addrlen), SyscallSucceeds()); 454 ASSERT_EQ(addrlen, sizeof(struct sockaddr_in6)); 455 456 ASSERT_THAT(listen(server->get(), 10), SyscallSucceeds()); 457 std::vector<TCP6Entry> entries = 458 ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCP6Entries()); 459 TCP6Entry listen_entry; 460 461 ASSERT_TRUE(FindByLocalAddr6(entries, &listen_entry, addr)); 462 EXPECT_EQ(listen_entry.state, TCP_LISTEN); 463 464 std::unique_ptr<FileDescriptor> client = 465 ASSERT_NO_ERRNO_AND_VALUE(IPv6TCPUnboundSocket(0).Create()); 466 ASSERT_THAT(RetryEINTR(connect)(client->get(), addr, addrlen), 467 SyscallSucceeds()); 468 entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCP6Entries()); 469 ASSERT_TRUE(FindByLocalAddr6(entries, &listen_entry, addr)); 470 EXPECT_EQ(listen_entry.state, TCP_LISTEN); 471 TCP6Entry client_entry; 472 ASSERT_TRUE(FindByRemoteAddr6(entries, &client_entry, addr)); 473 EXPECT_EQ(client_entry.state, TCP_ESTABLISHED); 474 475 FileDescriptor accepted = 476 ASSERT_NO_ERRNO_AND_VALUE(Accept(server->get(), nullptr, nullptr)); 477 478 const struct in6_addr* local = IP6FromInetSockaddr(addr); 479 const uint16_t accepted_local_port = PortFromInetSockaddr(addr); 480 481 entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCP6Entries()); 482 TCP6Entry accepted_entry; 483 ASSERT_TRUE(FindBy6( 484 entries, &accepted_entry, 485 [client_entry, local, accepted_local_port](const TCP6Entry& e) { 486 return IPv6AddrEqual(&e.local_addr, local) && 487 e.local_port == accepted_local_port && 488 IPv6AddrEqual(&e.remote_addr, &client_entry.local_addr) && 489 e.remote_port == client_entry.local_port; 490 })); 491 EXPECT_EQ(accepted_entry.state, TCP_ESTABLISHED); 492 } 493 494 } // namespace 495 } // namespace testing 496 } // namespace gvisor