679 lines · 14.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#define _GNU_SOURCE#include <errno.h>#include <fcntl.h>#include <limits.h>#include <sched.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <linux/nsfs.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/stat.h>#include <sys/syscall.h>#include <sys/types.h>#include <sys/wait.h>#include <unistd.h>#include "../kselftest_harness.h"#include "../filesystems/utils.h"#include "wrappers.h" /* * Test basic listns() functionality with the unified namespace tree. * List all active namespaces globally. */TEST(listns_basic_unified){	struct ns_id_req req = {		.size = sizeof(req),		.spare = 0,		.ns_id = 0,		.ns_type = 0,  /* All types */		.spare2 = 0,		.user_ns_id = 0,  /* Global listing */	};	__u64 ns_ids[100];	ssize_t ret; 	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);	if (ret < 0) {		if (errno == ENOSYS)			SKIP(return, "listns() not supported");		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);		ASSERT_TRUE(false);	} 	/* Should find at least the initial namespaces */	ASSERT_GT(ret, 0);	TH_LOG("Found %zd active namespaces", ret); 	/* Verify all returned IDs are non-zero */	for (ssize_t i = 0; i < ret; i++) {		ASSERT_NE(ns_ids[i], 0);		TH_LOG("  [%zd] ns_id: %llu", i, (unsigned long long)ns_ids[i]);	}} /* * Test listns() with type filtering. * List only network namespaces. */TEST(listns_filter_by_type){	struct ns_id_req req = {		.size = sizeof(req),		.spare = 0,		.ns_id = 0,		.ns_type = CLONE_NEWNET,  /* Only network namespaces */		.spare2 = 0,		.user_ns_id = 0,	};	__u64 ns_ids[100];	ssize_t ret; 	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);	if (ret < 0) {		if (errno == ENOSYS)			SKIP(return, "listns() not supported");		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);		ASSERT_TRUE(false);	}	ASSERT_GE(ret, 0); 	/* Should find at least init_net */	ASSERT_GT(ret, 0);	TH_LOG("Found %zd active network namespaces", ret); 	/* Verify we can open each namespace and it's actually a network namespace */	for (ssize_t i = 0; i < ret && i < 5; i++) {		struct nsfs_file_handle nsfh = {			.ns_id = ns_ids[i],			.ns_type = CLONE_NEWNET,			.ns_inum = 0,		};		struct file_handle *fh;		int fd; 		fh = (struct file_handle *)malloc(sizeof(*fh) + sizeof(nsfh));		ASSERT_NE(fh, NULL);		fh->handle_bytes = sizeof(nsfh);		fh->handle_type = 0;		memcpy(fh->f_handle, &nsfh, sizeof(nsfh)); 		fd = open_by_handle_at(-10003, fh, O_RDONLY);		free(fh); 		if (fd >= 0) {			int ns_type;			/* Verify it's a network namespace via ioctl */			ns_type = ioctl(fd, NS_GET_NSTYPE);			if (ns_type >= 0) {				ASSERT_EQ(ns_type, CLONE_NEWNET);			}			close(fd);		}	}} /* * Test listns() pagination. * List namespaces in batches. */TEST(listns_pagination){	struct ns_id_req req = {		.size = sizeof(req),		.spare = 0,		.ns_id = 0,		.ns_type = 0,		.spare2 = 0,		.user_ns_id = 0,	};	__u64 batch1[2], batch2[2];	ssize_t ret1, ret2; 	/* Get first batch */	ret1 = sys_listns(&req, batch1, ARRAY_SIZE(batch1), 0);	if (ret1 < 0) {		if (errno == ENOSYS)			SKIP(return, "listns() not supported");		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);		ASSERT_TRUE(false);	}	ASSERT_GE(ret1, 0); 	if (ret1 == 0)		SKIP(return, "No namespaces found"); 	TH_LOG("First batch: %zd namespaces", ret1); 	/* Get second batch using last ID from first batch */	if (ret1 == ARRAY_SIZE(batch1)) {		req.ns_id = batch1[ret1 - 1];		ret2 = sys_listns(&req, batch2, ARRAY_SIZE(batch2), 0);		ASSERT_GE(ret2, 0); 		TH_LOG("Second batch: %zd namespaces (after ns_id=%llu)",		       ret2, (unsigned long long)req.ns_id); 		/* If we got more results, verify IDs are monotonically increasing */		if (ret2 > 0) {			ASSERT_GT(batch2[0], batch1[ret1 - 1]);			TH_LOG("Pagination working: %llu > %llu",			       (unsigned long long)batch2[0],			       (unsigned long long)batch1[ret1 - 1]);		}	} else {		TH_LOG("All namespaces fit in first batch");	}} /* * Test listns() with LISTNS_CURRENT_USER. * List namespaces owned by current user namespace. */TEST(listns_current_user){	struct ns_id_req req = {		.size = sizeof(req),		.spare = 0,		.ns_id = 0,		.ns_type = 0,		.spare2 = 0,		.user_ns_id = LISTNS_CURRENT_USER,	};	__u64 ns_ids[100];	ssize_t ret; 	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);	if (ret < 0) {		if (errno == ENOSYS)			SKIP(return, "listns() not supported");		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);		ASSERT_TRUE(false);	}	ASSERT_GE(ret, 0); 	/* Should find at least the initial namespaces if we're in init_user_ns */	TH_LOG("Found %zd namespaces owned by current user namespace", ret); 	for (ssize_t i = 0; i < ret; i++)		TH_LOG("  [%zd] ns_id: %llu", i, (unsigned long long)ns_ids[i]);} /* * Test that listns() only returns active namespaces. * Create a namespace, let it become inactive, verify it's not listed. */TEST(listns_only_active){	struct ns_id_req req = {		.size = sizeof(req),		.spare = 0,		.ns_id = 0,		.ns_type = CLONE_NEWNET,		.spare2 = 0,		.user_ns_id = 0,	};	__u64 ns_ids_before[100], ns_ids_after[100];	ssize_t ret_before, ret_after;	int pipefd[2];	pid_t pid;	__u64 new_ns_id = 0;	int status; 	/* Get initial list */	ret_before = sys_listns(&req, ns_ids_before, ARRAY_SIZE(ns_ids_before), 0);	if (ret_before < 0) {		if (errno == ENOSYS)			SKIP(return, "listns() not supported");		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);		ASSERT_TRUE(false);	}	ASSERT_GE(ret_before, 0); 	TH_LOG("Before: %zd active network namespaces", ret_before); 	/* Create a new namespace in a child process and get its ID */	ASSERT_EQ(pipe(pipefd), 0); 	pid = fork();	ASSERT_GE(pid, 0); 	if (pid == 0) {		int fd;		__u64 ns_id; 		close(pipefd[0]); 		/* Create new network namespace */		if (unshare(CLONE_NEWNET) < 0) {			close(pipefd[1]);			exit(1);		} 		/* Get its ID */		fd = open("/proc/self/ns/net", O_RDONLY);		if (fd < 0) {			close(pipefd[1]);			exit(1);		} 		if (ioctl(fd, NS_GET_ID, &ns_id) < 0) {			close(fd);			close(pipefd[1]);			exit(1);		}		close(fd); 		/* Send ID to parent */		write(pipefd[1], &ns_id, sizeof(ns_id));		close(pipefd[1]); 		/* Keep namespace active briefly */		usleep(100000);		exit(0);	} 	/* Parent reads the new namespace ID */	{		int bytes; 		close(pipefd[1]);		bytes = read(pipefd[0], &new_ns_id, sizeof(new_ns_id));		close(pipefd[0]); 		if (bytes == sizeof(new_ns_id)) {			__u64 ns_ids_during[100];			int ret_during; 			TH_LOG("Child created namespace with ID %llu", (unsigned long long)new_ns_id); 			/* List namespaces while child is still alive - should see new one */			ret_during = sys_listns(&req, ns_ids_during, ARRAY_SIZE(ns_ids_during), 0);			ASSERT_GE(ret_during, 0);			TH_LOG("During: %d active network namespaces", ret_during); 			/* Should have more namespaces than before */			ASSERT_GE(ret_during, ret_before);		}	} 	/* Wait for child to exit */	waitpid(pid, &status, 0); 	/* Give time for namespace to become inactive */	usleep(100000); 	/* List namespaces after child exits - should not see new one */	ret_after = sys_listns(&req, ns_ids_after, ARRAY_SIZE(ns_ids_after), 0);	ASSERT_GE(ret_after, 0);	TH_LOG("After: %zd active network namespaces", ret_after); 	/* Verify the new namespace ID is not in the after list */	if (new_ns_id != 0) {		bool found = false; 		for (ssize_t i = 0; i < ret_after; i++) {			if (ns_ids_after[i] == new_ns_id) {				found = true;				break;			}		}		ASSERT_FALSE(found);	}} /* * Test listns() with specific user namespace ID. * Create a user namespace and list namespaces it owns. */TEST(listns_specific_userns){	struct ns_id_req req = {		.size = sizeof(req),		.spare = 0,		.ns_id = 0,		.ns_type = 0,		.spare2 = 0,		.user_ns_id = 0,  /* Will be filled with created userns ID */	};	__u64 ns_ids[100];	int sv[2];	pid_t pid;	int status;	__u64 user_ns_id = 0;	int bytes;	ssize_t ret; 	ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, sv), 0); 	pid = fork();	ASSERT_GE(pid, 0); 	if (pid == 0) {		int fd;		__u64 ns_id;		char buf; 		close(sv[0]); 		/* Create new user namespace */		if (setup_userns() < 0) {			close(sv[1]);			exit(1);		} 		/* Get user namespace ID */		fd = open("/proc/self/ns/user", O_RDONLY);		if (fd < 0) {			close(sv[1]);			exit(1);		} 		if (ioctl(fd, NS_GET_ID, &ns_id) < 0) {			close(fd);			close(sv[1]);			exit(1);		}		close(fd); 		/* Send ID to parent */		if (write(sv[1], &ns_id, sizeof(ns_id)) != sizeof(ns_id)) {			close(sv[1]);			exit(1);		} 		/* Create some namespaces owned by this user namespace */		unshare(CLONE_NEWNET);		unshare(CLONE_NEWUTS); 		/* Wait for parent signal */		if (read(sv[1], &buf, 1) != 1) {			close(sv[1]);			exit(1);		}		close(sv[1]);		exit(0);	} 	/* Parent */	close(sv[1]);	bytes = read(sv[0], &user_ns_id, sizeof(user_ns_id)); 	if (bytes != sizeof(user_ns_id)) {		close(sv[0]);		kill(pid, SIGKILL);		waitpid(pid, NULL, 0);		SKIP(return, "Failed to get user namespace ID from child");	} 	TH_LOG("Child created user namespace with ID %llu", (unsigned long long)user_ns_id); 	/* List namespaces owned by this user namespace */	req.user_ns_id = user_ns_id;	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0); 	if (ret < 0) {		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);		close(sv[0]);		kill(pid, SIGKILL);		waitpid(pid, NULL, 0);		if (errno == ENOSYS) {			SKIP(return, "listns() not supported");		}		ASSERT_GE(ret, 0);	} 	TH_LOG("Found %zd namespaces owned by user namespace %llu", ret,	       (unsigned long long)user_ns_id); 	/* Should find at least the network and UTS namespaces we created */	if (ret > 0) {		for (ssize_t i = 0; i < ret && i < 10; i++)			TH_LOG("  [%zd] ns_id: %llu", i, (unsigned long long)ns_ids[i]);	} 	/* Signal child to exit */	if (write(sv[0], "X", 1) != 1) {		close(sv[0]);		kill(pid, SIGKILL);		waitpid(pid, NULL, 0);		ASSERT_TRUE(false);	}	close(sv[0]);	waitpid(pid, &status, 0);} /* * Test listns() with multiple namespace types filter. */TEST(listns_multiple_types){	struct ns_id_req req = {		.size = sizeof(req),		.spare = 0,		.ns_id = 0,		.ns_type = CLONE_NEWNET | CLONE_NEWUTS,  /* Network and UTS */		.spare2 = 0,		.user_ns_id = 0,	};	__u64 ns_ids[100];	ssize_t ret; 	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);	if (ret < 0) {		if (errno == ENOSYS)			SKIP(return, "listns() not supported");		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);		ASSERT_TRUE(false);	}	ASSERT_GE(ret, 0); 	TH_LOG("Found %zd active network/UTS namespaces", ret); 	for (ssize_t i = 0; i < ret; i++)		TH_LOG("  [%zd] ns_id: %llu", i, (unsigned long long)ns_ids[i]);} /* * Test that hierarchical active reference propagation keeps parent * user namespaces visible in listns(). */TEST(listns_hierarchical_visibility){	struct ns_id_req req = {		.size = sizeof(req),		.spare = 0,		.ns_id = 0,		.ns_type = CLONE_NEWUSER,		.spare2 = 0,		.user_ns_id = 0,	};	__u64 parent_ns_id = 0, child_ns_id = 0;	int sv[2];	pid_t pid;	int status;	int bytes;	__u64 ns_ids[100];	ssize_t ret;

Lines 1–500 of 679.