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 /* 205 * Test that listns() only returns active namespaces. 206 * Create a namespace, let it become inactive, verify it's not listed. 207 */ 208 TEST(listns_only_active) 209 { 210 struct ns_id_req req = { 211 .size = sizeof(req), 212 .spare = 0, 213 .ns_id = 0, 214 .ns_type = CLONE_NEWNET, 215 .spare2 = 0, 216 .user_ns_id = 0, 217 }; 218 __u64 ns_ids_before[100], ns_ids_after[100]; 219 ssize_t ret_before, ret_after; 220 int pipefd[2]; 221 pid_t pid; 222 __u64 new_ns_id = 0; 223 int status; 224 225 /* Get initial list */ 226 ret_before = sys_listns(&req, ns_ids_before, ARRAY_SIZE(ns_ids_before), 0); 227 if (ret_before < 0) { 228 if (errno == ENOSYS) 229 SKIP(return, "listns() not supported"); 230 TH_LOG("listns failed: %s (errno=%d)", strerror(errno), errno); 231 ASSERT_TRUE(false); 232 } 233 ASSERT_GE(ret_before, 0); 234 235 TH_LOG("Before: %zd active network namespaces", ret_before); 236 237 /* Create a new namespace in a child process and get its ID */ 238 ASSERT_EQ(pipe(pipefd), 0); 239 240 pid = fork(); 241 ASSERT_GE(pid, 0); 242 243 if (pid == 0) { 244 int fd; 245 __u64 ns_id; 246 247 close(pipefd[0]); 248 249 /* Create new network namespace */ 250 if (unshare(CLONE_NEWNET) < 0) { 251 close(pipefd[1]); 252 exit(1); 253 } 254 255 /* Get its ID */ 256 fd = open("/proc/self/ns/net", O_RDONLY); 257 if (fd < 0) { 258 close(pipefd[1]); 259 exit(1); 260 } 261 262 if (ioctl(fd, NS_GET_ID, &ns_id) < 0) { 263 close(fd); 264 close(pipefd[1]); 265 exit(1); 266 } 267 close(fd); 268 269 /* Send ID to parent */ 270 write(pipefd[1], &ns_id, sizeof(ns_id)); 271 close(pipefd[1]); 272 273 /* Keep namespace active briefly */ 274 usleep(100000); 275 exit(0); 276 } 277 278 /* Parent reads the new namespace ID */ 279 { 280 int bytes; 281 282 close(pipefd[1]); 283 bytes = read(pipefd[0], &new_ns_id, sizeof(new_ns_id)); 284 close(pipefd[0]); 285 286 if (bytes == sizeof(new_ns_id)) { 287 __u64 ns_ids_during[100]; 288 int ret_during; 289 290 TH_LOG("Child created namespace with ID %llu", (unsigned long long)new_ns_id); 291 292 /* List namespaces while child is still alive - should see new one */ 293 ret_during = sys_listns(&req, ns_ids_during, ARRAY_SIZE(ns_ids_during), 0); 294 ASSERT_GE(ret_during, 0); 295 TH_LOG("During: %d active network namespaces", ret_during); 296 297 /* Should have more namespaces than before */ 298 ASSERT_GE(ret_during, ret_before); 299 } 300 } 301 302 /* Wait for child to exit */ 303 waitpid(pid, &status, 0); 304 305 /* Give time for namespace to become inactive */ 306 usleep(100000); 307 308 /* List namespaces after child exits - should not see new one */ 309 ret_after = sys_listns(&req, ns_ids_after, ARRAY_SIZE(ns_ids_after), 0); 310 ASSERT_GE(ret_after, 0); 311 TH_LOG("After: %zd active network namespaces", ret_after); 312 313 /* Verify the new namespace ID is not in the after list */ 314 if (new_ns_id != 0) { 315 bool found = false; 316 317 for (ssize_t i = 0; i < ret_after; i++) { 318 if (ns_ids_after[i] == new_ns_id) { 319 found = true; 320 break; 321 } 322 } 323 ASSERT_FALSE(found); 324 } 325 } 326 327 TEST_HARNESS_MAIN 328