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