File Index
Files and directories
Sign in
FILE

siocgskns_test.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/namespaces/siocgskns_test.c C
   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 */
  40TEST(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 */
  77TEST(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 */
 200TEST(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 */
 264TEST(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 */
 313TEST(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 */
 340TEST(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 */
 408TEST(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 */
 548TEST(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 */
 585TEST(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 */
 787TEST(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/*
 978 * Test combined listns() and file handle operations with socket-kept netns.
 979 * Create a netns, keep it alive with a socket, verify it appears in listns(),
 980 * then reopen it via file handle obtained from listns() entry.
 981 */
 982TEST(siocgskns_listns_and_file_handle)
 983{
 984	int sock_fd, netns_fd, userns_fd, reopened_fd;
 985	int ipc_sockets[2];
 986	pid_t pid;
 987	int status;
 988	struct stat st;
 989	ino_t netns_ino;
 990	__u64 netns_id, userns_id;
 991	struct ns_id_req req = {
 992		.size = sizeof(req),
 993		.spare = 0,
 994		.ns_id = 0,
 995		.ns_type = CLONE_NEWNET | CLONE_NEWUSER,
 996		.spare2 = 0,
 997		.user_ns_id = 0,
 998	};
 999	__u64 ns_ids[256];
1000	int ret, i;
1001	bool found_netns = false, found_userns = false;
1002	struct file_handle *handle;
1003	struct nsfs_file_handle *nsfs_fh;
1004
1005	/* Allocate file_handle structure for nsfs */
1006	handle = malloc(sizeof(struct file_handle) + sizeof(struct nsfs_file_handle));
1007	ASSERT_NE(handle, NULL);
1008	handle->handle_bytes = sizeof(struct nsfs_file_handle);
1009	handle->handle_type = FILEID_NSFS;
1010
1011	EXPECT_EQ(socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0);
1012
1013	pid = fork();
1014	ASSERT_GE(pid, 0);
1015
1016	if (pid == 0) {
1017		/* Child: create new userns and netns with socket */
1018		close(ipc_sockets[0]);
1019
1020		if (setup_userns() < 0) {
1021			close(ipc_sockets[1]);
1022			exit(1);
1023		}
1024
1025		if (unshare(CLONE_NEWNET) < 0) {
1026			close(ipc_sockets[1]);
1027			exit(1);
1028		}
1029
1030		sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
1031		if (sock_fd < 0) {
1032			close(ipc_sockets[1]);
1033			exit(1);
1034		}
1035
1036		/* Send socket FD to parent via SCM_RIGHTS */
1037		struct msghdr msg = {0};
1038		struct iovec iov = {0};
1039		char buf[1] = {'X'};
1040		char cmsg_buf[CMSG_SPACE(sizeof(int))];
1041
1042		iov.iov_base = buf;
1043		iov.iov_len = 1;
1044		msg.msg_iov = &iov;
1045		msg.msg_iovlen = 1;
1046		msg.msg_control = cmsg_buf;
1047		msg.msg_controllen = sizeof(cmsg_buf);
1048
1049		struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
1050		cmsg->cmsg_level = SOL_SOCKET;
1051		cmsg->cmsg_type = SCM_RIGHTS;
1052		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
1053		memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(int));
1054
1055		if (sendmsg(ipc_sockets[1], &msg, 0) < 0) {
1056			close(sock_fd);
1057			close(ipc_sockets[1]);
1058			exit(1);
1059		}
1060
1061		close(sock_fd);
1062		close(ipc_sockets[1]);
1063		exit(0);
1064	}
1065
1066	/* Parent: receive socket FD */
1067	close(ipc_sockets[1]);
1068
1069	struct msghdr msg = {0};
1070	struct iovec iov = {0};
1071	char buf[1];
1072	char cmsg_buf[CMSG_SPACE(sizeof(int))];
1073
1074	iov.iov_base = buf;
1075	iov.iov_len = 1;
1076	msg.msg_iov = &iov;
1077	msg.msg_iovlen = 1;
1078	msg.msg_control = cmsg_buf;
1079	msg.msg_controllen = sizeof(cmsg_buf);
1080
1081	ssize_t n = recvmsg(ipc_sockets[0], &msg, 0);
1082	close(ipc_sockets[0]);
1083	ASSERT_EQ(n, 1);
1084
1085	struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
1086	ASSERT_NE(cmsg, NULL);
1087	memcpy(&sock_fd, CMSG_DATA(cmsg), sizeof(int));
1088
1089	/* Wait for child to exit */
1090	waitpid(pid, &status, 0);
1091	ASSERT_TRUE(WIFEXITED(status));
1092	ASSERT_EQ(WEXITSTATUS(status), 0);
1093
1094	/* Get network namespace from socket */
1095	netns_fd = ioctl(sock_fd, SIOCGSKNS);
1096	if (netns_fd < 0) {
1097		free(handle);
1098		close(sock_fd);
1099		if (errno == ENOTTY || errno == EINVAL)
1100			SKIP(return, "SIOCGSKNS not supported");
1101		ASSERT_GE(netns_fd, 0);
1102	}
1103
1104	ASSERT_EQ(fstat(netns_fd, &st), 0);
1105	netns_ino = st.st_ino;
1106
1107	/* Get namespace ID */
1108	ret = ioctl(netns_fd, NS_GET_ID, &netns_id);
1109	if (ret < 0) {
1110		free(handle);
1111		close(sock_fd);
1112		close(netns_fd);
1113		if (errno == ENOTTY || errno == EINVAL)
1114			SKIP(return, "NS_GET_ID not supported");
1115		ASSERT_EQ(ret, 0);
1116	}
1117
1118	/* Get owner user namespace */
1119	userns_fd = ioctl(netns_fd, NS_GET_USERNS);
1120	if (userns_fd < 0) {
1121		free(handle);
1122		close(sock_fd);
1123		close(netns_fd);
1124		if (errno == ENOTTY || errno == EINVAL)
1125			SKIP(return, "NS_GET_USERNS not supported");
1126		ASSERT_GE(userns_fd, 0);
1127	}
1128
1129	/* Get owner namespace ID */
1130	ret = ioctl(userns_fd, NS_GET_ID, &userns_id);
1131	if (ret < 0) {
1132		close(userns_fd);
1133		free(handle);
1134		close(sock_fd);
1135		close(netns_fd);
1136		ASSERT_EQ(ret, 0);
1137	}
1138	close(userns_fd);
1139
1140	TH_LOG("Testing netns %lu (id=%llu) owned by userns id=%llu", netns_ino, netns_id, userns_id);
1141
1142	/* Verify namespace appears in listns() */
1143	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
1144	if (ret < 0) {
1145		free(handle);
1146		close(sock_fd);
1147		close(netns_fd);
1148		if (errno == ENOSYS)
1149			SKIP(return, "listns() not supported");
1150		TH_LOG("listns failed: %s", strerror(errno));
1151		ASSERT_GE(ret, 0);
1152	}
1153
1154	found_netns = false;
1155	found_userns = false;
1156	for (i = 0; i < ret; i++) {
1157		if (ns_ids[i] == netns_id)
1158			found_netns = true;
1159		if (ns_ids[i] == userns_id)
1160			found_userns = true;
1161	}
1162	ASSERT_TRUE(found_netns);
1163	ASSERT_TRUE(found_userns);
1164	TH_LOG("Found netns %llu in listns() output", netns_id);
1165
1166	/* Construct file handle from namespace ID */
1167	nsfs_fh = (struct nsfs_file_handle *)handle->f_handle;
1168	nsfs_fh->ns_id = netns_id;
1169	nsfs_fh->ns_type = 0;
1170	nsfs_fh->ns_inum = 0;
1171
1172	reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1173	if (reopened_fd < 0) {
1174		free(handle);
1175		close(sock_fd);
1176		if (errno == EOPNOTSUPP || errno == ENOSYS || errno == EBADF)
1177			SKIP(return, "open_by_handle_at with FD_NSFS_ROOT not supported");
1178		TH_LOG("open_by_handle_at failed: %s", strerror(errno));
1179		ASSERT_GE(reopened_fd, 0);
1180	}
1181
1182	struct stat reopened_st;
1183	ASSERT_EQ(fstat(reopened_fd, &reopened_st), 0);
1184	ASSERT_EQ(reopened_st.st_ino, netns_ino);
1185
1186	TH_LOG("Successfully reopened netns %lu via file handle (socket-kept)", netns_ino);
1187
1188	close(reopened_fd);
1189	close(netns_fd);
1190
1191	/* Try to reopen via file handle - should fail since namespace is now inactive */
1192	reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1193	ASSERT_LT(reopened_fd, 0);
1194	TH_LOG("Correctly failed to reopen inactive netns: %s", strerror(errno));
1195
1196	/* Get network namespace from socket */
1197	netns_fd = ioctl(sock_fd, SIOCGSKNS);
1198	if (netns_fd < 0) {
1199		free(handle);
1200		close(sock_fd);
1201		if (errno == ENOTTY || errno == EINVAL)
1202			SKIP(return, "SIOCGSKNS not supported");
1203		ASSERT_GE(netns_fd, 0);
1204	}
1205
1206	/* Verify namespace appears in listns() */
1207	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
1208	if (ret < 0) {
1209		free(handle);
1210		close(sock_fd);
1211		close(netns_fd);
1212		if (errno == ENOSYS)
1213			SKIP(return, "listns() not supported");
1214		TH_LOG("listns failed: %s", strerror(errno));
1215		ASSERT_GE(ret, 0);
1216	}
1217
1218	found_netns = false;
1219	found_userns = false;
1220	for (i = 0; i < ret; i++) {
1221		if (ns_ids[i] == netns_id)
1222			found_netns = true;
1223		if (ns_ids[i] == userns_id)
1224			found_userns = true;
1225	}
1226	ASSERT_TRUE(found_netns);
1227	ASSERT_TRUE(found_userns);
1228	TH_LOG("Found netns %llu in listns() output", netns_id);
1229
1230	close(netns_fd);
1231
1232	/* Verify namespace appears in listns() */
1233	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
1234	if (ret < 0) {
1235		free(handle);
1236		close(sock_fd);
1237		close(netns_fd);
1238		if (errno == ENOSYS)
1239			SKIP(return, "listns() not supported");
1240		TH_LOG("listns failed: %s", strerror(errno));
1241		ASSERT_GE(ret, 0);
1242	}
1243
1244	found_netns = false;
1245	found_userns = false;
1246	for (i = 0; i < ret; i++) {
1247		if (ns_ids[i] == netns_id)
1248			found_netns = true;
1249		if (ns_ids[i] == userns_id)
1250			found_userns = true;
1251	}
1252	ASSERT_FALSE(found_netns);
1253	ASSERT_FALSE(found_userns);
1254	TH_LOG("Netns %llu correctly disappeared from listns() after socket closed", netns_id);
1255
1256	close(sock_fd);
1257	free(handle);
1258}
1259
1260/*
1261 * Test multi-level namespace resurrection across three user namespace levels.
1262 *
1263 * This test creates a complex namespace hierarchy with three levels of user
1264 * namespaces and a network namespace at the deepest level. It verifies that
1265 * the resurrection semantics work correctly when SIOCGSKNS is called on a
1266 * socket from an inactive namespace tree, and that listns() and
1267 * open_by_handle_at() correctly respect visibility rules.
1268 *
1269 * Hierarchy after child processes exit (all with 0 active refcount):
1270 *
1271 *          net_L3A (0)                <- Level 3 network namespace
1272 *              |
1273 *              +
1274 *          userns_L3 (0)              <- Level 3 user namespace
1275 *              |
1276 *              +
1277 *          userns_L2 (0)              <- Level 2 user namespace
1278 *              |
1279 *              +
1280 *          userns_L1 (0)              <- Level 1 user namespace
1281 *              |
1282 *              x
1283 *          init_user_ns
1284 *
1285 * The test verifies:
1286 * 1. SIOCGSKNS on a socket from inactive net_L3A resurrects the entire chain
1287 * 2. After resurrection, all namespaces are visible in listns()
1288 * 3. Resurrected namespaces can be reopened via file handles
1289 * 4. Closing the netns FD cascades down: the entire ownership chain
1290 *    (userns_L3 -> userns_L2 -> userns_L1) becomes inactive again
1291 * 5. Inactive namespaces disappear from listns() and cannot be reopened
1292 * 6. Calling SIOCGSKNS again on the same socket resurrects the tree again
1293 * 7. After second resurrection, namespaces are visible and can be reopened
1294 */
1295TEST(siocgskns_multilevel_resurrection)
1296{
1297	int ipc_sockets[2];
1298	pid_t pid_l1, pid_l2, pid_l3;
1299	int status;
1300
1301	/* Namespace file descriptors to be received from child */
1302	int sock_L3A_fd = -1;
1303	int netns_L3A_fd = -1;
1304	__u64 netns_L3A_id;
1305	__u64 userns_L1_id, userns_L2_id, userns_L3_id;
1306
1307	/* For listns() and file handle testing */
1308	struct ns_id_req req = {
1309		.size = sizeof(req),
1310		.spare = 0,
1311		.ns_id = 0,
1312		.ns_type = CLONE_NEWNET | CLONE_NEWUSER,
1313		.spare2 = 0,
1314		.user_ns_id = 0,
1315	};
1316	__u64 ns_ids[256];
1317	int ret, i;
1318	struct file_handle *handle;
1319	struct nsfs_file_handle *nsfs_fh;
1320	int reopened_fd;
1321
1322	/* Allocate file handle for testing */
1323	handle = malloc(sizeof(struct file_handle) + sizeof(struct nsfs_file_handle));
1324	ASSERT_NE(handle, NULL);
1325	handle->handle_bytes = sizeof(struct nsfs_file_handle);
1326	handle->handle_type = FILEID_NSFS;
1327
1328	EXPECT_EQ(socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0);
1329
1330	/*
1331	 * Fork level 1 child that creates userns_L1
1332	 */
1333	pid_l1 = fork();
1334	ASSERT_GE(pid_l1, 0);
1335
1336	if (pid_l1 == 0) {
1337		/* Level 1 child */
1338		int ipc_L2[2];
1339		close(ipc_sockets[0]);
1340
1341		/* Create userns_L1 */
1342		if (setup_userns() < 0) {
1343			close(ipc_sockets[1]);
1344			exit(1);
1345		}
1346
1347		/* Create socketpair for communicating with L2 child */
1348		if (socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_L2) < 0) {
1349			close(ipc_sockets[1]);
1350			exit(1);
1351		}
1352
1353		/*
1354		 * Fork level 2 child that creates userns_L2
1355		 */
1356		pid_l2 = fork();
1357		if (pid_l2 < 0) {
1358			close(ipc_sockets[1]);
1359			close(ipc_L2[0]);
1360			close(ipc_L2[1]);
1361			exit(1);
1362		}
1363
1364		if (pid_l2 == 0) {
1365			/* Level 2 child */
1366			int ipc_L3[2];
1367			close(ipc_L2[0]);
1368
1369			/* Create userns_L2 (nested inside userns_L1) */
1370			if (setup_userns() < 0) {
1371				close(ipc_L2[1]);
1372				exit(1);
1373			}
1374
1375			/* Create socketpair for communicating with L3 child */
1376			if (socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_L3) < 0) {
1377				close(ipc_L2[1]);
1378				exit(1);
1379			}
1380
1381			/*
1382			 * Fork level 3 child that creates userns_L3 and network namespaces
1383			 */
1384			pid_l3 = fork();
1385			if (pid_l3 < 0) {
1386				close(ipc_L2[1]);
1387				close(ipc_L3[0]);
1388				close(ipc_L3[1]);
1389				exit(1);
1390			}
1391
1392			if (pid_l3 == 0) {
1393				/* Level 3 child - the deepest level */
1394				int sock_fd;
1395				close(ipc_L3[0]);
1396
1397				/* Create userns_L3 (nested inside userns_L2) */
1398				if (setup_userns() < 0) {
1399					close(ipc_L3[1]);
1400					exit(1);
1401				}
1402
1403				/* Create network namespace at level 3 */
1404				if (unshare(CLONE_NEWNET) < 0) {
1405					close(ipc_L3[1]);
1406					exit(1);
1407				}
1408
1409				/* Create socket in net_L3A */
1410				sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
1411				if (sock_fd < 0) {
1412					close(ipc_L3[1]);
1413					exit(1);
1414				}
1415
1416				/* Send socket FD to L2 parent */
1417				struct msghdr msg = {0};
1418				struct iovec iov = {0};
1419				char buf[1] = {'X'};
1420				char cmsg_buf[CMSG_SPACE(sizeof(int))];
1421
1422				iov.iov_base = buf;
1423				iov.iov_len = 1;
1424				msg.msg_iov = &iov;
1425				msg.msg_iovlen = 1;
1426				msg.msg_control = cmsg_buf;
1427				msg.msg_controllen = sizeof(cmsg_buf);
1428
1429				struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
1430				cmsg->cmsg_level = SOL_SOCKET;
1431				cmsg->cmsg_type = SCM_RIGHTS;
1432				cmsg->cmsg_len = CMSG_LEN(sizeof(int));
1433				memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(int));
1434
1435				if (sendmsg(ipc_L3[1], &msg, 0) < 0) {
1436					close(sock_fd);
1437					close(ipc_L3[1]);
1438					exit(1);
1439				}
1440
1441				close(sock_fd);
1442				close(ipc_L3[1]);
1443				exit(0);
1444			}
1445
1446			/* Level 2 child - receive from L3 and forward to L1 */
1447			close(ipc_L3[1]);
1448
1449			struct msghdr msg = {0};
1450			struct iovec iov = {0};
1451			char buf[1];
1452			char cmsg_buf[CMSG_SPACE(sizeof(int))];
1453			int received_fd;
1454
1455			iov.iov_base = buf;
1456			iov.iov_len = 1;
1457			msg.msg_iov = &iov;
1458			msg.msg_iovlen = 1;
1459			msg.msg_control = cmsg_buf;
1460			msg.msg_controllen = sizeof(cmsg_buf);
1461
1462			ssize_t n = recvmsg(ipc_L3[0], &msg, 0);
1463			close(ipc_L3[0]);
1464
1465			if (n != 1) {
1466				close(ipc_L2[1]);
1467				waitpid(pid_l3, NULL, 0);
1468				exit(1);
1469			}
1470
1471			struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
1472			if (!cmsg) {
1473				close(ipc_L2[1]);
1474				waitpid(pid_l3, NULL, 0);
1475				exit(1);
1476			}
1477			memcpy(&received_fd, CMSG_DATA(cmsg), sizeof(int));
1478
1479			/* Wait for L3 child */
1480			waitpid(pid_l3, NULL, 0);
1481
1482			/* Forward the socket FD to L1 parent */
1483			memset(&msg, 0, sizeof(msg));
1484			buf[0] = 'Y';
1485			iov.iov_base = buf;
1486			iov.iov_len = 1;
1487			msg.msg_iov = &iov;
1488			msg.msg_iovlen = 1;
1489			msg.msg_control = cmsg_buf;
1490			msg.msg_controllen = sizeof(cmsg_buf);
1491
1492			cmsg = CMSG_FIRSTHDR(&msg);
1493			cmsg->cmsg_level = SOL_SOCKET;
1494			cmsg->cmsg_type = SCM_RIGHTS;
1495			cmsg->cmsg_len = CMSG_LEN(sizeof(int));
1496			memcpy(CMSG_DATA(cmsg), &received_fd, sizeof(int));
1497
1498			if (sendmsg(ipc_L2[1], &msg, 0) < 0) {
1499				close(received_fd);
1500				close(ipc_L2[1]);
1501				exit(1);
1502			}
1503
1504			close(received_fd);
1505			close(ipc_L2[1]);
1506			exit(0);
1507		}
1508
1509		/* Level 1 child - receive from L2 and forward to parent */
1510		close(ipc_L2[1]);
1511
1512		struct msghdr msg = {0};
1513		struct iovec iov = {0};
1514		char buf[1];
1515		char cmsg_buf[CMSG_SPACE(sizeof(int))];
1516		int received_fd;
1517
1518		iov.iov_base = buf;
1519		iov.iov_len = 1;
1520		msg.msg_iov = &iov;
1521		msg.msg_iovlen = 1;
1522		msg.msg_control = cmsg_buf;
1523		msg.msg_controllen = sizeof(cmsg_buf);
1524
1525		ssize_t n = recvmsg(ipc_L2[0], &msg, 0);
1526		close(ipc_L2[0]);
1527
1528		if (n != 1) {
1529			close(ipc_sockets[1]);
1530			waitpid(pid_l2, NULL, 0);
1531			exit(1);
1532		}
1533
1534		struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
1535		if (!cmsg) {
1536			close(ipc_sockets[1]);
1537			waitpid(pid_l2, NULL, 0);
1538			exit(1);
1539		}
1540		memcpy(&received_fd, CMSG_DATA(cmsg), sizeof(int));
1541
1542		/* Wait for L2 child */
1543		waitpid(pid_l2, NULL, 0);
1544
1545		/* Forward the socket FD to parent */
1546		memset(&msg, 0, sizeof(msg));
1547		buf[0] = 'Z';
1548		iov.iov_base = buf;
1549		iov.iov_len = 1;
1550		msg.msg_iov = &iov;
1551		msg.msg_iovlen = 1;
1552		msg.msg_control = cmsg_buf;
1553		msg.msg_controllen = sizeof(cmsg_buf);
1554
1555		cmsg = CMSG_FIRSTHDR(&msg);
1556		cmsg->cmsg_level = SOL_SOCKET;
1557		cmsg->cmsg_type = SCM_RIGHTS;
1558		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
1559		memcpy(CMSG_DATA(cmsg), &received_fd, sizeof(int));
1560
1561		if (sendmsg(ipc_sockets[1], &msg, 0) < 0) {
1562			close(received_fd);
1563			close(ipc_sockets[1]);
1564			exit(1);
1565		}
1566
1567		close(received_fd);
1568		close(ipc_sockets[1]);
1569		exit(0);
1570	}
1571
1572	/* Parent - receive the socket from the deepest level */
1573	close(ipc_sockets[1]);
1574
1575	struct msghdr msg = {0};
1576	struct iovec iov = {0};
1577	char buf[1];
1578	char cmsg_buf[CMSG_SPACE(sizeof(int))];
1579
1580	iov.iov_base = buf;
1581	iov.iov_len = 1;
1582	msg.msg_iov = &iov;
1583	msg.msg_iovlen = 1;
1584	msg.msg_control = cmsg_buf;
1585	msg.msg_controllen = sizeof(cmsg_buf);
1586
1587	ssize_t n = recvmsg(ipc_sockets[0], &msg, 0);
1588	close(ipc_sockets[0]);
1589
1590	if (n != 1) {
1591		free(handle);
1592		waitpid(pid_l1, NULL, 0);
1593		SKIP(return, "Failed to receive socket from child");
1594	}
1595
1596	struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
1597	if (!cmsg) {
1598		free(handle);
1599		waitpid(pid_l1, NULL, 0);
1600		SKIP(return, "Failed to receive socket from child");
1601	}
1602	memcpy(&sock_L3A_fd, CMSG_DATA(cmsg), sizeof(int));
1603
1604	/* Wait for L1 child */
1605	waitpid(pid_l1, &status, 0);
1606	ASSERT_TRUE(WIFEXITED(status));
1607	ASSERT_EQ(WEXITSTATUS(status), 0);
1608
1609	/*
1610	 * At this point, all child processes have exited. The socket itself
1611	 * doesn't keep the namespace active - we need to call SIOCGSKNS which
1612	 * will resurrect the entire namespace tree by taking active references.
1613	 */
1614
1615	/* Get network namespace from socket - this resurrects the tree */
1616	netns_L3A_fd = ioctl(sock_L3A_fd, SIOCGSKNS);
1617	if (netns_L3A_fd < 0) {
1618		free(handle);
1619		close(sock_L3A_fd);
1620		if (errno == ENOTTY || errno == EINVAL)
1621			SKIP(return, "SIOCGSKNS not supported");
1622		ASSERT_GE(netns_L3A_fd, 0);
1623	}
1624
1625	/* Get namespace ID for net_L3A */
1626	ret = ioctl(netns_L3A_fd, NS_GET_ID, &netns_L3A_id);
1627	if (ret < 0) {
1628		free(handle);
1629		close(sock_L3A_fd);
1630		close(netns_L3A_fd);
1631		if (errno == ENOTTY || errno == EINVAL)
1632			SKIP(return, "NS_GET_ID not supported");
1633		ASSERT_EQ(ret, 0);
1634	}
1635
1636	/* Get owner user namespace chain: userns_L3 -> userns_L2 -> userns_L1 */
1637	int userns_L3_fd = ioctl(netns_L3A_fd, NS_GET_USERNS);
1638	if (userns_L3_fd < 0) {
1639		free(handle);
1640		close(sock_L3A_fd);
1641		close(netns_L3A_fd);
1642		if (errno == ENOTTY || errno == EINVAL)
1643			SKIP(return, "NS_GET_USERNS not supported");
1644		ASSERT_GE(userns_L3_fd, 0);
1645	}
1646
1647	ret = ioctl(userns_L3_fd, NS_GET_ID, &userns_L3_id);
1648	ASSERT_EQ(ret, 0);
1649
1650	int userns_L2_fd = ioctl(userns_L3_fd, NS_GET_USERNS);
1651	ASSERT_GE(userns_L2_fd, 0);
1652	ret = ioctl(userns_L2_fd, NS_GET_ID, &userns_L2_id);
1653	ASSERT_EQ(ret, 0);
1654
1655	int userns_L1_fd = ioctl(userns_L2_fd, NS_GET_USERNS);
1656	ASSERT_GE(userns_L1_fd, 0);
1657	ret = ioctl(userns_L1_fd, NS_GET_ID, &userns_L1_id);
1658	ASSERT_EQ(ret, 0);
1659
1660	close(userns_L1_fd);
1661	close(userns_L2_fd);
1662	close(userns_L3_fd);
1663
1664	TH_LOG("Multi-level hierarchy: net_L3A (id=%llu) -> userns_L3 (id=%llu) -> userns_L2 (id=%llu) -> userns_L1 (id=%llu)",
1665	       netns_L3A_id, userns_L3_id, userns_L2_id, userns_L1_id);
1666
1667	/*
1668	 * Test 1: Verify net_L3A is visible in listns() after resurrection.
1669	 * The entire ownership chain should be resurrected and visible.
1670	 */
1671	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
1672	if (ret < 0) {
1673		free(handle);
1674		close(sock_L3A_fd);
1675		close(netns_L3A_fd);
1676		if (errno == ENOSYS)
1677			SKIP(return, "listns() not supported");
1678		ASSERT_GE(ret, 0);
1679	}
1680
1681	bool found_netns_L3A = false;
1682	bool found_userns_L1 = false;
1683	bool found_userns_L2 = false;
1684	bool found_userns_L3 = false;
1685
1686	for (i = 0; i < ret; i++) {
1687		if (ns_ids[i] == netns_L3A_id)
1688			found_netns_L3A = true;
1689		if (ns_ids[i] == userns_L1_id)
1690			found_userns_L1 = true;
1691		if (ns_ids[i] == userns_L2_id)
1692			found_userns_L2 = true;
1693		if (ns_ids[i] == userns_L3_id)
1694			found_userns_L3 = true;
1695	}
1696
1697	ASSERT_TRUE(found_netns_L3A);
1698	ASSERT_TRUE(found_userns_L1);
1699	ASSERT_TRUE(found_userns_L2);
1700	ASSERT_TRUE(found_userns_L3);
1701	TH_LOG("Resurrection verified: all namespaces in hierarchy visible in listns()");
1702
1703	/*
1704	 * Test 2: Verify net_L3A can be reopened via file handle.
1705	 */
1706	nsfs_fh = (struct nsfs_file_handle *)handle->f_handle;
1707	nsfs_fh->ns_id = netns_L3A_id;
1708	nsfs_fh->ns_type = 0;
1709	nsfs_fh->ns_inum = 0;
1710
1711	reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1712	if (reopened_fd < 0) {
1713		free(handle);
1714		close(sock_L3A_fd);
1715		close(netns_L3A_fd);
1716		if (errno == EOPNOTSUPP || errno == ENOSYS || errno == EBADF)
1717			SKIP(return, "open_by_handle_at with FD_NSFS_ROOT not supported");
1718		TH_LOG("open_by_handle_at failed: %s", strerror(errno));
1719		ASSERT_GE(reopened_fd, 0);
1720	}
1721
1722	close(reopened_fd);
1723	TH_LOG("File handle test passed: net_L3A can be reopened");
1724
1725	/*
1726	 * Test 3: Verify that when we close the netns FD (dropping the last
1727	 * active reference), the entire tree becomes inactive and disappears
1728	 * from listns(). The cascade goes: net_L3A drops -> userns_L3 drops ->
1729	 * userns_L2 drops -> userns_L1 drops.
1730	 */
1731	close(netns_L3A_fd);
1732
1733	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
1734	ASSERT_GE(ret, 0);
1735
1736	found_netns_L3A = false;
1737	found_userns_L1 = false;
1738	found_userns_L2 = false;
1739	found_userns_L3 = false;
1740
1741	for (i = 0; i < ret; i++) {
1742		if (ns_ids[i] == netns_L3A_id)
1743			found_netns_L3A = true;
1744		if (ns_ids[i] == userns_L1_id)
1745			found_userns_L1 = true;
1746		if (ns_ids[i] == userns_L2_id)
1747			found_userns_L2 = true;
1748		if (ns_ids[i] == userns_L3_id)
1749			found_userns_L3 = true;
1750	}
1751
1752	ASSERT_FALSE(found_netns_L3A);
1753	ASSERT_FALSE(found_userns_L1);
1754	ASSERT_FALSE(found_userns_L2);
1755	ASSERT_FALSE(found_userns_L3);
1756	TH_LOG("Cascade test passed: all namespaces disappeared after netns FD closed");
1757
1758	/*
1759	 * Test 4: Verify file handle no longer works for inactive namespace.
1760	 */
1761	reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1762	if (reopened_fd >= 0) {
1763		close(reopened_fd);
1764		free(handle);
1765		ASSERT_TRUE(false); /* Should have failed */
1766	}
1767	TH_LOG("Inactive namespace correctly cannot be reopened via file handle");
1768
1769	/*
1770	 * Test 5: Verify that calling SIOCGSKNS again resurrects the tree again.
1771	 * The socket is still valid, so we can call SIOCGSKNS on it to resurrect
1772	 * the namespace tree once more.
1773	 */
1774	netns_L3A_fd = ioctl(sock_L3A_fd, SIOCGSKNS);
1775	ASSERT_GE(netns_L3A_fd, 0);
1776
1777	TH_LOG("Called SIOCGSKNS again to resurrect the namespace tree");
1778
1779	/* Verify the namespace tree is resurrected and visible in listns() */
1780	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
1781	ASSERT_GE(ret, 0);
1782
1783	found_netns_L3A = false;
1784	found_userns_L1 = false;
1785	found_userns_L2 = false;
1786	found_userns_L3 = false;
1787
1788	for (i = 0; i < ret; i++) {
1789		if (ns_ids[i] == netns_L3A_id)
1790			found_netns_L3A = true;
1791		if (ns_ids[i] == userns_L1_id)
1792			found_userns_L1 = true;
1793		if (ns_ids[i] == userns_L2_id)
1794			found_userns_L2 = true;
1795		if (ns_ids[i] == userns_L3_id)
1796			found_userns_L3 = true;
1797	}
1798
1799	ASSERT_TRUE(found_netns_L3A);
1800	ASSERT_TRUE(found_userns_L1);
1801	ASSERT_TRUE(found_userns_L2);
1802	ASSERT_TRUE(found_userns_L3);
1803	TH_LOG("Second resurrection verified: all namespaces in hierarchy visible in listns() again");
1804
1805	/* Verify we can reopen via file handle again */
1806	reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1807	if (reopened_fd < 0) {
1808		free(handle);
1809		close(sock_L3A_fd);
1810		close(netns_L3A_fd);
1811		TH_LOG("open_by_handle_at failed after second resurrection: %s", strerror(errno));
1812		ASSERT_GE(reopened_fd, 0);
1813	}
1814
1815	close(reopened_fd);
1816	TH_LOG("File handle test passed: net_L3A can be reopened after second resurrection");
1817
1818	/* Final cleanup */
1819	close(sock_L3A_fd);
1820	close(netns_L3A_fd);
1821	free(handle);
1822}
1823
1824TEST_HARNESS_MAIN
~/ Read-only file preview.