xref: /linux/tools/testing/selftests/namespaces/listns_test.c (revision abac8de3e5000b2b448bab4a9d19e35ce32e80ce)
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/stat.h>
13 #include <sys/syscall.h>
14 #include <sys/types.h>
15 #include <sys/wait.h>
16 #include <unistd.h>
17 #include "../kselftest_harness.h"
18 #include "../filesystems/utils.h"
19 #include "wrappers.h"
20 
21 /*
22  * Test basic listns() functionality with the unified namespace tree.
23  * List all active namespaces globally.
24  */
25 TEST(listns_basic_unified)
26 {
27 	struct ns_id_req req = {
28 		.size = sizeof(req),
29 		.spare = 0,
30 		.ns_id = 0,
31 		.ns_type = 0,  /* All types */
32 		.spare2 = 0,
33 		.user_ns_id = 0,  /* Global listing */
34 	};
35 	__u64 ns_ids[100];
36 	ssize_t ret;
37 
38 	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
39 	if (ret < 0) {
40 		if (errno == ENOSYS)
41 			SKIP(return, "listns() not supported");
42 		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);
43 		ASSERT_TRUE(false);
44 	}
45 
46 	/* Should find at least the initial namespaces */
47 	ASSERT_GT(ret, 0);
48 	TH_LOG("Found %zd active namespaces", ret);
49 
50 	/* Verify all returned IDs are non-zero */
51 	for (ssize_t i = 0; i < ret; i++) {
52 		ASSERT_NE(ns_ids[i], 0);
53 		TH_LOG("  [%zd] ns_id: %llu", i, (unsigned long long)ns_ids[i]);
54 	}
55 }
56 
57 /*
58  * Test listns() with type filtering.
59  * List only network namespaces.
60  */
61 TEST(listns_filter_by_type)
62 {
63 	struct ns_id_req req = {
64 		.size = sizeof(req),
65 		.spare = 0,
66 		.ns_id = 0,
67 		.ns_type = CLONE_NEWNET,  /* Only network namespaces */
68 		.spare2 = 0,
69 		.user_ns_id = 0,
70 	};
71 	__u64 ns_ids[100];
72 	ssize_t ret;
73 
74 	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
75 	if (ret < 0) {
76 		if (errno == ENOSYS)
77 			SKIP(return, "listns() not supported");
78 		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);
79 		ASSERT_TRUE(false);
80 	}
81 	ASSERT_GE(ret, 0);
82 
83 	/* Should find at least init_net */
84 	ASSERT_GT(ret, 0);
85 	TH_LOG("Found %zd active network namespaces", ret);
86 
87 	/* Verify we can open each namespace and it's actually a network namespace */
88 	for (ssize_t i = 0; i < ret && i < 5; i++) {
89 		struct nsfs_file_handle nsfh = {
90 			.ns_id = ns_ids[i],
91 			.ns_type = CLONE_NEWNET,
92 			.ns_inum = 0,
93 		};
94 		struct file_handle *fh;
95 		int fd;
96 
97 		fh = (struct file_handle *)malloc(sizeof(*fh) + sizeof(nsfh));
98 		ASSERT_NE(fh, NULL);
99 		fh->handle_bytes = sizeof(nsfh);
100 		fh->handle_type = 0;
101 		memcpy(fh->f_handle, &nsfh, sizeof(nsfh));
102 
103 		fd = open_by_handle_at(-10003, fh, O_RDONLY);
104 		free(fh);
105 
106 		if (fd >= 0) {
107 			int ns_type;
108 			/* Verify it's a network namespace via ioctl */
109 			ns_type = ioctl(fd, NS_GET_NSTYPE);
110 			if (ns_type >= 0) {
111 				ASSERT_EQ(ns_type, CLONE_NEWNET);
112 			}
113 			close(fd);
114 		}
115 	}
116 }
117 
118 /*
119  * Test listns() pagination.
120  * List namespaces in batches.
121  */
122 TEST(listns_pagination)
123 {
124 	struct ns_id_req req = {
125 		.size = sizeof(req),
126 		.spare = 0,
127 		.ns_id = 0,
128 		.ns_type = 0,
129 		.spare2 = 0,
130 		.user_ns_id = 0,
131 	};
132 	__u64 batch1[2], batch2[2];
133 	ssize_t ret1, ret2;
134 
135 	/* Get first batch */
136 	ret1 = sys_listns(&req, batch1, ARRAY_SIZE(batch1), 0);
137 	if (ret1 < 0) {
138 		if (errno == ENOSYS)
139 			SKIP(return, "listns() not supported");
140 		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);
141 		ASSERT_TRUE(false);
142 	}
143 	ASSERT_GE(ret1, 0);
144 
145 	if (ret1 == 0)
146 		SKIP(return, "No namespaces found");
147 
148 	TH_LOG("First batch: %zd namespaces", ret1);
149 
150 	/* Get second batch using last ID from first batch */
151 	if (ret1 == ARRAY_SIZE(batch1)) {
152 		req.ns_id = batch1[ret1 - 1];
153 		ret2 = sys_listns(&req, batch2, ARRAY_SIZE(batch2), 0);
154 		ASSERT_GE(ret2, 0);
155 
156 		TH_LOG("Second batch: %zd namespaces (after ns_id=%llu)",
157 		       ret2, (unsigned long long)req.ns_id);
158 
159 		/* If we got more results, verify IDs are monotonically increasing */
160 		if (ret2 > 0) {
161 			ASSERT_GT(batch2[0], batch1[ret1 - 1]);
162 			TH_LOG("Pagination working: %llu > %llu",
163 			       (unsigned long long)batch2[0],
164 			       (unsigned long long)batch1[ret1 - 1]);
165 		}
166 	} else {
167 		TH_LOG("All namespaces fit in first batch");
168 	}
169 }
170 
171 /*
172  * Test listns() with LISTNS_CURRENT_USER.
173  * List namespaces owned by current user namespace.
174  */
175 TEST(listns_current_user)
176 {
177 	struct ns_id_req req = {
178 		.size = sizeof(req),
179 		.spare = 0,
180 		.ns_id = 0,
181 		.ns_type = 0,
182 		.spare2 = 0,
183 		.user_ns_id = LISTNS_CURRENT_USER,
184 	};
185 	__u64 ns_ids[100];
186 	ssize_t ret;
187 
188 	ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
189 	if (ret < 0) {
190 		if (errno == ENOSYS)
191 			SKIP(return, "listns() not supported");
192 		TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno);
193 		ASSERT_TRUE(false);
194 	}
195 	ASSERT_GE(ret, 0);
196 
197 	/* Should find at least the initial namespaces if we're in init_user_ns */
198 	TH_LOG("Found %zd namespaces owned by current user namespace", ret);
199 
200 	for (ssize_t i = 0; i < ret; i++)
201 		TH_LOG("  [%zd] ns_id: %llu", i, (unsigned long long)ns_ids[i]);
202 }
203 
204 TEST_HARNESS_MAIN
205