1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <assert.h>
4 #include <fcntl.h>
5 #include <inttypes.h>
6 #include <libgen.h>
7 #include <limits.h>
8 #include <pthread.h>
9 #include <signal.h>
10 #include <string.h>
11 #include <sys/mount.h>
12 #include <poll.h>
13 #include <sys/epoll.h>
14 #include <sys/resource.h>
15 #include <sys/stat.h>
16 #include <sys/socket.h>
17 #include <sys/un.h>
18 #include <sys/wait.h>
19 #include <unistd.h>
20 #include <linux/fs.h>
21 #include <linux/limits.h>
22 #include <linux/nsfs.h>
23 #include "../kselftest_harness.h"
24
25 /* Fixture for tests that create child processes */
FIXTURE(nsid)26 FIXTURE(nsid) {
27 pid_t child_pid;
28 };
29
FIXTURE_SETUP(nsid)30 FIXTURE_SETUP(nsid) {
31 self->child_pid = 0;
32 }
33
FIXTURE_TEARDOWN(nsid)34 FIXTURE_TEARDOWN(nsid) {
35 /* Clean up any child process that may still be running */
36 if (self->child_pid > 0) {
37 kill(self->child_pid, SIGKILL);
38 waitpid(self->child_pid, NULL, 0);
39 }
40 }
41
TEST(nsid_mntns_basic)42 TEST(nsid_mntns_basic)
43 {
44 __u64 mnt_ns_id = 0;
45 int fd_mntns;
46 int ret;
47
48 /* Open the current mount namespace */
49 fd_mntns = open("/proc/self/ns/mnt", O_RDONLY);
50 ASSERT_GE(fd_mntns, 0);
51
52 /* Get the mount namespace ID */
53 ret = ioctl(fd_mntns, NS_GET_MNTNS_ID, &mnt_ns_id);
54 ASSERT_EQ(ret, 0);
55 ASSERT_NE(mnt_ns_id, 0);
56
57 /* Verify we can get the same ID again */
58 __u64 mnt_ns_id2 = 0;
59 ret = ioctl(fd_mntns, NS_GET_ID, &mnt_ns_id2);
60 ASSERT_EQ(ret, 0);
61 ASSERT_EQ(mnt_ns_id, mnt_ns_id2);
62
63 close(fd_mntns);
64 }
65
TEST_F(nsid,mntns_separate)66 TEST_F(nsid, mntns_separate)
67 {
68 __u64 parent_mnt_ns_id = 0;
69 __u64 child_mnt_ns_id = 0;
70 int fd_parent_mntns, fd_child_mntns;
71 int ret;
72 pid_t pid;
73 int pipefd[2];
74
75 /* Get parent's mount namespace ID */
76 fd_parent_mntns = open("/proc/self/ns/mnt", O_RDONLY);
77 ASSERT_GE(fd_parent_mntns, 0);
78 ret = ioctl(fd_parent_mntns, NS_GET_ID, &parent_mnt_ns_id);
79 ASSERT_EQ(ret, 0);
80 ASSERT_NE(parent_mnt_ns_id, 0);
81
82 /* Create a pipe for synchronization */
83 ASSERT_EQ(pipe(pipefd), 0);
84
85 pid = fork();
86 ASSERT_GE(pid, 0);
87
88 if (pid == 0) {
89 /* Child process */
90 close(pipefd[0]);
91
92 /* Create new mount namespace */
93 ret = unshare(CLONE_NEWNS);
94 if (ret != 0) {
95 /* Skip test if we don't have permission */
96 if (errno == EPERM || errno == EACCES) {
97 write(pipefd[1], "S", 1); /* Signal skip */
98 _exit(0);
99 }
100 _exit(1);
101 }
102
103 /* Signal success */
104 write(pipefd[1], "Y", 1);
105 close(pipefd[1]);
106
107 /* Keep namespace alive */
108 pause();
109 _exit(0);
110 }
111
112 /* Track child for cleanup */
113 self->child_pid = pid;
114
115 /* Parent process */
116 close(pipefd[1]);
117
118 char buf;
119 ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
120 close(pipefd[0]);
121
122 if (buf == 'S') {
123 /* Child couldn't create namespace, skip test */
124 close(fd_parent_mntns);
125 SKIP(return, "No permission to create mount namespace");
126 }
127
128 ASSERT_EQ(buf, 'Y');
129
130 /* Open child's mount namespace */
131 char path[256];
132 snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
133 fd_child_mntns = open(path, O_RDONLY);
134 ASSERT_GE(fd_child_mntns, 0);
135
136 /* Get child's mount namespace ID */
137 ret = ioctl(fd_child_mntns, NS_GET_ID, &child_mnt_ns_id);
138 ASSERT_EQ(ret, 0);
139 ASSERT_NE(child_mnt_ns_id, 0);
140
141 /* Parent and child should have different mount namespace IDs */
142 ASSERT_NE(parent_mnt_ns_id, child_mnt_ns_id);
143
144 close(fd_parent_mntns);
145 close(fd_child_mntns);
146 }
147
TEST(nsid_cgroupns_basic)148 TEST(nsid_cgroupns_basic)
149 {
150 __u64 cgroup_ns_id = 0;
151 int fd_cgroupns;
152 int ret;
153
154 /* Open the current cgroup namespace */
155 fd_cgroupns = open("/proc/self/ns/cgroup", O_RDONLY);
156 ASSERT_GE(fd_cgroupns, 0);
157
158 /* Get the cgroup namespace ID */
159 ret = ioctl(fd_cgroupns, NS_GET_ID, &cgroup_ns_id);
160 ASSERT_EQ(ret, 0);
161 ASSERT_NE(cgroup_ns_id, 0);
162
163 /* Verify we can get the same ID again */
164 __u64 cgroup_ns_id2 = 0;
165 ret = ioctl(fd_cgroupns, NS_GET_ID, &cgroup_ns_id2);
166 ASSERT_EQ(ret, 0);
167 ASSERT_EQ(cgroup_ns_id, cgroup_ns_id2);
168
169 close(fd_cgroupns);
170 }
171
TEST_F(nsid,cgroupns_separate)172 TEST_F(nsid, cgroupns_separate)
173 {
174 __u64 parent_cgroup_ns_id = 0;
175 __u64 child_cgroup_ns_id = 0;
176 int fd_parent_cgroupns, fd_child_cgroupns;
177 int ret;
178 pid_t pid;
179 int pipefd[2];
180
181 /* Get parent's cgroup namespace ID */
182 fd_parent_cgroupns = open("/proc/self/ns/cgroup", O_RDONLY);
183 ASSERT_GE(fd_parent_cgroupns, 0);
184 ret = ioctl(fd_parent_cgroupns, NS_GET_ID, &parent_cgroup_ns_id);
185 ASSERT_EQ(ret, 0);
186 ASSERT_NE(parent_cgroup_ns_id, 0);
187
188 /* Create a pipe for synchronization */
189 ASSERT_EQ(pipe(pipefd), 0);
190
191 pid = fork();
192 ASSERT_GE(pid, 0);
193
194 if (pid == 0) {
195 /* Child process */
196 close(pipefd[0]);
197
198 /* Create new cgroup namespace */
199 ret = unshare(CLONE_NEWCGROUP);
200 if (ret != 0) {
201 /* Skip test if we don't have permission */
202 if (errno == EPERM || errno == EACCES) {
203 write(pipefd[1], "S", 1); /* Signal skip */
204 _exit(0);
205 }
206 _exit(1);
207 }
208
209 /* Signal success */
210 write(pipefd[1], "Y", 1);
211 close(pipefd[1]);
212
213 /* Keep namespace alive */
214 pause();
215 _exit(0);
216 }
217
218 /* Track child for cleanup */
219 self->child_pid = pid;
220
221 /* Parent process */
222 close(pipefd[1]);
223
224 char buf;
225 ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
226 close(pipefd[0]);
227
228 if (buf == 'S') {
229 /* Child couldn't create namespace, skip test */
230 close(fd_parent_cgroupns);
231 SKIP(return, "No permission to create cgroup namespace");
232 }
233
234 ASSERT_EQ(buf, 'Y');
235
236 /* Open child's cgroup namespace */
237 char path[256];
238 snprintf(path, sizeof(path), "/proc/%d/ns/cgroup", pid);
239 fd_child_cgroupns = open(path, O_RDONLY);
240 ASSERT_GE(fd_child_cgroupns, 0);
241
242 /* Get child's cgroup namespace ID */
243 ret = ioctl(fd_child_cgroupns, NS_GET_ID, &child_cgroup_ns_id);
244 ASSERT_EQ(ret, 0);
245 ASSERT_NE(child_cgroup_ns_id, 0);
246
247 /* Parent and child should have different cgroup namespace IDs */
248 ASSERT_NE(parent_cgroup_ns_id, child_cgroup_ns_id);
249
250 close(fd_parent_cgroupns);
251 close(fd_child_cgroupns);
252 }
253
TEST(nsid_ipcns_basic)254 TEST(nsid_ipcns_basic)
255 {
256 __u64 ipc_ns_id = 0;
257 int fd_ipcns;
258 int ret;
259
260 /* Open the current IPC namespace */
261 fd_ipcns = open("/proc/self/ns/ipc", O_RDONLY);
262 ASSERT_GE(fd_ipcns, 0);
263
264 /* Get the IPC namespace ID */
265 ret = ioctl(fd_ipcns, NS_GET_ID, &ipc_ns_id);
266 ASSERT_EQ(ret, 0);
267 ASSERT_NE(ipc_ns_id, 0);
268
269 /* Verify we can get the same ID again */
270 __u64 ipc_ns_id2 = 0;
271 ret = ioctl(fd_ipcns, NS_GET_ID, &ipc_ns_id2);
272 ASSERT_EQ(ret, 0);
273 ASSERT_EQ(ipc_ns_id, ipc_ns_id2);
274
275 close(fd_ipcns);
276 }
277
TEST_F(nsid,ipcns_separate)278 TEST_F(nsid, ipcns_separate)
279 {
280 __u64 parent_ipc_ns_id = 0;
281 __u64 child_ipc_ns_id = 0;
282 int fd_parent_ipcns, fd_child_ipcns;
283 int ret;
284 pid_t pid;
285 int pipefd[2];
286
287 /* Get parent's IPC namespace ID */
288 fd_parent_ipcns = open("/proc/self/ns/ipc", O_RDONLY);
289 ASSERT_GE(fd_parent_ipcns, 0);
290 ret = ioctl(fd_parent_ipcns, NS_GET_ID, &parent_ipc_ns_id);
291 ASSERT_EQ(ret, 0);
292 ASSERT_NE(parent_ipc_ns_id, 0);
293
294 /* Create a pipe for synchronization */
295 ASSERT_EQ(pipe(pipefd), 0);
296
297 pid = fork();
298 ASSERT_GE(pid, 0);
299
300 if (pid == 0) {
301 /* Child process */
302 close(pipefd[0]);
303
304 /* Create new IPC namespace */
305 ret = unshare(CLONE_NEWIPC);
306 if (ret != 0) {
307 /* Skip test if we don't have permission */
308 if (errno == EPERM || errno == EACCES) {
309 write(pipefd[1], "S", 1); /* Signal skip */
310 _exit(0);
311 }
312 _exit(1);
313 }
314
315 /* Signal success */
316 write(pipefd[1], "Y", 1);
317 close(pipefd[1]);
318
319 /* Keep namespace alive */
320 pause();
321 _exit(0);
322 }
323
324 /* Track child for cleanup */
325 self->child_pid = pid;
326
327 /* Parent process */
328 close(pipefd[1]);
329
330 char buf;
331 ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
332 close(pipefd[0]);
333
334 if (buf == 'S') {
335 /* Child couldn't create namespace, skip test */
336 close(fd_parent_ipcns);
337 SKIP(return, "No permission to create IPC namespace");
338 }
339
340 ASSERT_EQ(buf, 'Y');
341
342 /* Open child's IPC namespace */
343 char path[256];
344 snprintf(path, sizeof(path), "/proc/%d/ns/ipc", pid);
345 fd_child_ipcns = open(path, O_RDONLY);
346 ASSERT_GE(fd_child_ipcns, 0);
347
348 /* Get child's IPC namespace ID */
349 ret = ioctl(fd_child_ipcns, NS_GET_ID, &child_ipc_ns_id);
350 ASSERT_EQ(ret, 0);
351 ASSERT_NE(child_ipc_ns_id, 0);
352
353 /* Parent and child should have different IPC namespace IDs */
354 ASSERT_NE(parent_ipc_ns_id, child_ipc_ns_id);
355
356 close(fd_parent_ipcns);
357 close(fd_child_ipcns);
358 }
359
TEST(nsid_utsns_basic)360 TEST(nsid_utsns_basic)
361 {
362 __u64 uts_ns_id = 0;
363 int fd_utsns;
364 int ret;
365
366 /* Open the current UTS namespace */
367 fd_utsns = open("/proc/self/ns/uts", O_RDONLY);
368 ASSERT_GE(fd_utsns, 0);
369
370 /* Get the UTS namespace ID */
371 ret = ioctl(fd_utsns, NS_GET_ID, &uts_ns_id);
372 ASSERT_EQ(ret, 0);
373 ASSERT_NE(uts_ns_id, 0);
374
375 /* Verify we can get the same ID again */
376 __u64 uts_ns_id2 = 0;
377 ret = ioctl(fd_utsns, NS_GET_ID, &uts_ns_id2);
378 ASSERT_EQ(ret, 0);
379 ASSERT_EQ(uts_ns_id, uts_ns_id2);
380
381 close(fd_utsns);
382 }
383
TEST_F(nsid,utsns_separate)384 TEST_F(nsid, utsns_separate)
385 {
386 __u64 parent_uts_ns_id = 0;
387 __u64 child_uts_ns_id = 0;
388 int fd_parent_utsns, fd_child_utsns;
389 int ret;
390 pid_t pid;
391 int pipefd[2];
392
393 /* Get parent's UTS namespace ID */
394 fd_parent_utsns = open("/proc/self/ns/uts", O_RDONLY);
395 ASSERT_GE(fd_parent_utsns, 0);
396 ret = ioctl(fd_parent_utsns, NS_GET_ID, &parent_uts_ns_id);
397 ASSERT_EQ(ret, 0);
398 ASSERT_NE(parent_uts_ns_id, 0);
399
400 /* Create a pipe for synchronization */
401 ASSERT_EQ(pipe(pipefd), 0);
402
403 pid = fork();
404 ASSERT_GE(pid, 0);
405
406 if (pid == 0) {
407 /* Child process */
408 close(pipefd[0]);
409
410 /* Create new UTS namespace */
411 ret = unshare(CLONE_NEWUTS);
412 if (ret != 0) {
413 /* Skip test if we don't have permission */
414 if (errno == EPERM || errno == EACCES) {
415 write(pipefd[1], "S", 1); /* Signal skip */
416 _exit(0);
417 }
418 _exit(1);
419 }
420
421 /* Signal success */
422 write(pipefd[1], "Y", 1);
423 close(pipefd[1]);
424
425 /* Keep namespace alive */
426 pause();
427 _exit(0);
428 }
429
430 /* Track child for cleanup */
431 self->child_pid = pid;
432
433 /* Parent process */
434 close(pipefd[1]);
435
436 char buf;
437 ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
438 close(pipefd[0]);
439
440 if (buf == 'S') {
441 /* Child couldn't create namespace, skip test */
442 close(fd_parent_utsns);
443 SKIP(return, "No permission to create UTS namespace");
444 }
445
446 ASSERT_EQ(buf, 'Y');
447
448 /* Open child's UTS namespace */
449 char path[256];
450 snprintf(path, sizeof(path), "/proc/%d/ns/uts", pid);
451 fd_child_utsns = open(path, O_RDONLY);
452 ASSERT_GE(fd_child_utsns, 0);
453
454 /* Get child's UTS namespace ID */
455 ret = ioctl(fd_child_utsns, NS_GET_ID, &child_uts_ns_id);
456 ASSERT_EQ(ret, 0);
457 ASSERT_NE(child_uts_ns_id, 0);
458
459 /* Parent and child should have different UTS namespace IDs */
460 ASSERT_NE(parent_uts_ns_id, child_uts_ns_id);
461
462 close(fd_parent_utsns);
463 close(fd_child_utsns);
464 }
465
TEST(nsid_userns_basic)466 TEST(nsid_userns_basic)
467 {
468 __u64 user_ns_id = 0;
469 int fd_userns;
470 int ret;
471
472 /* Open the current user namespace */
473 fd_userns = open("/proc/self/ns/user", O_RDONLY);
474 ASSERT_GE(fd_userns, 0);
475
476 /* Get the user namespace ID */
477 ret = ioctl(fd_userns, NS_GET_ID, &user_ns_id);
478 ASSERT_EQ(ret, 0);
479 ASSERT_NE(user_ns_id, 0);
480
481 /* Verify we can get the same ID again */
482 __u64 user_ns_id2 = 0;
483 ret = ioctl(fd_userns, NS_GET_ID, &user_ns_id2);
484 ASSERT_EQ(ret, 0);
485 ASSERT_EQ(user_ns_id, user_ns_id2);
486
487 close(fd_userns);
488 }
489
TEST_F(nsid,userns_separate)490 TEST_F(nsid, userns_separate)
491 {
492 __u64 parent_user_ns_id = 0;
493 __u64 child_user_ns_id = 0;
494 int fd_parent_userns, fd_child_userns;
495 int ret;
496 pid_t pid;
497 int pipefd[2];
498
499 /* Get parent's user namespace ID */
500 fd_parent_userns = open("/proc/self/ns/user", O_RDONLY);
501 ASSERT_GE(fd_parent_userns, 0);
502 ret = ioctl(fd_parent_userns, NS_GET_ID, &parent_user_ns_id);
503 ASSERT_EQ(ret, 0);
504 ASSERT_NE(parent_user_ns_id, 0);
505
506 /* Create a pipe for synchronization */
507 ASSERT_EQ(pipe(pipefd), 0);
508
509 pid = fork();
510 ASSERT_GE(pid, 0);
511
512 if (pid == 0) {
513 /* Child process */
514 close(pipefd[0]);
515
516 /* Create new user namespace */
517 ret = unshare(CLONE_NEWUSER);
518 if (ret != 0) {
519 /* Skip test if we don't have permission */
520 if (errno == EPERM || errno == EACCES) {
521 write(pipefd[1], "S", 1); /* Signal skip */
522 _exit(0);
523 }
524 _exit(1);
525 }
526
527 /* Signal success */
528 write(pipefd[1], "Y", 1);
529 close(pipefd[1]);
530
531 /* Keep namespace alive */
532 pause();
533 _exit(0);
534 }
535
536 /* Track child for cleanup */
537 self->child_pid = pid;
538
539 /* Parent process */
540 close(pipefd[1]);
541
542 char buf;
543 ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
544 close(pipefd[0]);
545
546 if (buf == 'S') {
547 /* Child couldn't create namespace, skip test */
548 close(fd_parent_userns);
549 SKIP(return, "No permission to create user namespace");
550 }
551
552 ASSERT_EQ(buf, 'Y');
553
554 /* Open child's user namespace */
555 char path[256];
556 snprintf(path, sizeof(path), "/proc/%d/ns/user", pid);
557 fd_child_userns = open(path, O_RDONLY);
558 ASSERT_GE(fd_child_userns, 0);
559
560 /* Get child's user namespace ID */
561 ret = ioctl(fd_child_userns, NS_GET_ID, &child_user_ns_id);
562 ASSERT_EQ(ret, 0);
563 ASSERT_NE(child_user_ns_id, 0);
564
565 /* Parent and child should have different user namespace IDs */
566 ASSERT_NE(parent_user_ns_id, child_user_ns_id);
567
568 close(fd_parent_userns);
569 close(fd_child_userns);
570 }
571
TEST(nsid_timens_basic)572 TEST(nsid_timens_basic)
573 {
574 __u64 time_ns_id = 0;
575 int fd_timens;
576 int ret;
577
578 /* Open the current time namespace */
579 fd_timens = open("/proc/self/ns/time", O_RDONLY);
580 if (fd_timens < 0) {
581 SKIP(return, "Time namespaces not supported");
582 }
583
584 /* Get the time namespace ID */
585 ret = ioctl(fd_timens, NS_GET_ID, &time_ns_id);
586 ASSERT_EQ(ret, 0);
587 ASSERT_NE(time_ns_id, 0);
588
589 /* Verify we can get the same ID again */
590 __u64 time_ns_id2 = 0;
591 ret = ioctl(fd_timens, NS_GET_ID, &time_ns_id2);
592 ASSERT_EQ(ret, 0);
593 ASSERT_EQ(time_ns_id, time_ns_id2);
594
595 close(fd_timens);
596 }
597
TEST_F(nsid,timens_separate)598 TEST_F(nsid, timens_separate)
599 {
600 __u64 parent_time_ns_id = 0;
601 __u64 child_time_ns_id = 0;
602 int fd_parent_timens, fd_child_timens;
603 int ret;
604 pid_t pid;
605 int pipefd[2];
606
607 /* Open the current time namespace */
608 fd_parent_timens = open("/proc/self/ns/time", O_RDONLY);
609 if (fd_parent_timens < 0) {
610 SKIP(return, "Time namespaces not supported");
611 }
612
613 /* Get parent's time namespace ID */
614 ret = ioctl(fd_parent_timens, NS_GET_ID, &parent_time_ns_id);
615 ASSERT_EQ(ret, 0);
616 ASSERT_NE(parent_time_ns_id, 0);
617
618 /* Create a pipe for synchronization */
619 ASSERT_EQ(pipe(pipefd), 0);
620
621 pid = fork();
622 ASSERT_GE(pid, 0);
623
624 if (pid == 0) {
625 /* Child process */
626 close(pipefd[0]);
627
628 /* Create new time namespace */
629 ret = unshare(CLONE_NEWTIME);
630 if (ret != 0) {
631 /* Skip test if we don't have permission */
632 if (errno == EPERM || errno == EACCES || errno == EINVAL) {
633 write(pipefd[1], "S", 1); /* Signal skip */
634 _exit(0);
635 }
636 _exit(1);
637 }
638
639 /* Fork a grandchild to actually enter the new namespace */
640 pid_t grandchild = fork();
641 if (grandchild == 0) {
642 /* Grandchild is in the new namespace */
643 write(pipefd[1], "Y", 1);
644 close(pipefd[1]);
645 pause();
646 _exit(0);
647 } else if (grandchild > 0) {
648 /* Child writes grandchild PID and waits */
649 write(pipefd[1], "Y", 1);
650 write(pipefd[1], &grandchild, sizeof(grandchild));
651 close(pipefd[1]);
652 pause(); /* Keep the parent alive to maintain the grandchild */
653 _exit(0);
654 } else {
655 _exit(1);
656 }
657 }
658
659 /* Track child for cleanup */
660 self->child_pid = pid;
661
662 /* Parent process */
663 close(pipefd[1]);
664
665 char buf;
666 ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
667
668 if (buf == 'S') {
669 /* Child couldn't create namespace, skip test */
670 close(fd_parent_timens);
671 close(pipefd[0]);
672 SKIP(return, "Cannot create time namespace");
673 }
674
675 ASSERT_EQ(buf, 'Y');
676
677 pid_t grandchild_pid;
678 ASSERT_EQ(read(pipefd[0], &grandchild_pid, sizeof(grandchild_pid)), sizeof(grandchild_pid));
679 close(pipefd[0]);
680
681 /* Open grandchild's time namespace */
682 char path[256];
683 snprintf(path, sizeof(path), "/proc/%d/ns/time", grandchild_pid);
684 fd_child_timens = open(path, O_RDONLY);
685 ASSERT_GE(fd_child_timens, 0);
686
687 /* Get child's time namespace ID */
688 ret = ioctl(fd_child_timens, NS_GET_ID, &child_time_ns_id);
689 ASSERT_EQ(ret, 0);
690 ASSERT_NE(child_time_ns_id, 0);
691
692 /* Parent and child should have different time namespace IDs */
693 ASSERT_NE(parent_time_ns_id, child_time_ns_id);
694
695 close(fd_parent_timens);
696 close(fd_child_timens);
697 }
698
TEST(nsid_pidns_basic)699 TEST(nsid_pidns_basic)
700 {
701 __u64 pid_ns_id = 0;
702 int fd_pidns;
703 int ret;
704
705 /* Open the current PID namespace */
706 fd_pidns = open("/proc/self/ns/pid", O_RDONLY);
707 ASSERT_GE(fd_pidns, 0);
708
709 /* Get the PID namespace ID */
710 ret = ioctl(fd_pidns, NS_GET_ID, &pid_ns_id);
711 ASSERT_EQ(ret, 0);
712 ASSERT_NE(pid_ns_id, 0);
713
714 /* Verify we can get the same ID again */
715 __u64 pid_ns_id2 = 0;
716 ret = ioctl(fd_pidns, NS_GET_ID, &pid_ns_id2);
717 ASSERT_EQ(ret, 0);
718 ASSERT_EQ(pid_ns_id, pid_ns_id2);
719
720 close(fd_pidns);
721 }
722
TEST_F(nsid,pidns_separate)723 TEST_F(nsid, pidns_separate)
724 {
725 __u64 parent_pid_ns_id = 0;
726 __u64 child_pid_ns_id = 0;
727 int fd_parent_pidns, fd_child_pidns;
728 int ret;
729 pid_t pid;
730 int pipefd[2];
731
732 /* Get parent's PID namespace ID */
733 fd_parent_pidns = open("/proc/self/ns/pid", O_RDONLY);
734 ASSERT_GE(fd_parent_pidns, 0);
735 ret = ioctl(fd_parent_pidns, NS_GET_ID, &parent_pid_ns_id);
736 ASSERT_EQ(ret, 0);
737 ASSERT_NE(parent_pid_ns_id, 0);
738
739 /* Create a pipe for synchronization */
740 ASSERT_EQ(pipe(pipefd), 0);
741
742 pid = fork();
743 ASSERT_GE(pid, 0);
744
745 if (pid == 0) {
746 /* Child process */
747 close(pipefd[0]);
748
749 /* Create new PID namespace */
750 ret = unshare(CLONE_NEWPID);
751 if (ret != 0) {
752 /* Skip test if we don't have permission */
753 if (errno == EPERM || errno == EACCES) {
754 write(pipefd[1], "S", 1); /* Signal skip */
755 _exit(0);
756 }
757 _exit(1);
758 }
759
760 /* Fork a grandchild to actually enter the new namespace */
761 pid_t grandchild = fork();
762 if (grandchild == 0) {
763 /* Grandchild is in the new namespace */
764 write(pipefd[1], "Y", 1);
765 close(pipefd[1]);
766 pause();
767 _exit(0);
768 } else if (grandchild > 0) {
769 /* Child writes grandchild PID and waits */
770 write(pipefd[1], "Y", 1);
771 write(pipefd[1], &grandchild, sizeof(grandchild));
772 close(pipefd[1]);
773 pause(); /* Keep the parent alive to maintain the grandchild */
774 _exit(0);
775 } else {
776 _exit(1);
777 }
778 }
779
780 /* Track child for cleanup */
781 self->child_pid = pid;
782
783 /* Parent process */
784 close(pipefd[1]);
785
786 char buf;
787 ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
788
789 if (buf == 'S') {
790 /* Child couldn't create namespace, skip test */
791 close(fd_parent_pidns);
792 close(pipefd[0]);
793 SKIP(return, "No permission to create PID namespace");
794 }
795
796 ASSERT_EQ(buf, 'Y');
797
798 pid_t grandchild_pid;
799 ASSERT_EQ(read(pipefd[0], &grandchild_pid, sizeof(grandchild_pid)), sizeof(grandchild_pid));
800 close(pipefd[0]);
801
802 /* Open grandchild's PID namespace */
803 char path[256];
804 snprintf(path, sizeof(path), "/proc/%d/ns/pid", grandchild_pid);
805 fd_child_pidns = open(path, O_RDONLY);
806 ASSERT_GE(fd_child_pidns, 0);
807
808 /* Get child's PID namespace ID */
809 ret = ioctl(fd_child_pidns, NS_GET_ID, &child_pid_ns_id);
810 ASSERT_EQ(ret, 0);
811 ASSERT_NE(child_pid_ns_id, 0);
812
813 /* Parent and child should have different PID namespace IDs */
814 ASSERT_NE(parent_pid_ns_id, child_pid_ns_id);
815
816 close(fd_parent_pidns);
817 close(fd_child_pidns);
818 }
819
TEST(nsid_netns_basic)820 TEST(nsid_netns_basic)
821 {
822 __u64 net_ns_id = 0;
823 __u64 netns_cookie = 0;
824 int fd_netns;
825 int sock;
826 socklen_t optlen;
827 int ret;
828
829 /* Open the current network namespace */
830 fd_netns = open("/proc/self/ns/net", O_RDONLY);
831 ASSERT_GE(fd_netns, 0);
832
833 /* Get the network namespace ID via ioctl */
834 ret = ioctl(fd_netns, NS_GET_ID, &net_ns_id);
835 ASSERT_EQ(ret, 0);
836 ASSERT_NE(net_ns_id, 0);
837
838 /* Create a socket to get the SO_NETNS_COOKIE */
839 sock = socket(AF_UNIX, SOCK_STREAM, 0);
840 ASSERT_GE(sock, 0);
841
842 /* Get the network namespace cookie via socket option */
843 optlen = sizeof(netns_cookie);
844 ret = getsockopt(sock, SOL_SOCKET, SO_NETNS_COOKIE, &netns_cookie, &optlen);
845 ASSERT_EQ(ret, 0);
846 ASSERT_EQ(optlen, sizeof(netns_cookie));
847
848 /* The namespace ID and cookie should be identical */
849 ASSERT_EQ(net_ns_id, netns_cookie);
850
851 /* Verify we can get the same ID again */
852 __u64 net_ns_id2 = 0;
853 ret = ioctl(fd_netns, NS_GET_ID, &net_ns_id2);
854 ASSERT_EQ(ret, 0);
855 ASSERT_EQ(net_ns_id, net_ns_id2);
856
857 close(sock);
858 close(fd_netns);
859 }
860
TEST_F(nsid,netns_separate)861 TEST_F(nsid, netns_separate)
862 {
863 __u64 parent_net_ns_id = 0;
864 __u64 parent_netns_cookie = 0;
865 __u64 child_net_ns_id = 0;
866 __u64 child_netns_cookie = 0;
867 int fd_parent_netns, fd_child_netns;
868 int parent_sock, child_sock;
869 socklen_t optlen;
870 int ret;
871 pid_t pid;
872 int pipefd[2];
873
874 /* Get parent's network namespace ID */
875 fd_parent_netns = open("/proc/self/ns/net", O_RDONLY);
876 ASSERT_GE(fd_parent_netns, 0);
877 ret = ioctl(fd_parent_netns, NS_GET_ID, &parent_net_ns_id);
878 ASSERT_EQ(ret, 0);
879 ASSERT_NE(parent_net_ns_id, 0);
880
881 /* Get parent's network namespace cookie */
882 parent_sock = socket(AF_UNIX, SOCK_STREAM, 0);
883 ASSERT_GE(parent_sock, 0);
884 optlen = sizeof(parent_netns_cookie);
885 ret = getsockopt(parent_sock, SOL_SOCKET, SO_NETNS_COOKIE, &parent_netns_cookie, &optlen);
886 ASSERT_EQ(ret, 0);
887
888 /* Verify parent's ID and cookie match */
889 ASSERT_EQ(parent_net_ns_id, parent_netns_cookie);
890
891 /* Create a pipe for synchronization */
892 ASSERT_EQ(pipe(pipefd), 0);
893
894 pid = fork();
895 ASSERT_GE(pid, 0);
896
897 if (pid == 0) {
898 /* Child process */
899 close(pipefd[0]);
900
901 /* Create new network namespace */
902 ret = unshare(CLONE_NEWNET);
903 if (ret != 0) {
904 /* Skip test if we don't have permission */
905 if (errno == EPERM || errno == EACCES) {
906 write(pipefd[1], "S", 1); /* Signal skip */
907 _exit(0);
908 }
909 _exit(1);
910 }
911
912 /* Signal success */
913 write(pipefd[1], "Y", 1);
914 close(pipefd[1]);
915
916 /* Keep namespace alive */
917 pause();
918 _exit(0);
919 }
920
921 /* Track child for cleanup */
922 self->child_pid = pid;
923
924 /* Parent process */
925 close(pipefd[1]);
926
927 char buf;
928 ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
929 close(pipefd[0]);
930
931 if (buf == 'S') {
932 /* Child couldn't create namespace, skip test */
933 close(fd_parent_netns);
934 close(parent_sock);
935 SKIP(return, "No permission to create network namespace");
936 }
937
938 ASSERT_EQ(buf, 'Y');
939
940 /* Open child's network namespace */
941 char path[256];
942 snprintf(path, sizeof(path), "/proc/%d/ns/net", pid);
943 fd_child_netns = open(path, O_RDONLY);
944 ASSERT_GE(fd_child_netns, 0);
945
946 /* Get child's network namespace ID */
947 ret = ioctl(fd_child_netns, NS_GET_ID, &child_net_ns_id);
948 ASSERT_EQ(ret, 0);
949 ASSERT_NE(child_net_ns_id, 0);
950
951 /* Create socket in child's namespace to get cookie */
952 ret = setns(fd_child_netns, CLONE_NEWNET);
953 if (ret == 0) {
954 child_sock = socket(AF_UNIX, SOCK_STREAM, 0);
955 ASSERT_GE(child_sock, 0);
956
957 optlen = sizeof(child_netns_cookie);
958 ret = getsockopt(child_sock, SOL_SOCKET, SO_NETNS_COOKIE, &child_netns_cookie, &optlen);
959 ASSERT_EQ(ret, 0);
960
961 /* Verify child's ID and cookie match */
962 ASSERT_EQ(child_net_ns_id, child_netns_cookie);
963
964 close(child_sock);
965
966 /* Return to parent namespace */
967 setns(fd_parent_netns, CLONE_NEWNET);
968 }
969
970 /* Parent and child should have different network namespace IDs */
971 ASSERT_NE(parent_net_ns_id, child_net_ns_id);
972 if (child_netns_cookie != 0) {
973 ASSERT_NE(parent_netns_cookie, child_netns_cookie);
974 }
975
976 close(fd_parent_netns);
977 close(fd_child_netns);
978 close(parent_sock);
979 }
980
981 TEST_HARNESS_MAIN
982