File Index
Files and directories
Sign in
FILE

listns_test.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/namespaces/listns_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 <linux/nsfs.h>
 11#include <sys/ioctl.h>
 12#include <sys/socket.h>
 13#include <sys/stat.h>
 14#include <sys/syscall.h>
 15#include <sys/types.h>
 16#include <sys/wait.h>
 17#include <unistd.h>
 18#include "../kselftest_harness.h"
 19#include "../filesystems/utils.h"
 20#include "wrappers.h"
 21
 22/*
 23 * Test basic listns() functionality with the unified namespace tree.
 24 * List all active namespaces globally.
 25 */
 26TEST(listns_basic_unified)
 27{
 28	struct ns_id_req req = {
 29		.size = sizeof(req),
 30		.spare = 0,
 31		.ns_id = 0,
 32		.ns_type = 0,  /* All types */
 33		.spare2 = 0,
 34		.user_ns_id = 0,  /* Global listing */
 35	};
 36	__u64 ns_ids[100];
 37	ssize_t ret;
 38
 39	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
 40	if (ret < 0) {
 41		if (errno == ENOSYS)
 42			SKIP(return, "listns() not supported");
 43		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);
 44		ASSERT_TRUE(false);
 45	}
 46
 47	/* Should find at least the initial namespaces */
 48	ASSERT_GT(ret, 0);
 49	TH_LOG("Found %zd active namespaces", ret);
 50
 51	/* Verify all returned IDs are non-zero */
 52	for (ssize_t i = 0; i < ret; i++) {
 53		ASSERT_NE(ns_ids[i], 0);
 54		TH_LOG("  [%zd] ns_id: %llu", i, (unsigned long long)ns_ids[i]);
 55	}
 56}
 57
 58/*
 59 * Test listns() with type filtering.
 60 * List only network namespaces.
 61 */
 62TEST(listns_filter_by_type)
 63{
 64	struct ns_id_req req = {
 65		.size = sizeof(req),
 66		.spare = 0,
 67		.ns_id = 0,
 68		.ns_type = CLONE_NEWNET,  /* Only network namespaces */
 69		.spare2 = 0,
 70		.user_ns_id = 0,
 71	};
 72	__u64 ns_ids[100];
 73	ssize_t ret;
 74
 75	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
 76	if (ret < 0) {
 77		if (errno == ENOSYS)
 78			SKIP(return, "listns() not supported");
 79		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);
 80		ASSERT_TRUE(false);
 81	}
 82	ASSERT_GE(ret, 0);
 83
 84	/* Should find at least init_net */
 85	ASSERT_GT(ret, 0);
 86	TH_LOG("Found %zd active network namespaces", ret);
 87
 88	/* Verify we can open each namespace and it's actually a network namespace */
 89	for (ssize_t i = 0; i < ret && i < 5; i++) {
 90		struct nsfs_file_handle nsfh = {
 91			.ns_id = ns_ids[i],
 92			.ns_type = CLONE_NEWNET,
 93			.ns_inum = 0,
 94		};
 95		struct file_handle *fh;
 96		int fd;
 97
 98		fh = (struct file_handle *)malloc(sizeof(*fh) + sizeof(nsfh));
 99		ASSERT_NE(fh, NULL);
100		fh->handle_bytes = sizeof(nsfh);
101		fh->handle_type = 0;
102		memcpy(fh->f_handle, &nsfh, sizeof(nsfh));
103
104		fd = open_by_handle_at(-10003, fh, O_RDONLY);
105		free(fh);
106
107		if (fd >= 0) {
108			int ns_type;
109			/* Verify it's a network namespace via ioctl */
110			ns_type = ioctl(fd, NS_GET_NSTYPE);
111			if (ns_type >= 0) {
112				ASSERT_EQ(ns_type, CLONE_NEWNET);
113			}
114			close(fd);
115		}
116	}
117}
118
119/*
120 * Test listns() pagination.
121 * List namespaces in batches.
122 */
123TEST(listns_pagination)
124{
125	struct ns_id_req req = {
126		.size = sizeof(req),
127		.spare = 0,
128		.ns_id = 0,
129		.ns_type = 0,
130		.spare2 = 0,
131		.user_ns_id = 0,
132	};
133	__u64 batch1[2], batch2[2];
134	ssize_t ret1, ret2;
135
136	/* Get first batch */
137	ret1 = sys_listns(&req, batch1, ARRAY_SIZE(batch1), 0);
138	if (ret1 < 0) {
139		if (errno == ENOSYS)
140			SKIP(return, "listns() not supported");
141		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);
142		ASSERT_TRUE(false);
143	}
144	ASSERT_GE(ret1, 0);
145
146	if (ret1 == 0)
147		SKIP(return, "No namespaces found");
148
149	TH_LOG("First batch: %zd namespaces", ret1);
150
151	/* Get second batch using last ID from first batch */
152	if (ret1 == ARRAY_SIZE(batch1)) {
153		req.ns_id = batch1[ret1 - 1];
154		ret2 = sys_listns(&req, batch2, ARRAY_SIZE(batch2), 0);
155		ASSERT_GE(ret2, 0);
156
157		TH_LOG("Second batch: %zd namespaces (after ns_id=%llu)",
158		       ret2, (unsigned long long)req.ns_id);
159
160		/* If we got more results, verify IDs are monotonically increasing */
161		if (ret2 > 0) {
162			ASSERT_GT(batch2[0], batch1[ret1 - 1]);
163			TH_LOG("Pagination working: %llu > %llu",
164			       (unsigned long long)batch2[0],
165			       (unsigned long long)batch1[ret1 - 1]);
166		}
167	} else {
168		TH_LOG("All namespaces fit in first batch");
169	}
170}
171
172/*
173 * Test listns() with LISTNS_CURRENT_USER.
174 * List namespaces owned by current user namespace.
175 */
176TEST(listns_current_user)
177{
178	struct ns_id_req req = {
179		.size = sizeof(req),
180		.spare = 0,
181		.ns_id = 0,
182		.ns_type = 0,
183		.spare2 = 0,
184		.user_ns_id = LISTNS_CURRENT_USER,
185	};
186	__u64 ns_ids[100];
187	ssize_t ret;
188
189	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
190	if (ret < 0) {
191		if (errno == ENOSYS)
192			SKIP(return, "listns() not supported");
193		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);
194		ASSERT_TRUE(false);
195	}
196	ASSERT_GE(ret, 0);
197
198	/* Should find at least the initial namespaces if we're in init_user_ns */
199	TH_LOG("Found %zd namespaces owned by current user namespace", ret);
200
201	for (ssize_t i = 0; i < ret; i++)
202		TH_LOG("  [%zd] ns_id: %llu", i, (unsigned long long)ns_ids[i]);
203}
204
205/*
206 * Test that listns() only returns active namespaces.
207 * Create a namespace, let it become inactive, verify it's not listed.
208 */
209TEST(listns_only_active)
210{
211	struct ns_id_req req = {
212		.size = sizeof(req),
213		.spare = 0,
214		.ns_id = 0,
215		.ns_type = CLONE_NEWNET,
216		.spare2 = 0,
217		.user_ns_id = 0,
218	};
219	__u64 ns_ids_before[100], ns_ids_after[100];
220	ssize_t ret_before, ret_after;
221	int pipefd[2];
222	pid_t pid;
223	__u64 new_ns_id = 0;
224	int status;
225
226	/* Get initial list */
227	ret_before = sys_listns(&req, ns_ids_before, ARRAY_SIZE(ns_ids_before), 0);
228	if (ret_before < 0) {
229		if (errno == ENOSYS)
230			SKIP(return, "listns() not supported");
231		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);
232		ASSERT_TRUE(false);
233	}
234	ASSERT_GE(ret_before, 0);
235
236	TH_LOG("Before: %zd active network namespaces", ret_before);
237
238	/* Create a new namespace in a child process and get its ID */
239	ASSERT_EQ(pipe(pipefd), 0);
240
241	pid = fork();
242	ASSERT_GE(pid, 0);
243
244	if (pid == 0) {
245		int fd;
246		__u64 ns_id;
247
248		close(pipefd[0]);
249
250		/* Create new network namespace */
251		if (unshare(CLONE_NEWNET) < 0) {
252			close(pipefd[1]);
253			exit(1);
254		}
255
256		/* Get its ID */
257		fd = open("/proc/self/ns/net", O_RDONLY);
258		if (fd < 0) {
259			close(pipefd[1]);
260			exit(1);
261		}
262
263		if (ioctl(fd, NS_GET_ID, &ns_id) < 0) {
264			close(fd);
265			close(pipefd[1]);
266			exit(1);
267		}
268		close(fd);
269
270		/* Send ID to parent */
271		write(pipefd[1], &ns_id, sizeof(ns_id));
272		close(pipefd[1]);
273
274		/* Keep namespace active briefly */
275		usleep(100000);
276		exit(0);
277	}
278
279	/* Parent reads the new namespace ID */
280	{
281		int bytes;
282
283		close(pipefd[1]);
284		bytes = read(pipefd[0], &new_ns_id, sizeof(new_ns_id));
285		close(pipefd[0]);
286
287		if (bytes == sizeof(new_ns_id)) {
288			__u64 ns_ids_during[100];
289			int ret_during;
290
291			TH_LOG("Child created namespace with ID %llu", (unsigned long long)new_ns_id);
292
293			/* List namespaces while child is still alive - should see new one */
294			ret_during = sys_listns(&req, ns_ids_during, ARRAY_SIZE(ns_ids_during), 0);
295			ASSERT_GE(ret_during, 0);
296			TH_LOG("During: %d active network namespaces", ret_during);
297
298			/* Should have more namespaces than before */
299			ASSERT_GE(ret_during, ret_before);
300		}
301	}
302
303	/* Wait for child to exit */
304	waitpid(pid, &status, 0);
305
306	/* Give time for namespace to become inactive */
307	usleep(100000);
308
309	/* List namespaces after child exits - should not see new one */
310	ret_after = sys_listns(&req, ns_ids_after, ARRAY_SIZE(ns_ids_after), 0);
311	ASSERT_GE(ret_after, 0);
312	TH_LOG("After: %zd active network namespaces", ret_after);
313
314	/* Verify the new namespace ID is not in the after list */
315	if (new_ns_id != 0) {
316		bool found = false;
317
318		for (ssize_t i = 0; i < ret_after; i++) {
319			if (ns_ids_after[i] == new_ns_id) {
320				found = true;
321				break;
322			}
323		}
324		ASSERT_FALSE(found);
325	}
326}
327
328/*
329 * Test listns() with specific user namespace ID.
330 * Create a user namespace and list namespaces it owns.
331 */
332TEST(listns_specific_userns)
333{
334	struct ns_id_req req = {
335		.size = sizeof(req),
336		.spare = 0,
337		.ns_id = 0,
338		.ns_type = 0,
339		.spare2 = 0,
340		.user_ns_id = 0,  /* Will be filled with created userns ID */
341	};
342	__u64 ns_ids[100];
343	int sv[2];
344	pid_t pid;
345	int status;
346	__u64 user_ns_id = 0;
347	int bytes;
348	ssize_t ret;
349
350	ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, sv), 0);
351
352	pid = fork();
353	ASSERT_GE(pid, 0);
354
355	if (pid == 0) {
356		int fd;
357		__u64 ns_id;
358		char buf;
359
360		close(sv[0]);
361
362		/* Create new user namespace */
363		if (setup_userns() < 0) {
364			close(sv[1]);
365			exit(1);
366		}
367
368		/* Get user namespace ID */
369		fd = open("/proc/self/ns/user", O_RDONLY);
370		if (fd < 0) {
371			close(sv[1]);
372			exit(1);
373		}
374
375		if (ioctl(fd, NS_GET_ID, &ns_id) < 0) {
376			close(fd);
377			close(sv[1]);
378			exit(1);
379		}
380		close(fd);
381
382		/* Send ID to parent */
383		if (write(sv[1], &ns_id, sizeof(ns_id)) != sizeof(ns_id)) {
384			close(sv[1]);
385			exit(1);
386		}
387
388		/* Create some namespaces owned by this user namespace */
389		unshare(CLONE_NEWNET);
390		unshare(CLONE_NEWUTS);
391
392		/* Wait for parent signal */
393		if (read(sv[1], &buf, 1) != 1) {
394			close(sv[1]);
395			exit(1);
396		}
397		close(sv[1]);
398		exit(0);
399	}
400
401	/* Parent */
402	close(sv[1]);
403	bytes = read(sv[0], &user_ns_id, sizeof(user_ns_id));
404
405	if (bytes != sizeof(user_ns_id)) {
406		close(sv[0]);
407		kill(pid, SIGKILL);
408		waitpid(pid, NULL, 0);
409		SKIP(return, "Failed to get user namespace ID from child");
410	}
411
412	TH_LOG("Child created user namespace with ID %llu", (unsigned long long)user_ns_id);
413
414	/* List namespaces owned by this user namespace */
415	req.user_ns_id = user_ns_id;
416	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
417
418	if (ret < 0) {
419		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);
420		close(sv[0]);
421		kill(pid, SIGKILL);
422		waitpid(pid, NULL, 0);
423		if (errno == ENOSYS) {
424			SKIP(return, "listns() not supported");
425		}
426		ASSERT_GE(ret, 0);
427	}
428
429	TH_LOG("Found %zd namespaces owned by user namespace %llu", ret,
430	       (unsigned long long)user_ns_id);
431
432	/* Should find at least the network and UTS namespaces we created */
433	if (ret > 0) {
434		for (ssize_t i = 0; i < ret && i < 10; i++)
435			TH_LOG("  [%zd] ns_id: %llu", i, (unsigned long long)ns_ids[i]);
436	}
437
438	/* Signal child to exit */
439	if (write(sv[0], "X", 1) != 1) {
440		close(sv[0]);
441		kill(pid, SIGKILL);
442		waitpid(pid, NULL, 0);
443		ASSERT_TRUE(false);
444	}
445	close(sv[0]);
446	waitpid(pid, &status, 0);
447}
448
449/*
450 * Test listns() with multiple namespace types filter.
451 */
452TEST(listns_multiple_types)
453{
454	struct ns_id_req req = {
455		.size = sizeof(req),
456		.spare = 0,
457		.ns_id = 0,
458		.ns_type = CLONE_NEWNET | CLONE_NEWUTS,  /* Network and UTS */
459		.spare2 = 0,
460		.user_ns_id = 0,
461	};
462	__u64 ns_ids[100];
463	ssize_t ret;
464
465	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
466	if (ret < 0) {
467		if (errno == ENOSYS)
468			SKIP(return, "listns() not supported");
469		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);
470		ASSERT_TRUE(false);
471	}
472	ASSERT_GE(ret, 0);
473
474	TH_LOG("Found %zd active network/UTS namespaces", ret);
475
476	for (ssize_t i = 0; i < ret; i++)
477		TH_LOG("  [%zd] ns_id: %llu", i, (unsigned long long)ns_ids[i]);
478}
479
480/*
481 * Test that hierarchical active reference propagation keeps parent
482 * user namespaces visible in listns().
483 */
484TEST(listns_hierarchical_visibility)
485{
486	struct ns_id_req req = {
487		.size = sizeof(req),
488		.spare = 0,
489		.ns_id = 0,
490		.ns_type = CLONE_NEWUSER,
491		.spare2 = 0,
492		.user_ns_id = 0,
493	};
494	__u64 parent_ns_id = 0, child_ns_id = 0;
495	int sv[2];
496	pid_t pid;
497	int status;
498	int bytes;
499	__u64 ns_ids[100];
500	ssize_t ret;
501	bool found_parent, found_child;
502
503	ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, sv), 0);
504
505	pid = fork();
506	ASSERT_GE(pid, 0);
507
508	if (pid == 0) {
509		int fd;
510		char buf;
511
512		close(sv[0]);
513
514		/* Create parent user namespace */
515		if (setup_userns() < 0) {
516			close(sv[1]);
517			exit(1);
518		}
519
520		fd = open("/proc/self/ns/user", O_RDONLY);
521		if (fd < 0) {
522			close(sv[1]);
523			exit(1);
524		}
525
526		if (ioctl(fd, NS_GET_ID, &parent_ns_id) < 0) {
527			close(fd);
528			close(sv[1]);
529			exit(1);
530		}
531		close(fd);
532
533		/* Create child user namespace */
534		if (setup_userns() < 0) {
535			close(sv[1]);
536			exit(1);
537		}
538
539		fd = open("/proc/self/ns/user", O_RDONLY);
540		if (fd < 0) {
541			close(sv[1]);
542			exit(1);
543		}
544
545		if (ioctl(fd, NS_GET_ID, &child_ns_id) < 0) {
546			close(fd);
547			close(sv[1]);
548			exit(1);
549		}
550		close(fd);
551
552		/* Send both IDs to parent */
553		if (write(sv[1], &parent_ns_id, sizeof(parent_ns_id)) != sizeof(parent_ns_id)) {
554			close(sv[1]);
555			exit(1);
556		}
557		if (write(sv[1], &child_ns_id, sizeof(child_ns_id)) != sizeof(child_ns_id)) {
558			close(sv[1]);
559			exit(1);
560		}
561
562		/* Wait for parent signal */
563		if (read(sv[1], &buf, 1) != 1) {
564			close(sv[1]);
565			exit(1);
566		}
567		close(sv[1]);
568		exit(0);
569	}
570
571	/* Parent */
572	close(sv[1]);
573
574	/* Read both namespace IDs */
575	bytes = read(sv[0], &parent_ns_id, sizeof(parent_ns_id));
576	bytes += read(sv[0], &child_ns_id, sizeof(child_ns_id));
577
578	if (bytes != (int)(2 * sizeof(__u64))) {
579		close(sv[0]);
580		kill(pid, SIGKILL);
581		waitpid(pid, NULL, 0);
582		SKIP(return, "Failed to get namespace IDs from child");
583	}
584
585	TH_LOG("Parent user namespace ID: %llu", (unsigned long long)parent_ns_id);
586	TH_LOG("Child user namespace ID: %llu", (unsigned long long)child_ns_id);
587
588	/* List all user namespaces */
589	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
590
591	if (ret < 0 && errno == ENOSYS) {
592		close(sv[0]);
593		kill(pid, SIGKILL);
594		waitpid(pid, NULL, 0);
595		SKIP(return, "listns() not supported");
596	}
597
598	ASSERT_GE(ret, 0);
599	TH_LOG("Found %zd active user namespaces", ret);
600
601	/* Both parent and child should be visible (active due to child process) */
602	found_parent = false;
603	found_child = false;
604	for (ssize_t i = 0; i < ret; i++) {
605		if (ns_ids[i] == parent_ns_id)
606			found_parent = true;
607		if (ns_ids[i] == child_ns_id)
608			found_child = true;
609	}
610
611	TH_LOG("Parent namespace %s, child namespace %s",
612	       found_parent ? "found" : "NOT FOUND",
613	       found_child ? "found" : "NOT FOUND");
614
615	ASSERT_TRUE(found_child);
616	/* With hierarchical propagation, parent should also be active */
617	ASSERT_TRUE(found_parent);
618
619	/* Signal child to exit */
620	if (write(sv[0], "X", 1) != 1) {
621		close(sv[0]);
622		kill(pid, SIGKILL);
623		waitpid(pid, NULL, 0);
624		ASSERT_TRUE(false);
625	}
626	close(sv[0]);
627	waitpid(pid, &status, 0);
628}
629
630/*
631 * Test error cases for listns().
632 */
633TEST(listns_error_cases)
634{
635	struct ns_id_req req = {
636		.size = sizeof(req),
637		.spare = 0,
638		.ns_id = 0,
639		.ns_type = 0,
640		.spare2 = 0,
641		.user_ns_id = 0,
642	};
643	__u64 ns_ids[10];
644	int ret;
645
646	/* Test with invalid flags */
647	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0xFFFF);
648	if (errno == ENOSYS) {
649		/* listns() not supported, skip this check */
650	} else {
651		ASSERT_LT(ret, 0);
652		ASSERT_EQ(errno, EINVAL);
653	}
654
655	/* Test with NULL ns_ids array */
656	ret = sys_listns(&req, NULL, 10, 0);
657	ASSERT_LT(ret, 0);
658
659	/* Test with invalid spare field */
660	req.spare = 1;
661	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
662	if (errno == ENOSYS) {
663		/* listns() not supported, skip this check */
664	} else {
665		ASSERT_LT(ret, 0);
666		ASSERT_EQ(errno, EINVAL);
667	}
668	req.spare = 0;
669
670	/* Test with huge nr_ns_ids */
671	ret = sys_listns(&req, ns_ids, 2000000, 0);
672	if (errno == ENOSYS) {
673		/* listns() not supported, skip this check */
674	} else {
675		ASSERT_LT(ret, 0);
676	}
677}
678
679TEST_HARNESS_MAIN
~/ Read-only file preview.