1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #include <errno.h> 4 #include <fcntl.h> 5 #include <limits.h> 6 #include <sched.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <sys/ioctl.h> 11 #include <sys/socket.h> 12 #include <sys/stat.h> 13 #include <sys/types.h> 14 #include <sys/wait.h> 15 #include <unistd.h> 16 #include <linux/if.h> 17 #include <linux/sockios.h> 18 #include <linux/nsfs.h> 19 #include <arpa/inet.h> 20 #include "../kselftest_harness.h" 21 #include "../filesystems/utils.h" 22 #include "wrappers.h" 23 24 #ifndef SIOCGSKNS 25 #define SIOCGSKNS 0x894C 26 #endif 27 28 #ifndef FD_NSFS_ROOT 29 #define FD_NSFS_ROOT -10003 30 #endif 31 32 #ifndef FILEID_NSFS 33 #define FILEID_NSFS 0xf1 34 #endif 35 36 /* 37 * Test basic SIOCGSKNS functionality. 38 * Create a socket and verify SIOCGSKNS returns the correct network namespace. 39 */ 40 TEST(siocgskns_basic) 41 { 42 int sock_fd, netns_fd, current_netns_fd; 43 struct stat st1, st2; 44 45 /* Create a TCP socket */ 46 sock_fd = socket(AF_INET, SOCK_STREAM, 0); 47 ASSERT_GE(sock_fd, 0); 48 49 /* Use SIOCGSKNS to get network namespace */ 50 netns_fd = ioctl(sock_fd, SIOCGSKNS); 51 if (netns_fd < 0) { 52 close(sock_fd); 53 if (errno == ENOTTY || errno == EINVAL) 54 SKIP(return, "SIOCGSKNS not supported"); 55 ASSERT_GE(netns_fd, 0); 56 } 57 58 /* Get current network namespace */ 59 current_netns_fd = open("/proc/self/ns/net", O_RDONLY); 60 ASSERT_GE(current_netns_fd, 0); 61 62 /* Verify they match */ 63 ASSERT_EQ(fstat(netns_fd, &st1), 0); 64 ASSERT_EQ(fstat(current_netns_fd, &st2), 0); 65 ASSERT_EQ(st1.st_ino, st2.st_ino); 66 67 close(sock_fd); 68 close(netns_fd); 69 close(current_netns_fd); 70 } 71 72 /* 73 * Test that socket file descriptors keep network namespaces active. 74 * Create a network namespace, create a socket in it, then exit the namespace. 75 * The namespace should remain active while the socket FD is held. 76 */ 77 TEST(siocgskns_keeps_netns_active) 78 { 79 int sock_fd, netns_fd, test_fd; 80 int ipc_sockets[2]; 81 pid_t pid; 82 int status; 83 struct stat st; 84 85 EXPECT_EQ(socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0); 86 87 pid = fork(); 88 ASSERT_GE(pid, 0); 89 90 if (pid == 0) { 91 /* Child: create new netns and socket */ 92 close(ipc_sockets[0]); 93 94 if (unshare(CLONE_NEWNET) < 0) { 95 TH_LOG("unshare(CLONE_NEWNET) failed: %s", strerror(errno)); 96 close(ipc_sockets[1]); 97 exit(1); 98 } 99 100 /* Create a socket in the new network namespace */ 101 sock_fd = socket(AF_INET, SOCK_DGRAM, 0); 102 if (sock_fd < 0) { 103 TH_LOG("socket() failed: %s", strerror(errno)); 104 close(ipc_sockets[1]); 105 exit(1); 106 } 107 108 /* Send socket FD to parent via SCM_RIGHTS */ 109 struct msghdr msg = {0}; 110 struct iovec iov = {0}; 111 char buf[1] = {'X'}; 112 char cmsg_buf[CMSG_SPACE(sizeof(int))]; 113 114 iov.iov_base = buf; 115 iov.iov_len = 1; 116 msg.msg_iov = &iov; 117 msg.msg_iovlen = 1; 118 msg.msg_control = cmsg_buf; 119 msg.msg_controllen = sizeof(cmsg_buf); 120 121 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 122 cmsg->cmsg_level = SOL_SOCKET; 123 cmsg->cmsg_type = SCM_RIGHTS; 124 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 125 memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(int)); 126 127 if (sendmsg(ipc_sockets[1], &msg, 0) < 0) { 128 close(sock_fd); 129 close(ipc_sockets[1]); 130 exit(1); 131 } 132 133 close(sock_fd); 134 close(ipc_sockets[1]); 135 exit(0); 136 } 137 138 /* Parent: receive socket FD */ 139 close(ipc_sockets[1]); 140 141 struct msghdr msg = {0}; 142 struct iovec iov = {0}; 143 char buf[1]; 144 char cmsg_buf[CMSG_SPACE(sizeof(int))]; 145 146 iov.iov_base = buf; 147 iov.iov_len = 1; 148 msg.msg_iov = &iov; 149 msg.msg_iovlen = 1; 150 msg.msg_control = cmsg_buf; 151 msg.msg_controllen = sizeof(cmsg_buf); 152 153 ssize_t n = recvmsg(ipc_sockets[0], &msg, 0); 154 close(ipc_sockets[0]); 155 ASSERT_EQ(n, 1); 156 157 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 158 ASSERT_NE(cmsg, NULL); 159 ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS); 160 161 memcpy(&sock_fd, CMSG_DATA(cmsg), sizeof(int)); 162 163 /* Wait for child to exit */ 164 waitpid(pid, &status, 0); 165 ASSERT_TRUE(WIFEXITED(status)); 166 ASSERT_EQ(WEXITSTATUS(status), 0); 167 168 /* Get network namespace from socket */ 169 netns_fd = ioctl(sock_fd, SIOCGSKNS); 170 if (netns_fd < 0) { 171 close(sock_fd); 172 if (errno == ENOTTY || errno == EINVAL) 173 SKIP(return, "SIOCGSKNS not supported"); 174 ASSERT_GE(netns_fd, 0); 175 } 176 177 ASSERT_EQ(fstat(netns_fd, &st), 0); 178 179 /* 180 * Namespace should still be active because socket FD keeps it alive. 181 * Try to access it via /proc/self/fd/<fd>. 182 */ 183 char path[64]; 184 snprintf(path, sizeof(path), "/proc/self/fd/%d", netns_fd); 185 test_fd = open(path, O_RDONLY); 186 ASSERT_GE(test_fd, 0); 187 close(test_fd); 188 close(netns_fd); 189 190 /* Close socket - namespace should become inactive */ 191 close(sock_fd); 192 193 /* Try SIOCGSKNS again - should fail since socket is closed */ 194 ASSERT_LT(ioctl(sock_fd, SIOCGSKNS), 0); 195 } 196 197 /* 198 * Test SIOCGSKNS with different socket types (TCP, UDP, RAW). 199 */ 200 TEST(siocgskns_socket_types) 201 { 202 int sock_tcp, sock_udp, sock_raw; 203 int netns_tcp, netns_udp, netns_raw; 204 struct stat st_tcp, st_udp, st_raw; 205 206 /* TCP socket */ 207 sock_tcp = socket(AF_INET, SOCK_STREAM, 0); 208 ASSERT_GE(sock_tcp, 0); 209 210 /* UDP socket */ 211 sock_udp = socket(AF_INET, SOCK_DGRAM, 0); 212 ASSERT_GE(sock_udp, 0); 213 214 /* RAW socket (may require privileges) */ 215 sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 216 if (sock_raw < 0 && (errno == EPERM || errno == EACCES)) { 217 sock_raw = -1; /* Skip raw socket test */ 218 } 219 220 /* Test SIOCGSKNS on TCP */ 221 netns_tcp = ioctl(sock_tcp, SIOCGSKNS); 222 if (netns_tcp < 0) { 223 close(sock_tcp); 224 close(sock_udp); 225 if (sock_raw >= 0) close(sock_raw); 226 if (errno == ENOTTY || errno == EINVAL) 227 SKIP(return, "SIOCGSKNS not supported"); 228 ASSERT_GE(netns_tcp, 0); 229 } 230 231 /* Test SIOCGSKNS on UDP */ 232 netns_udp = ioctl(sock_udp, SIOCGSKNS); 233 ASSERT_GE(netns_udp, 0); 234 235 /* Test SIOCGSKNS on RAW (if available) */ 236 if (sock_raw >= 0) { 237 netns_raw = ioctl(sock_raw, SIOCGSKNS); 238 ASSERT_GE(netns_raw, 0); 239 } 240 241 /* Verify all return the same network namespace */ 242 ASSERT_EQ(fstat(netns_tcp, &st_tcp), 0); 243 ASSERT_EQ(fstat(netns_udp, &st_udp), 0); 244 ASSERT_EQ(st_tcp.st_ino, st_udp.st_ino); 245 246 if (sock_raw >= 0) { 247 ASSERT_EQ(fstat(netns_raw, &st_raw), 0); 248 ASSERT_EQ(st_tcp.st_ino, st_raw.st_ino); 249 close(netns_raw); 250 close(sock_raw); 251 } 252 253 close(netns_tcp); 254 close(netns_udp); 255 close(sock_tcp); 256 close(sock_udp); 257 } 258 259 /* 260 * Test SIOCGSKNS across setns. 261 * Create a socket in netns A, switch to netns B, verify SIOCGSKNS still 262 * returns netns A. 263 */ 264 TEST(siocgskns_across_setns) 265 { 266 int sock_fd, netns_a_fd, netns_b_fd, result_fd; 267 struct stat st_a; 268 269 /* Get current netns (A) */ 270 netns_a_fd = open("/proc/self/ns/net", O_RDONLY); 271 ASSERT_GE(netns_a_fd, 0); 272 ASSERT_EQ(fstat(netns_a_fd, &st_a), 0); 273 274 /* Create socket in netns A */ 275 sock_fd = socket(AF_INET, SOCK_STREAM, 0); 276 ASSERT_GE(sock_fd, 0); 277 278 /* Create new netns (B) */ 279 ASSERT_EQ(unshare(CLONE_NEWNET), 0); 280 281 netns_b_fd = open("/proc/self/ns/net", O_RDONLY); 282 ASSERT_GE(netns_b_fd, 0); 283 284 /* Get netns from socket created in A */ 285 result_fd = ioctl(sock_fd, SIOCGSKNS); 286 if (result_fd < 0) { 287 close(sock_fd); 288 setns(netns_a_fd, CLONE_NEWNET); 289 close(netns_a_fd); 290 close(netns_b_fd); 291 if (errno == ENOTTY || errno == EINVAL) 292 SKIP(return, "SIOCGSKNS not supported"); 293 ASSERT_GE(result_fd, 0); 294 } 295 296 /* Verify it still points to netns A */ 297 struct stat st_result_stat; 298 ASSERT_EQ(fstat(result_fd, &st_result_stat), 0); 299 ASSERT_EQ(st_a.st_ino, st_result_stat.st_ino); 300 301 close(result_fd); 302 close(sock_fd); 303 close(netns_b_fd); 304 305 /* Restore original netns */ 306 ASSERT_EQ(setns(netns_a_fd, CLONE_NEWNET), 0); 307 close(netns_a_fd); 308 } 309 310 /* 311 * Test SIOCGSKNS fails on non-socket file descriptors. 312 */ 313 TEST(siocgskns_non_socket) 314 { 315 int fd; 316 int pipefd[2]; 317 318 /* Test on regular file */ 319 fd = open("/dev/null", O_RDONLY); 320 ASSERT_GE(fd, 0); 321 322 ASSERT_LT(ioctl(fd, SIOCGSKNS), 0); 323 ASSERT_TRUE(errno == ENOTTY || errno == EINVAL); 324 close(fd); 325 326 /* Test on pipe */ 327 ASSERT_EQ(pipe(pipefd), 0); 328 329 ASSERT_LT(ioctl(pipefd[0], SIOCGSKNS), 0); 330 ASSERT_TRUE(errno == ENOTTY || errno == EINVAL); 331 332 close(pipefd[0]); 333 close(pipefd[1]); 334 } 335 336 /* 337 * Test multiple sockets keep the same network namespace active. 338 * Create multiple sockets, verify closing some doesn't affect others. 339 */ 340 TEST(siocgskns_multiple_sockets) 341 { 342 int socks[5]; 343 int netns_fds[5]; 344 int i; 345 struct stat st; 346 ino_t netns_ino; 347 348 /* Create new network namespace */ 349 ASSERT_EQ(unshare(CLONE_NEWNET), 0); 350 351 /* Create multiple sockets */ 352 for (i = 0; i < 5; i++) { 353 socks[i] = socket(AF_INET, SOCK_STREAM, 0); 354 ASSERT_GE(socks[i], 0); 355 } 356 357 /* Get netns from all sockets */ 358 for (i = 0; i < 5; i++) { 359 netns_fds[i] = ioctl(socks[i], SIOCGSKNS); 360 if (netns_fds[i] < 0) { 361 int j; 362 for (j = 0; j <= i; j++) { 363 close(socks[j]); 364 if (j < i && netns_fds[j] >= 0) 365 close(netns_fds[j]); 366 } 367 if (errno == ENOTTY || errno == EINVAL) 368 SKIP(return, "SIOCGSKNS not supported"); 369 ASSERT_GE(netns_fds[i], 0); 370 } 371 } 372 373 /* Verify all point to same netns */ 374 ASSERT_EQ(fstat(netns_fds[0], &st), 0); 375 netns_ino = st.st_ino; 376 377 for (i = 1; i < 5; i++) { 378 ASSERT_EQ(fstat(netns_fds[i], &st), 0); 379 ASSERT_EQ(st.st_ino, netns_ino); 380 } 381 382 /* Close some sockets */ 383 for (i = 0; i < 3; i++) { 384 close(socks[i]); 385 } 386 387 /* Remaining netns FDs should still be valid */ 388 for (i = 3; i < 5; i++) { 389 char path[64]; 390 snprintf(path, sizeof(path), "/proc/self/fd/%d", netns_fds[i]); 391 int test_fd = open(path, O_RDONLY); 392 ASSERT_GE(test_fd, 0); 393 close(test_fd); 394 } 395 396 /* Cleanup */ 397 for (i = 0; i < 5; i++) { 398 if (i >= 3) 399 close(socks[i]); 400 close(netns_fds[i]); 401 } 402 } 403 404 /* 405 * Test socket keeps netns active after creating process exits. 406 * Verify that as long as the socket FD exists, the namespace remains active. 407 */ 408 TEST(siocgskns_netns_lifecycle) 409 { 410 int sock_fd, netns_fd; 411 int ipc_sockets[2]; 412 int syncpipe[2]; 413 pid_t pid; 414 int status; 415 char sync_byte; 416 struct stat st; 417 ino_t netns_ino; 418 419 EXPECT_EQ(socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0); 420 421 ASSERT_EQ(pipe(syncpipe), 0); 422 423 pid = fork(); 424 ASSERT_GE(pid, 0); 425 426 if (pid == 0) { 427 /* Child */ 428 close(ipc_sockets[0]); 429 close(syncpipe[1]); 430 431 if (unshare(CLONE_NEWNET) < 0) { 432 close(ipc_sockets[1]); 433 close(syncpipe[0]); 434 exit(1); 435 } 436 437 sock_fd = socket(AF_INET, SOCK_STREAM, 0); 438 if (sock_fd < 0) { 439 close(ipc_sockets[1]); 440 close(syncpipe[0]); 441 exit(1); 442 } 443 444 /* Send socket to parent */ 445 struct msghdr msg = {0}; 446 struct iovec iov = {0}; 447 char buf[1] = {'X'}; 448 char cmsg_buf[CMSG_SPACE(sizeof(int))]; 449 450 iov.iov_base = buf; 451 iov.iov_len = 1; 452 msg.msg_iov = &iov; 453 msg.msg_iovlen = 1; 454 msg.msg_control = cmsg_buf; 455 msg.msg_controllen = sizeof(cmsg_buf); 456 457 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 458 cmsg->cmsg_level = SOL_SOCKET; 459 cmsg->cmsg_type = SCM_RIGHTS; 460 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 461 memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(int)); 462 463 if (sendmsg(ipc_sockets[1], &msg, 0) < 0) { 464 close(sock_fd); 465 close(ipc_sockets[1]); 466 close(syncpipe[0]); 467 exit(1); 468 } 469 470 close(sock_fd); 471 close(ipc_sockets[1]); 472 473 /* Wait for parent signal */ 474 read(syncpipe[0], &sync_byte, 1); 475 close(syncpipe[0]); 476 exit(0); 477 } 478 479 /* Parent */ 480 close(ipc_sockets[1]); 481 close(syncpipe[0]); 482 483 /* Receive socket FD */ 484 struct msghdr msg = {0}; 485 struct iovec iov = {0}; 486 char buf[1]; 487 char cmsg_buf[CMSG_SPACE(sizeof(int))]; 488 489 iov.iov_base = buf; 490 iov.iov_len = 1; 491 msg.msg_iov = &iov; 492 msg.msg_iovlen = 1; 493 msg.msg_control = cmsg_buf; 494 msg.msg_controllen = sizeof(cmsg_buf); 495 496 ssize_t n = recvmsg(ipc_sockets[0], &msg, 0); 497 close(ipc_sockets[0]); 498 ASSERT_EQ(n, 1); 499 500 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 501 ASSERT_NE(cmsg, NULL); 502 memcpy(&sock_fd, CMSG_DATA(cmsg), sizeof(int)); 503 504 /* Get netns from socket while child is alive */ 505 netns_fd = ioctl(sock_fd, SIOCGSKNS); 506 if (netns_fd < 0) { 507 sync_byte = 'G'; 508 write(syncpipe[1], &sync_byte, 1); 509 close(syncpipe[1]); 510 close(sock_fd); 511 waitpid(pid, NULL, 0); 512 if (errno == ENOTTY || errno == EINVAL) 513 SKIP(return, "SIOCGSKNS not supported"); 514 ASSERT_GE(netns_fd, 0); 515 } 516 ASSERT_EQ(fstat(netns_fd, &st), 0); 517 netns_ino = st.st_ino; 518 519 /* Signal child to exit */ 520 sync_byte = 'G'; 521 write(syncpipe[1], &sync_byte, 1); 522 close(syncpipe[1]); 523 524 waitpid(pid, &status, 0); 525 ASSERT_TRUE(WIFEXITED(status)); 526 527 /* 528 * Socket FD should still keep namespace active even after 529 * the creating process exited. 530 */ 531 int test_fd = ioctl(sock_fd, SIOCGSKNS); 532 ASSERT_GE(test_fd, 0); 533 534 struct stat st_test; 535 ASSERT_EQ(fstat(test_fd, &st_test), 0); 536 ASSERT_EQ(st_test.st_ino, netns_ino); 537 538 close(test_fd); 539 close(netns_fd); 540 541 /* Close socket - namespace should become inactive */ 542 close(sock_fd); 543 } 544 545 /* 546 * Test IPv6 sockets also work with SIOCGSKNS. 547 */ 548 TEST(siocgskns_ipv6) 549 { 550 int sock_fd, netns_fd, current_netns_fd; 551 struct stat st1, st2; 552 553 /* Create an IPv6 TCP socket */ 554 sock_fd = socket(AF_INET6, SOCK_STREAM, 0); 555 ASSERT_GE(sock_fd, 0); 556 557 /* Use SIOCGSKNS */ 558 netns_fd = ioctl(sock_fd, SIOCGSKNS); 559 if (netns_fd < 0) { 560 close(sock_fd); 561 if (errno == ENOTTY || errno == EINVAL) 562 SKIP(return, "SIOCGSKNS not supported"); 563 ASSERT_GE(netns_fd, 0); 564 } 565 566 /* Verify it matches current namespace */ 567 current_netns_fd = open("/proc/self/ns/net", O_RDONLY); 568 ASSERT_GE(current_netns_fd, 0); 569 570 ASSERT_EQ(fstat(netns_fd, &st1), 0); 571 ASSERT_EQ(fstat(current_netns_fd, &st2), 0); 572 ASSERT_EQ(st1.st_ino, st2.st_ino); 573 574 close(sock_fd); 575 close(netns_fd); 576 close(current_netns_fd); 577 } 578 579 /* 580 * Test that socket-kept netns appears in listns() output. 581 * Verify that a network namespace kept alive by a socket FD appears in 582 * listns() output even after the creating process exits, and that it 583 * disappears when the socket is closed. 584 */ 585 TEST(siocgskns_listns_visibility) 586 { 587 int sock_fd, netns_fd, owner_fd; 588 int ipc_sockets[2]; 589 pid_t pid; 590 int status; 591 __u64 netns_id, owner_id; 592 struct ns_id_req req = { 593 .size = sizeof(req), 594 .spare = 0, 595 .ns_id = 0, 596 .ns_type = CLONE_NEWNET, 597 .spare2 = 0, 598 .user_ns_id = 0, 599 }; 600 __u64 ns_ids[256]; 601 int ret, i; 602 bool found_netns = false; 603 604 EXPECT_EQ(socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0); 605 606 pid = fork(); 607 ASSERT_GE(pid, 0); 608 609 if (pid == 0) { 610 /* Child: create new netns and socket */ 611 close(ipc_sockets[0]); 612 613 if (unshare(CLONE_NEWNET) < 0) { 614 close(ipc_sockets[1]); 615 exit(1); 616 } 617 618 sock_fd = socket(AF_INET, SOCK_DGRAM, 0); 619 if (sock_fd < 0) { 620 close(ipc_sockets[1]); 621 exit(1); 622 } 623 624 /* Send socket FD to parent via SCM_RIGHTS */ 625 struct msghdr msg = {0}; 626 struct iovec iov = {0}; 627 char buf[1] = {'X'}; 628 char cmsg_buf[CMSG_SPACE(sizeof(int))]; 629 630 iov.iov_base = buf; 631 iov.iov_len = 1; 632 msg.msg_iov = &iov; 633 msg.msg_iovlen = 1; 634 msg.msg_control = cmsg_buf; 635 msg.msg_controllen = sizeof(cmsg_buf); 636 637 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 638 cmsg->cmsg_level = SOL_SOCKET; 639 cmsg->cmsg_type = SCM_RIGHTS; 640 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 641 memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(int)); 642 643 if (sendmsg(ipc_sockets[1], &msg, 0) < 0) { 644 close(sock_fd); 645 close(ipc_sockets[1]); 646 exit(1); 647 } 648 649 close(sock_fd); 650 close(ipc_sockets[1]); 651 exit(0); 652 } 653 654 /* Parent: receive socket FD */ 655 close(ipc_sockets[1]); 656 657 struct msghdr msg = {0}; 658 struct iovec iov = {0}; 659 char buf[1]; 660 char cmsg_buf[CMSG_SPACE(sizeof(int))]; 661 662 iov.iov_base = buf; 663 iov.iov_len = 1; 664 msg.msg_iov = &iov; 665 msg.msg_iovlen = 1; 666 msg.msg_control = cmsg_buf; 667 msg.msg_controllen = sizeof(cmsg_buf); 668 669 ssize_t n = recvmsg(ipc_sockets[0], &msg, 0); 670 close(ipc_sockets[0]); 671 ASSERT_EQ(n, 1); 672 673 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 674 ASSERT_NE(cmsg, NULL); 675 memcpy(&sock_fd, CMSG_DATA(cmsg), sizeof(int)); 676 677 /* Wait for child to exit */ 678 waitpid(pid, &status, 0); 679 ASSERT_TRUE(WIFEXITED(status)); 680 ASSERT_EQ(WEXITSTATUS(status), 0); 681 682 /* Get network namespace from socket */ 683 netns_fd = ioctl(sock_fd, SIOCGSKNS); 684 if (netns_fd < 0) { 685 close(sock_fd); 686 if (errno == ENOTTY || errno == EINVAL) 687 SKIP(return, "SIOCGSKNS not supported"); 688 ASSERT_GE(netns_fd, 0); 689 } 690 691 /* Get namespace ID */ 692 ret = ioctl(netns_fd, NS_GET_ID, &netns_id); 693 if (ret < 0) { 694 close(sock_fd); 695 close(netns_fd); 696 if (errno == ENOTTY || errno == EINVAL) 697 SKIP(return, "NS_GET_ID not supported"); 698 ASSERT_EQ(ret, 0); 699 } 700 701 /* Get owner user namespace */ 702 owner_fd = ioctl(netns_fd, NS_GET_USERNS); 703 if (owner_fd < 0) { 704 close(sock_fd); 705 close(netns_fd); 706 if (errno == ENOTTY || errno == EINVAL) 707 SKIP(return, "NS_GET_USERNS not supported"); 708 ASSERT_GE(owner_fd, 0); 709 } 710 711 /* Get owner namespace ID */ 712 ret = ioctl(owner_fd, NS_GET_ID, &owner_id); 713 if (ret < 0) { 714 close(owner_fd); 715 close(sock_fd); 716 close(netns_fd); 717 ASSERT_EQ(ret, 0); 718 } 719 close(owner_fd); 720 721 /* Namespace should appear in listns() output */ 722 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0); 723 if (ret < 0) { 724 close(sock_fd); 725 close(netns_fd); 726 if (errno == ENOSYS) 727 SKIP(return, "listns() not supported"); 728 TH_LOG("listns failed: %s", strerror(errno)); 729 ASSERT_GE(ret, 0); 730 } 731 732 /* Search for our network namespace in the list */ 733 for (i = 0; i < ret; i++) { 734 if (ns_ids[i] == netns_id) { 735 found_netns = true; 736 break; 737 } 738 } 739 740 ASSERT_TRUE(found_netns); 741 TH_LOG("Found netns %llu in listns() output (kept alive by socket)", netns_id); 742 743 /* Now verify with owner filtering */ 744 req.user_ns_id = owner_id; 745 found_netns = false; 746 747 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0); 748 ASSERT_GE(ret, 0); 749 750 for (i = 0; i < ret; i++) { 751 if (ns_ids[i] == netns_id) { 752 found_netns = true; 753 break; 754 } 755 } 756 757 ASSERT_TRUE(found_netns); 758 TH_LOG("Found netns %llu owned by userns %llu", netns_id, owner_id); 759 760 /* Close socket - namespace should become inactive and disappear from listns() */ 761 close(sock_fd); 762 close(netns_fd); 763 764 /* Verify it's no longer in listns() output */ 765 req.user_ns_id = 0; 766 found_netns = false; 767 768 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0); 769 ASSERT_GE(ret, 0); 770 771 for (i = 0; i < ret; i++) { 772 if (ns_ids[i] == netns_id) { 773 found_netns = true; 774 break; 775 } 776 } 777 778 ASSERT_FALSE(found_netns); 779 TH_LOG("Netns %llu correctly disappeared from listns() after socket closed", netns_id); 780 } 781 782 /* 783 * Test that socket-kept netns can be reopened via file handle. 784 * Verify that a network namespace kept alive by a socket FD can be 785 * reopened using file handles even after the creating process exits. 786 */ 787 TEST(siocgskns_file_handle) 788 { 789 int sock_fd, netns_fd, reopened_fd; 790 int ipc_sockets[2]; 791 pid_t pid; 792 int status; 793 struct stat st1, st2; 794 ino_t netns_ino; 795 __u64 netns_id; 796 struct file_handle *handle; 797 struct nsfs_file_handle *nsfs_fh; 798 int ret; 799 800 /* Allocate file_handle structure for nsfs */ 801 handle = malloc(sizeof(struct file_handle) + sizeof(struct nsfs_file_handle)); 802 ASSERT_NE(handle, NULL); 803 handle->handle_bytes = sizeof(struct nsfs_file_handle); 804 handle->handle_type = FILEID_NSFS; 805 806 EXPECT_EQ(socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0); 807 808 pid = fork(); 809 ASSERT_GE(pid, 0); 810 811 if (pid == 0) { 812 /* Child: create new netns and socket */ 813 close(ipc_sockets[0]); 814 815 if (unshare(CLONE_NEWNET) < 0) { 816 close(ipc_sockets[1]); 817 exit(1); 818 } 819 820 sock_fd = socket(AF_INET, SOCK_DGRAM, 0); 821 if (sock_fd < 0) { 822 close(ipc_sockets[1]); 823 exit(1); 824 } 825 826 /* Send socket FD to parent via SCM_RIGHTS */ 827 struct msghdr msg = {0}; 828 struct iovec iov = {0}; 829 char buf[1] = {'X'}; 830 char cmsg_buf[CMSG_SPACE(sizeof(int))]; 831 832 iov.iov_base = buf; 833 iov.iov_len = 1; 834 msg.msg_iov = &iov; 835 msg.msg_iovlen = 1; 836 msg.msg_control = cmsg_buf; 837 msg.msg_controllen = sizeof(cmsg_buf); 838 839 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 840 cmsg->cmsg_level = SOL_SOCKET; 841 cmsg->cmsg_type = SCM_RIGHTS; 842 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 843 memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(int)); 844 845 if (sendmsg(ipc_sockets[1], &msg, 0) < 0) { 846 close(sock_fd); 847 close(ipc_sockets[1]); 848 exit(1); 849 } 850 851 close(sock_fd); 852 close(ipc_sockets[1]); 853 exit(0); 854 } 855 856 /* Parent: receive socket FD */ 857 close(ipc_sockets[1]); 858 859 struct msghdr msg = {0}; 860 struct iovec iov = {0}; 861 char buf[1]; 862 char cmsg_buf[CMSG_SPACE(sizeof(int))]; 863 864 iov.iov_base = buf; 865 iov.iov_len = 1; 866 msg.msg_iov = &iov; 867 msg.msg_iovlen = 1; 868 msg.msg_control = cmsg_buf; 869 msg.msg_controllen = sizeof(cmsg_buf); 870 871 ssize_t n = recvmsg(ipc_sockets[0], &msg, 0); 872 close(ipc_sockets[0]); 873 ASSERT_EQ(n, 1); 874 875 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 876 ASSERT_NE(cmsg, NULL); 877 memcpy(&sock_fd, CMSG_DATA(cmsg), sizeof(int)); 878 879 /* Wait for child to exit */ 880 waitpid(pid, &status, 0); 881 ASSERT_TRUE(WIFEXITED(status)); 882 ASSERT_EQ(WEXITSTATUS(status), 0); 883 884 /* Get network namespace from socket */ 885 netns_fd = ioctl(sock_fd, SIOCGSKNS); 886 if (netns_fd < 0) { 887 free(handle); 888 close(sock_fd); 889 if (errno == ENOTTY || errno == EINVAL) 890 SKIP(return, "SIOCGSKNS not supported"); 891 ASSERT_GE(netns_fd, 0); 892 } 893 894 ASSERT_EQ(fstat(netns_fd, &st1), 0); 895 netns_ino = st1.st_ino; 896 897 /* Get namespace ID */ 898 ret = ioctl(netns_fd, NS_GET_ID, &netns_id); 899 if (ret < 0) { 900 free(handle); 901 close(sock_fd); 902 close(netns_fd); 903 if (errno == ENOTTY || errno == EINVAL) 904 SKIP(return, "NS_GET_ID not supported"); 905 ASSERT_EQ(ret, 0); 906 } 907 908 /* Construct file handle from namespace ID */ 909 nsfs_fh = (struct nsfs_file_handle *)handle->f_handle; 910 nsfs_fh->ns_id = netns_id; 911 nsfs_fh->ns_type = 0; /* Type field not needed for reopening */ 912 nsfs_fh->ns_inum = 0; /* Inum field not needed for reopening */ 913 914 TH_LOG("Constructed file handle for netns %lu (id=%llu)", netns_ino, netns_id); 915 916 /* Reopen namespace using file handle (while socket still keeps it alive) */ 917 reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); 918 if (reopened_fd < 0) { 919 free(handle); 920 close(sock_fd); 921 if (errno == EOPNOTSUPP || errno == ENOSYS || errno == EBADF) 922 SKIP(return, "open_by_handle_at with FD_NSFS_ROOT not supported"); 923 TH_LOG("open_by_handle_at failed: %s", strerror(errno)); 924 ASSERT_GE(reopened_fd, 0); 925 } 926 927 /* Verify it's the same namespace */ 928 ASSERT_EQ(fstat(reopened_fd, &st2), 0); 929 ASSERT_EQ(st1.st_ino, st2.st_ino); 930 ASSERT_EQ(st1.st_dev, st2.st_dev); 931 932 TH_LOG("Successfully reopened netns %lu via file handle", netns_ino); 933 934 close(reopened_fd); 935 936 /* Close the netns FD */ 937 close(netns_fd); 938 939 /* Try to reopen via file handle - should fail since namespace is now inactive */ 940 reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); 941 ASSERT_LT(reopened_fd, 0); 942 TH_LOG("Correctly failed to reopen inactive netns: %s", strerror(errno)); 943 944 /* Get network namespace from socket */ 945 netns_fd = ioctl(sock_fd, SIOCGSKNS); 946 if (netns_fd < 0) { 947 free(handle); 948 close(sock_fd); 949 if (errno == ENOTTY || errno == EINVAL) 950 SKIP(return, "SIOCGSKNS not supported"); 951 ASSERT_GE(netns_fd, 0); 952 } 953 954 /* Reopen namespace using file handle (while socket still keeps it alive) */ 955 reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY); 956 if (reopened_fd < 0) { 957 free(handle); 958 close(sock_fd); 959 if (errno == EOPNOTSUPP || errno == ENOSYS || errno == EBADF) 960 SKIP(return, "open_by_handle_at with FD_NSFS_ROOT not supported"); 961 TH_LOG("open_by_handle_at failed: %s", strerror(errno)); 962 ASSERT_GE(reopened_fd, 0); 963 } 964 965 /* Verify it's the same namespace */ 966 ASSERT_EQ(fstat(reopened_fd, &st2), 0); 967 ASSERT_EQ(st1.st_ino, st2.st_ino); 968 ASSERT_EQ(st1.st_dev, st2.st_dev); 969 970 TH_LOG("Successfully reopened netns %lu via file handle", netns_ino); 971 972 /* Close socket - namespace should become inactive */ 973 close(sock_fd); 974 free(handle); 975 } 976 977 TEST_HARNESS_MAIN 978