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 <sys/ioctl.h>
11 #include <sys/socket.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <unistd.h>
16 #include <linux/if.h>
17 #include <linux/sockios.h>
18 #include <linux/nsfs.h>
19 #include <arpa/inet.h>
20 #include "../kselftest_harness.h"
21 #include "../filesystems/utils.h"
22 #include "wrappers.h"
23
24 #ifndef SIOCGSKNS
25 #define SIOCGSKNS 0x894C
26 #endif
27
28 #ifndef FD_NSFS_ROOT
29 #define FD_NSFS_ROOT -10003
30 #endif
31
32 #ifndef FILEID_NSFS
33 #define FILEID_NSFS 0xf1
34 #endif
35
36 /*
37 * Test basic SIOCGSKNS functionality.
38 * Create a socket and verify SIOCGSKNS returns the correct network namespace.
39 */
TEST(siocgskns_basic)40 TEST(siocgskns_basic)
41 {
42 int sock_fd, netns_fd, current_netns_fd;
43 struct stat st1, st2;
44
45 /* Create a TCP socket */
46 sock_fd = socket(AF_INET, SOCK_STREAM, 0);
47 ASSERT_GE(sock_fd, 0);
48
49 /* Use SIOCGSKNS to get network namespace */
50 netns_fd = ioctl(sock_fd, SIOCGSKNS);
51 if (netns_fd < 0) {
52 close(sock_fd);
53 if (errno == ENOTTY || errno == EINVAL)
54 SKIP(return, "SIOCGSKNS not supported");
55 ASSERT_GE(netns_fd, 0);
56 }
57
58 /* Get current network namespace */
59 current_netns_fd = open("/proc/self/ns/net", O_RDONLY);
60 ASSERT_GE(current_netns_fd, 0);
61
62 /* Verify they match */
63 ASSERT_EQ(fstat(netns_fd, &st1), 0);
64 ASSERT_EQ(fstat(current_netns_fd, &st2), 0);
65 ASSERT_EQ(st1.st_ino, st2.st_ino);
66
67 close(sock_fd);
68 close(netns_fd);
69 close(current_netns_fd);
70 }
71
72 /*
73 * Test that socket file descriptors keep network namespaces active.
74 * Create a network namespace, create a socket in it, then exit the namespace.
75 * The namespace should remain active while the socket FD is held.
76 */
TEST(siocgskns_keeps_netns_active)77 TEST(siocgskns_keeps_netns_active)
78 {
79 int sock_fd, netns_fd, test_fd;
80 int ipc_sockets[2];
81 pid_t pid;
82 int status;
83 struct stat st;
84
85 EXPECT_EQ(socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0);
86
87 pid = fork();
88 ASSERT_GE(pid, 0);
89
90 if (pid == 0) {
91 /* Child: create new netns and socket */
92 close(ipc_sockets[0]);
93
94 if (unshare(CLONE_NEWNET) < 0) {
95 TH_LOG("unshare(CLONE_NEWNET) failed: %s", strerror(errno));
96 close(ipc_sockets[1]);
97 exit(1);
98 }
99
100 /* Create a socket in the new network namespace */
101 sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
102 if (sock_fd < 0) {
103 TH_LOG("socket() failed: %s", strerror(errno));
104 close(ipc_sockets[1]);
105 exit(1);
106 }
107
108 /* Send socket FD to parent via SCM_RIGHTS */
109 struct msghdr msg = {0};
110 struct iovec iov = {0};
111 char buf[1] = {'X'};
112 char cmsg_buf[CMSG_SPACE(sizeof(int))];
113
114 iov.iov_base = buf;
115 iov.iov_len = 1;
116 msg.msg_iov = &iov;
117 msg.msg_iovlen = 1;
118 msg.msg_control = cmsg_buf;
119 msg.msg_controllen = sizeof(cmsg_buf);
120
121 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
122 cmsg->cmsg_level = SOL_SOCKET;
123 cmsg->cmsg_type = SCM_RIGHTS;
124 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
125 memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(int));
126
127 if (sendmsg(ipc_sockets[1], &msg, 0) < 0) {
128 close(sock_fd);
129 close(ipc_sockets[1]);
130 exit(1);
131 }
132
133 close(sock_fd);
134 close(ipc_sockets[1]);
135 exit(0);
136 }
137
138 /* Parent: receive socket FD */
139 close(ipc_sockets[1]);
140
141 struct msghdr msg = {0};
142 struct iovec iov = {0};
143 char buf[1];
144 char cmsg_buf[CMSG_SPACE(sizeof(int))];
145
146 iov.iov_base = buf;
147 iov.iov_len = 1;
148 msg.msg_iov = &iov;
149 msg.msg_iovlen = 1;
150 msg.msg_control = cmsg_buf;
151 msg.msg_controllen = sizeof(cmsg_buf);
152
153 ssize_t n = recvmsg(ipc_sockets[0], &msg, 0);
154 close(ipc_sockets[0]);
155 ASSERT_EQ(n, 1);
156
157 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
158 ASSERT_NE(cmsg, NULL);
159 ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS);
160
161 memcpy(&sock_fd, CMSG_DATA(cmsg), sizeof(int));
162
163 /* Wait for child to exit */
164 waitpid(pid, &status, 0);
165 ASSERT_TRUE(WIFEXITED(status));
166 ASSERT_EQ(WEXITSTATUS(status), 0);
167
168 /* Get network namespace from socket */
169 netns_fd = ioctl(sock_fd, SIOCGSKNS);
170 if (netns_fd < 0) {
171 close(sock_fd);
172 if (errno == ENOTTY || errno == EINVAL)
173 SKIP(return, "SIOCGSKNS not supported");
174 ASSERT_GE(netns_fd, 0);
175 }
176
177 ASSERT_EQ(fstat(netns_fd, &st), 0);
178
179 /*
180 * Namespace should still be active because socket FD keeps it alive.
181 * Try to access it via /proc/self/fd/<fd>.
182 */
183 char path[64];
184 snprintf(path, sizeof(path), "/proc/self/fd/%d", netns_fd);
185 test_fd = open(path, O_RDONLY);
186 ASSERT_GE(test_fd, 0);
187 close(test_fd);
188 close(netns_fd);
189
190 /* Close socket - namespace should become inactive */
191 close(sock_fd);
192
193 /* Try SIOCGSKNS again - should fail since socket is closed */
194 ASSERT_LT(ioctl(sock_fd, SIOCGSKNS), 0);
195 }
196
197 /*
198 * Test SIOCGSKNS with different socket types (TCP, UDP, RAW).
199 */
TEST(siocgskns_socket_types)200 TEST(siocgskns_socket_types)
201 {
202 int sock_tcp, sock_udp, sock_raw;
203 int netns_tcp, netns_udp, netns_raw;
204 struct stat st_tcp, st_udp, st_raw;
205
206 /* TCP socket */
207 sock_tcp = socket(AF_INET, SOCK_STREAM, 0);
208 ASSERT_GE(sock_tcp, 0);
209
210 /* UDP socket */
211 sock_udp = socket(AF_INET, SOCK_DGRAM, 0);
212 ASSERT_GE(sock_udp, 0);
213
214 /* RAW socket (may require privileges) */
215 sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
216 if (sock_raw < 0 && (errno == EPERM || errno == EACCES)) {
217 sock_raw = -1; /* Skip raw socket test */
218 }
219
220 /* Test SIOCGSKNS on TCP */
221 netns_tcp = ioctl(sock_tcp, SIOCGSKNS);
222 if (netns_tcp < 0) {
223 close(sock_tcp);
224 close(sock_udp);
225 if (sock_raw >= 0) close(sock_raw);
226 if (errno == ENOTTY || errno == EINVAL)
227 SKIP(return, "SIOCGSKNS not supported");
228 ASSERT_GE(netns_tcp, 0);
229 }
230
231 /* Test SIOCGSKNS on UDP */
232 netns_udp = ioctl(sock_udp, SIOCGSKNS);
233 ASSERT_GE(netns_udp, 0);
234
235 /* Test SIOCGSKNS on RAW (if available) */
236 if (sock_raw >= 0) {
237 netns_raw = ioctl(sock_raw, SIOCGSKNS);
238 ASSERT_GE(netns_raw, 0);
239 }
240
241 /* Verify all return the same network namespace */
242 ASSERT_EQ(fstat(netns_tcp, &st_tcp), 0);
243 ASSERT_EQ(fstat(netns_udp, &st_udp), 0);
244 ASSERT_EQ(st_tcp.st_ino, st_udp.st_ino);
245
246 if (sock_raw >= 0) {
247 ASSERT_EQ(fstat(netns_raw, &st_raw), 0);
248 ASSERT_EQ(st_tcp.st_ino, st_raw.st_ino);
249 close(netns_raw);
250 close(sock_raw);
251 }
252
253 close(netns_tcp);
254 close(netns_udp);
255 close(sock_tcp);
256 close(sock_udp);
257 }
258
259 /*
260 * Test SIOCGSKNS across setns.
261 * Create a socket in netns A, switch to netns B, verify SIOCGSKNS still
262 * returns netns A.
263 */
TEST(siocgskns_across_setns)264 TEST(siocgskns_across_setns)
265 {
266 int sock_fd, netns_a_fd, netns_b_fd, result_fd;
267 struct stat st_a;
268
269 /* Get current netns (A) */
270 netns_a_fd = open("/proc/self/ns/net", O_RDONLY);
271 ASSERT_GE(netns_a_fd, 0);
272 ASSERT_EQ(fstat(netns_a_fd, &st_a), 0);
273
274 /* Create socket in netns A */
275 sock_fd = socket(AF_INET, SOCK_STREAM, 0);
276 ASSERT_GE(sock_fd, 0);
277
278 /* Create new netns (B) */
279 ASSERT_EQ(unshare(CLONE_NEWNET), 0);
280
281 netns_b_fd = open("/proc/self/ns/net", O_RDONLY);
282 ASSERT_GE(netns_b_fd, 0);
283
284 /* Get netns from socket created in A */
285 result_fd = ioctl(sock_fd, SIOCGSKNS);
286 if (result_fd < 0) {
287 close(sock_fd);
288 setns(netns_a_fd, CLONE_NEWNET);
289 close(netns_a_fd);
290 close(netns_b_fd);
291 if (errno == ENOTTY || errno == EINVAL)
292 SKIP(return, "SIOCGSKNS not supported");
293 ASSERT_GE(result_fd, 0);
294 }
295
296 /* Verify it still points to netns A */
297 struct stat st_result_stat;
298 ASSERT_EQ(fstat(result_fd, &st_result_stat), 0);
299 ASSERT_EQ(st_a.st_ino, st_result_stat.st_ino);
300
301 close(result_fd);
302 close(sock_fd);
303 close(netns_b_fd);
304
305 /* Restore original netns */
306 ASSERT_EQ(setns(netns_a_fd, CLONE_NEWNET), 0);
307 close(netns_a_fd);
308 }
309
310 /*
311 * Test SIOCGSKNS fails on non-socket file descriptors.
312 */
TEST(siocgskns_non_socket)313 TEST(siocgskns_non_socket)
314 {
315 int fd;
316 int pipefd[2];
317
318 /* Test on regular file */
319 fd = open("/dev/null", O_RDONLY);
320 ASSERT_GE(fd, 0);
321
322 ASSERT_LT(ioctl(fd, SIOCGSKNS), 0);
323 ASSERT_TRUE(errno == ENOTTY || errno == EINVAL);
324 close(fd);
325
326 /* Test on pipe */
327 ASSERT_EQ(pipe(pipefd), 0);
328
329 ASSERT_LT(ioctl(pipefd[0], SIOCGSKNS), 0);
330 ASSERT_TRUE(errno == ENOTTY || errno == EINVAL);
331
332 close(pipefd[0]);
333 close(pipefd[1]);
334 }
335
336 /*
337 * Test multiple sockets keep the same network namespace active.
338 * Create multiple sockets, verify closing some doesn't affect others.
339 */
TEST(siocgskns_multiple_sockets)340 TEST(siocgskns_multiple_sockets)
341 {
342 int socks[5];
343 int netns_fds[5];
344 int i;
345 struct stat st;
346 ino_t netns_ino;
347
348 /* Create new network namespace */
349 ASSERT_EQ(unshare(CLONE_NEWNET), 0);
350
351 /* Create multiple sockets */
352 for (i = 0; i < 5; i++) {
353 socks[i] = socket(AF_INET, SOCK_STREAM, 0);
354 ASSERT_GE(socks[i], 0);
355 }
356
357 /* Get netns from all sockets */
358 for (i = 0; i < 5; i++) {
359 netns_fds[i] = ioctl(socks[i], SIOCGSKNS);
360 if (netns_fds[i] < 0) {
361 int j;
362 for (j = 0; j <= i; j++) {
363 close(socks[j]);
364 if (j < i && netns_fds[j] >= 0)
365 close(netns_fds[j]);
366 }
367 if (errno == ENOTTY || errno == EINVAL)
368 SKIP(return, "SIOCGSKNS not supported");
369 ASSERT_GE(netns_fds[i], 0);
370 }
371 }
372
373 /* Verify all point to same netns */
374 ASSERT_EQ(fstat(netns_fds[0], &st), 0);
375 netns_ino = st.st_ino;
376
377 for (i = 1; i < 5; i++) {
378 ASSERT_EQ(fstat(netns_fds[i], &st), 0);
379 ASSERT_EQ(st.st_ino, netns_ino);
380 }
381
382 /* Close some sockets */
383 for (i = 0; i < 3; i++) {
384 close(socks[i]);
385 }
386
387 /* Remaining netns FDs should still be valid */
388 for (i = 3; i < 5; i++) {
389 char path[64];
390 snprintf(path, sizeof(path), "/proc/self/fd/%d", netns_fds[i]);
391 int test_fd = open(path, O_RDONLY);
392 ASSERT_GE(test_fd, 0);
393 close(test_fd);
394 }
395
396 /* Cleanup */
397 for (i = 0; i < 5; i++) {
398 if (i >= 3)
399 close(socks[i]);
400 close(netns_fds[i]);
401 }
402 }
403
404 /*
405 * Test socket keeps netns active after creating process exits.
406 * Verify that as long as the socket FD exists, the namespace remains active.
407 */
TEST(siocgskns_netns_lifecycle)408 TEST(siocgskns_netns_lifecycle)
409 {
410 int sock_fd, netns_fd;
411 int ipc_sockets[2];
412 int syncpipe[2];
413 pid_t pid;
414 int status;
415 char sync_byte;
416 struct stat st;
417 ino_t netns_ino;
418
419 EXPECT_EQ(socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0);
420
421 ASSERT_EQ(pipe(syncpipe), 0);
422
423 pid = fork();
424 ASSERT_GE(pid, 0);
425
426 if (pid == 0) {
427 /* Child */
428 close(ipc_sockets[0]);
429 close(syncpipe[1]);
430
431 if (unshare(CLONE_NEWNET) < 0) {
432 close(ipc_sockets[1]);
433 close(syncpipe[0]);
434 exit(1);
435 }
436
437 sock_fd = socket(AF_INET, SOCK_STREAM, 0);
438 if (sock_fd < 0) {
439 close(ipc_sockets[1]);
440 close(syncpipe[0]);
441 exit(1);
442 }
443
444 /* Send socket to parent */
445 struct msghdr msg = {0};
446 struct iovec iov = {0};
447 char buf[1] = {'X'};
448 char cmsg_buf[CMSG_SPACE(sizeof(int))];
449
450 iov.iov_base = buf;
451 iov.iov_len = 1;
452 msg.msg_iov = &iov;
453 msg.msg_iovlen = 1;
454 msg.msg_control = cmsg_buf;
455 msg.msg_controllen = sizeof(cmsg_buf);
456
457 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
458 cmsg->cmsg_level = SOL_SOCKET;
459 cmsg->cmsg_type = SCM_RIGHTS;
460 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
461 memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(int));
462
463 if (sendmsg(ipc_sockets[1], &msg, 0) < 0) {
464 close(sock_fd);
465 close(ipc_sockets[1]);
466 close(syncpipe[0]);
467 exit(1);
468 }
469
470 close(sock_fd);
471 close(ipc_sockets[1]);
472
473 /* Wait for parent signal */
474 read(syncpipe[0], &sync_byte, 1);
475 close(syncpipe[0]);
476 exit(0);
477 }
478
479 /* Parent */
480 close(ipc_sockets[1]);
481 close(syncpipe[0]);
482
483 /* Receive socket FD */
484 struct msghdr msg = {0};
485 struct iovec iov = {0};
486 char buf[1];
487 char cmsg_buf[CMSG_SPACE(sizeof(int))];
488
489 iov.iov_base = buf;
490 iov.iov_len = 1;
491 msg.msg_iov = &iov;
492 msg.msg_iovlen = 1;
493 msg.msg_control = cmsg_buf;
494 msg.msg_controllen = sizeof(cmsg_buf);
495
496 ssize_t n = recvmsg(ipc_sockets[0], &msg, 0);
497 close(ipc_sockets[0]);
498 ASSERT_EQ(n, 1);
499
500 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
501 ASSERT_NE(cmsg, NULL);
502 memcpy(&sock_fd, CMSG_DATA(cmsg), sizeof(int));
503
504 /* Get netns from socket while child is alive */
505 netns_fd = ioctl(sock_fd, SIOCGSKNS);
506 if (netns_fd < 0) {
507 sync_byte = 'G';
508 write(syncpipe[1], &sync_byte, 1);
509 close(syncpipe[1]);
510 close(sock_fd);
511 waitpid(pid, NULL, 0);
512 if (errno == ENOTTY || errno == EINVAL)
513 SKIP(return, "SIOCGSKNS not supported");
514 ASSERT_GE(netns_fd, 0);
515 }
516 ASSERT_EQ(fstat(netns_fd, &st), 0);
517 netns_ino = st.st_ino;
518
519 /* Signal child to exit */
520 sync_byte = 'G';
521 write(syncpipe[1], &sync_byte, 1);
522 close(syncpipe[1]);
523
524 waitpid(pid, &status, 0);
525 ASSERT_TRUE(WIFEXITED(status));
526
527 /*
528 * Socket FD should still keep namespace active even after
529 * the creating process exited.
530 */
531 int test_fd = ioctl(sock_fd, SIOCGSKNS);
532 ASSERT_GE(test_fd, 0);
533
534 struct stat st_test;
535 ASSERT_EQ(fstat(test_fd, &st_test), 0);
536 ASSERT_EQ(st_test.st_ino, netns_ino);
537
538 close(test_fd);
539 close(netns_fd);
540
541 /* Close socket - namespace should become inactive */
542 close(sock_fd);
543 }
544
545 /*
546 * Test IPv6 sockets also work with SIOCGSKNS.
547 */
TEST(siocgskns_ipv6)548 TEST(siocgskns_ipv6)
549 {
550 int sock_fd, netns_fd, current_netns_fd;
551 struct stat st1, st2;
552
553 /* Create an IPv6 TCP socket */
554 sock_fd = socket(AF_INET6, SOCK_STREAM, 0);
555 ASSERT_GE(sock_fd, 0);
556
557 /* Use SIOCGSKNS */
558 netns_fd = ioctl(sock_fd, SIOCGSKNS);
559 if (netns_fd < 0) {
560 close(sock_fd);
561 if (errno == ENOTTY || errno == EINVAL)
562 SKIP(return, "SIOCGSKNS not supported");
563 ASSERT_GE(netns_fd, 0);
564 }
565
566 /* Verify it matches current namespace */
567 current_netns_fd = open("/proc/self/ns/net", O_RDONLY);
568 ASSERT_GE(current_netns_fd, 0);
569
570 ASSERT_EQ(fstat(netns_fd, &st1), 0);
571 ASSERT_EQ(fstat(current_netns_fd, &st2), 0);
572 ASSERT_EQ(st1.st_ino, st2.st_ino);
573
574 close(sock_fd);
575 close(netns_fd);
576 close(current_netns_fd);
577 }
578
579 /*
580 * Test that socket-kept netns appears in listns() output.
581 * Verify that a network namespace kept alive by a socket FD appears in
582 * listns() output even after the creating process exits, and that it
583 * disappears when the socket is closed.
584 */
TEST(siocgskns_listns_visibility)585 TEST(siocgskns_listns_visibility)
586 {
587 int sock_fd, netns_fd, owner_fd;
588 int ipc_sockets[2];
589 pid_t pid;
590 int status;
591 __u64 netns_id, owner_id;
592 struct ns_id_req req = {
593 .size = sizeof(req),
594 .spare = 0,
595 .ns_id = 0,
596 .ns_type = CLONE_NEWNET,
597 .spare2 = 0,
598 .user_ns_id = 0,
599 };
600 __u64 ns_ids[256];
601 int ret, i;
602 bool found_netns = false;
603
604 EXPECT_EQ(socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0);
605
606 pid = fork();
607 ASSERT_GE(pid, 0);
608
609 if (pid == 0) {
610 /* Child: create new netns and socket */
611 close(ipc_sockets[0]);
612
613 if (unshare(CLONE_NEWNET) < 0) {
614 close(ipc_sockets[1]);
615 exit(1);
616 }
617
618 sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
619 if (sock_fd < 0) {
620 close(ipc_sockets[1]);
621 exit(1);
622 }
623
624 /* Send socket FD to parent via SCM_RIGHTS */
625 struct msghdr msg = {0};
626 struct iovec iov = {0};
627 char buf[1] = {'X'};
628 char cmsg_buf[CMSG_SPACE(sizeof(int))];
629
630 iov.iov_base = buf;
631 iov.iov_len = 1;
632 msg.msg_iov = &iov;
633 msg.msg_iovlen = 1;
634 msg.msg_control = cmsg_buf;
635 msg.msg_controllen = sizeof(cmsg_buf);
636
637 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
638 cmsg->cmsg_level = SOL_SOCKET;
639 cmsg->cmsg_type = SCM_RIGHTS;
640 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
641 memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(int));
642
643 if (sendmsg(ipc_sockets[1], &msg, 0) < 0) {
644 close(sock_fd);
645 close(ipc_sockets[1]);
646 exit(1);
647 }
648
649 close(sock_fd);
650 close(ipc_sockets[1]);
651 exit(0);
652 }
653
654 /* Parent: receive socket FD */
655 close(ipc_sockets[1]);
656
657 struct msghdr msg = {0};
658 struct iovec iov = {0};
659 char buf[1];
660 char cmsg_buf[CMSG_SPACE(sizeof(int))];
661
662 iov.iov_base = buf;
663 iov.iov_len = 1;
664 msg.msg_iov = &iov;
665 msg.msg_iovlen = 1;
666 msg.msg_control = cmsg_buf;
667 msg.msg_controllen = sizeof(cmsg_buf);
668
669 ssize_t n = recvmsg(ipc_sockets[0], &msg, 0);
670 close(ipc_sockets[0]);
671 ASSERT_EQ(n, 1);
672
673 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
674 ASSERT_NE(cmsg, NULL);
675 memcpy(&sock_fd, CMSG_DATA(cmsg), sizeof(int));
676
677 /* Wait for child to exit */
678 waitpid(pid, &status, 0);
679 ASSERT_TRUE(WIFEXITED(status));
680 ASSERT_EQ(WEXITSTATUS(status), 0);
681
682 /* Get network namespace from socket */
683 netns_fd = ioctl(sock_fd, SIOCGSKNS);
684 if (netns_fd < 0) {
685 close(sock_fd);
686 if (errno == ENOTTY || errno == EINVAL)
687 SKIP(return, "SIOCGSKNS not supported");
688 ASSERT_GE(netns_fd, 0);
689 }
690
691 /* Get namespace ID */
692 ret = ioctl(netns_fd, NS_GET_ID, &netns_id);
693 if (ret < 0) {
694 close(sock_fd);
695 close(netns_fd);
696 if (errno == ENOTTY || errno == EINVAL)
697 SKIP(return, "NS_GET_ID not supported");
698 ASSERT_EQ(ret, 0);
699 }
700
701 /* Get owner user namespace */
702 owner_fd = ioctl(netns_fd, NS_GET_USERNS);
703 if (owner_fd < 0) {
704 close(sock_fd);
705 close(netns_fd);
706 if (errno == ENOTTY || errno == EINVAL)
707 SKIP(return, "NS_GET_USERNS not supported");
708 ASSERT_GE(owner_fd, 0);
709 }
710
711 /* Get owner namespace ID */
712 ret = ioctl(owner_fd, NS_GET_ID, &owner_id);
713 if (ret < 0) {
714 close(owner_fd);
715 close(sock_fd);
716 close(netns_fd);
717 ASSERT_EQ(ret, 0);
718 }
719 close(owner_fd);
720
721 /* Namespace should appear in listns() output */
722 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
723 if (ret < 0) {
724 close(sock_fd);
725 close(netns_fd);
726 if (errno == ENOSYS)
727 SKIP(return, "listns() not supported");
728 TH_LOG("listns failed: %s", strerror(errno));
729 ASSERT_GE(ret, 0);
730 }
731
732 /* Search for our network namespace in the list */
733 for (i = 0; i < ret; i++) {
734 if (ns_ids[i] == netns_id) {
735 found_netns = true;
736 break;
737 }
738 }
739
740 ASSERT_TRUE(found_netns);
741 TH_LOG("Found netns %llu in listns() output (kept alive by socket)", netns_id);
742
743 /* Now verify with owner filtering */
744 req.user_ns_id = owner_id;
745 found_netns = false;
746
747 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
748 ASSERT_GE(ret, 0);
749
750 for (i = 0; i < ret; i++) {
751 if (ns_ids[i] == netns_id) {
752 found_netns = true;
753 break;
754 }
755 }
756
757 ASSERT_TRUE(found_netns);
758 TH_LOG("Found netns %llu owned by userns %llu", netns_id, owner_id);
759
760 /* Close socket - namespace should become inactive and disappear from listns() */
761 close(sock_fd);
762 close(netns_fd);
763
764 /* Verify it's no longer in listns() output */
765 req.user_ns_id = 0;
766 found_netns = false;
767
768 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
769 ASSERT_GE(ret, 0);
770
771 for (i = 0; i < ret; i++) {
772 if (ns_ids[i] == netns_id) {
773 found_netns = true;
774 break;
775 }
776 }
777
778 ASSERT_FALSE(found_netns);
779 TH_LOG("Netns %llu correctly disappeared from listns() after socket closed", netns_id);
780 }
781
782 /*
783 * Test that socket-kept netns can be reopened via file handle.
784 * Verify that a network namespace kept alive by a socket FD can be
785 * reopened using file handles even after the creating process exits.
786 */
TEST(siocgskns_file_handle)787 TEST(siocgskns_file_handle)
788 {
789 int sock_fd, netns_fd, reopened_fd;
790 int ipc_sockets[2];
791 pid_t pid;
792 int status;
793 struct stat st1, st2;
794 ino_t netns_ino;
795 __u64 netns_id;
796 struct file_handle *handle;
797 struct nsfs_file_handle *nsfs_fh;
798 int ret;
799
800 /* Allocate file_handle structure for nsfs */
801 handle = malloc(sizeof(struct file_handle) + sizeof(struct nsfs_file_handle));
802 ASSERT_NE(handle, NULL);
803 handle->handle_bytes = sizeof(struct nsfs_file_handle);
804 handle->handle_type = FILEID_NSFS;
805
806 EXPECT_EQ(socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0);
807
808 pid = fork();
809 ASSERT_GE(pid, 0);
810
811 if (pid == 0) {
812 /* Child: create new netns and socket */
813 close(ipc_sockets[0]);
814
815 if (unshare(CLONE_NEWNET) < 0) {
816 close(ipc_sockets[1]);
817 exit(1);
818 }
819
820 sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
821 if (sock_fd < 0) {
822 close(ipc_sockets[1]);
823 exit(1);
824 }
825
826 /* Send socket FD to parent via SCM_RIGHTS */
827 struct msghdr msg = {0};
828 struct iovec iov = {0};
829 char buf[1] = {'X'};
830 char cmsg_buf[CMSG_SPACE(sizeof(int))];
831
832 iov.iov_base = buf;
833 iov.iov_len = 1;
834 msg.msg_iov = &iov;
835 msg.msg_iovlen = 1;
836 msg.msg_control = cmsg_buf;
837 msg.msg_controllen = sizeof(cmsg_buf);
838
839 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
840 cmsg->cmsg_level = SOL_SOCKET;
841 cmsg->cmsg_type = SCM_RIGHTS;
842 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
843 memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(int));
844
845 if (sendmsg(ipc_sockets[1], &msg, 0) < 0) {
846 close(sock_fd);
847 close(ipc_sockets[1]);
848 exit(1);
849 }
850
851 close(sock_fd);
852 close(ipc_sockets[1]);
853 exit(0);
854 }
855
856 /* Parent: receive socket FD */
857 close(ipc_sockets[1]);
858
859 struct msghdr msg = {0};
860 struct iovec iov = {0};
861 char buf[1];
862 char cmsg_buf[CMSG_SPACE(sizeof(int))];
863
864 iov.iov_base = buf;
865 iov.iov_len = 1;
866 msg.msg_iov = &iov;
867 msg.msg_iovlen = 1;
868 msg.msg_control = cmsg_buf;
869 msg.msg_controllen = sizeof(cmsg_buf);
870
871 ssize_t n = recvmsg(ipc_sockets[0], &msg, 0);
872 close(ipc_sockets[0]);
873 ASSERT_EQ(n, 1);
874
875 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
876 ASSERT_NE(cmsg, NULL);
877 memcpy(&sock_fd, CMSG_DATA(cmsg), sizeof(int));
878
879 /* Wait for child to exit */
880 waitpid(pid, &status, 0);
881 ASSERT_TRUE(WIFEXITED(status));
882 ASSERT_EQ(WEXITSTATUS(status), 0);
883
884 /* Get network namespace from socket */
885 netns_fd = ioctl(sock_fd, SIOCGSKNS);
886 if (netns_fd < 0) {
887 free(handle);
888 close(sock_fd);
889 if (errno == ENOTTY || errno == EINVAL)
890 SKIP(return, "SIOCGSKNS not supported");
891 ASSERT_GE(netns_fd, 0);
892 }
893
894 ASSERT_EQ(fstat(netns_fd, &st1), 0);
895 netns_ino = st1.st_ino;
896
897 /* Get namespace ID */
898 ret = ioctl(netns_fd, NS_GET_ID, &netns_id);
899 if (ret < 0) {
900 free(handle);
901 close(sock_fd);
902 close(netns_fd);
903 if (errno == ENOTTY || errno == EINVAL)
904 SKIP(return, "NS_GET_ID not supported");
905 ASSERT_EQ(ret, 0);
906 }
907
908 /* Construct file handle from namespace ID */
909 nsfs_fh = (struct nsfs_file_handle *)handle->f_handle;
910 nsfs_fh->ns_id = netns_id;
911 nsfs_fh->ns_type = 0; /* Type field not needed for reopening */
912 nsfs_fh->ns_inum = 0; /* Inum field not needed for reopening */
913
914 TH_LOG("Constructed file handle for netns %lu (id=%llu)", netns_ino, netns_id);
915
916 /* Reopen namespace using file handle (while socket still keeps it alive) */
917 reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
918 if (reopened_fd < 0) {
919 free(handle);
920 close(sock_fd);
921 if (errno == EOPNOTSUPP || errno == ENOSYS || errno == EBADF)
922 SKIP(return, "open_by_handle_at with FD_NSFS_ROOT not supported");
923 TH_LOG("open_by_handle_at failed: %s", strerror(errno));
924 ASSERT_GE(reopened_fd, 0);
925 }
926
927 /* Verify it's the same namespace */
928 ASSERT_EQ(fstat(reopened_fd, &st2), 0);
929 ASSERT_EQ(st1.st_ino, st2.st_ino);
930 ASSERT_EQ(st1.st_dev, st2.st_dev);
931
932 TH_LOG("Successfully reopened netns %lu via file handle", netns_ino);
933
934 close(reopened_fd);
935
936 /* Close the netns FD */
937 close(netns_fd);
938
939 /* Try to reopen via file handle - should fail since namespace is now inactive */
940 reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
941 ASSERT_LT(reopened_fd, 0);
942 TH_LOG("Correctly failed to reopen inactive netns: %s", strerror(errno));
943
944 /* Get network namespace from socket */
945 netns_fd = ioctl(sock_fd, SIOCGSKNS);
946 if (netns_fd < 0) {
947 free(handle);
948 close(sock_fd);
949 if (errno == ENOTTY || errno == EINVAL)
950 SKIP(return, "SIOCGSKNS not supported");
951 ASSERT_GE(netns_fd, 0);
952 }
953
954 /* Reopen namespace using file handle (while socket still keeps it alive) */
955 reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
956 if (reopened_fd < 0) {
957 free(handle);
958 close(sock_fd);
959 if (errno == EOPNOTSUPP || errno == ENOSYS || errno == EBADF)
960 SKIP(return, "open_by_handle_at with FD_NSFS_ROOT not supported");
961 TH_LOG("open_by_handle_at failed: %s", strerror(errno));
962 ASSERT_GE(reopened_fd, 0);
963 }
964
965 /* Verify it's the same namespace */
966 ASSERT_EQ(fstat(reopened_fd, &st2), 0);
967 ASSERT_EQ(st1.st_ino, st2.st_ino);
968 ASSERT_EQ(st1.st_dev, st2.st_dev);
969
970 TH_LOG("Successfully reopened netns %lu via file handle", netns_ino);
971
972 /* Close socket - namespace should become inactive */
973 close(sock_fd);
974 free(handle);
975 }
976
977 /*
978 * Test combined listns() and file handle operations with socket-kept netns.
979 * Create a netns, keep it alive with a socket, verify it appears in listns(),
980 * then reopen it via file handle obtained from listns() entry.
981 */
TEST(siocgskns_listns_and_file_handle)982 TEST(siocgskns_listns_and_file_handle)
983 {
984 int sock_fd, netns_fd, userns_fd, reopened_fd;
985 int ipc_sockets[2];
986 pid_t pid;
987 int status;
988 struct stat st;
989 ino_t netns_ino;
990 __u64 netns_id, userns_id;
991 struct ns_id_req req = {
992 .size = sizeof(req),
993 .spare = 0,
994 .ns_id = 0,
995 .ns_type = CLONE_NEWNET | CLONE_NEWUSER,
996 .spare2 = 0,
997 .user_ns_id = 0,
998 };
999 __u64 ns_ids[256];
1000 int ret, i;
1001 bool found_netns = false, found_userns = false;
1002 struct file_handle *handle;
1003 struct nsfs_file_handle *nsfs_fh;
1004
1005 /* Allocate file_handle structure for nsfs */
1006 handle = malloc(sizeof(struct file_handle) + sizeof(struct nsfs_file_handle));
1007 ASSERT_NE(handle, NULL);
1008 handle->handle_bytes = sizeof(struct nsfs_file_handle);
1009 handle->handle_type = FILEID_NSFS;
1010
1011 EXPECT_EQ(socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0);
1012
1013 pid = fork();
1014 ASSERT_GE(pid, 0);
1015
1016 if (pid == 0) {
1017 /* Child: create new userns and netns with socket */
1018 close(ipc_sockets[0]);
1019
1020 if (setup_userns() < 0) {
1021 close(ipc_sockets[1]);
1022 exit(1);
1023 }
1024
1025 if (unshare(CLONE_NEWNET) < 0) {
1026 close(ipc_sockets[1]);
1027 exit(1);
1028 }
1029
1030 sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
1031 if (sock_fd < 0) {
1032 close(ipc_sockets[1]);
1033 exit(1);
1034 }
1035
1036 /* Send socket FD to parent via SCM_RIGHTS */
1037 struct msghdr msg = {0};
1038 struct iovec iov = {0};
1039 char buf[1] = {'X'};
1040 char cmsg_buf[CMSG_SPACE(sizeof(int))];
1041
1042 iov.iov_base = buf;
1043 iov.iov_len = 1;
1044 msg.msg_iov = &iov;
1045 msg.msg_iovlen = 1;
1046 msg.msg_control = cmsg_buf;
1047 msg.msg_controllen = sizeof(cmsg_buf);
1048
1049 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
1050 cmsg->cmsg_level = SOL_SOCKET;
1051 cmsg->cmsg_type = SCM_RIGHTS;
1052 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
1053 memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(int));
1054
1055 if (sendmsg(ipc_sockets[1], &msg, 0) < 0) {
1056 close(sock_fd);
1057 close(ipc_sockets[1]);
1058 exit(1);
1059 }
1060
1061 close(sock_fd);
1062 close(ipc_sockets[1]);
1063 exit(0);
1064 }
1065
1066 /* Parent: receive socket FD */
1067 close(ipc_sockets[1]);
1068
1069 struct msghdr msg = {0};
1070 struct iovec iov = {0};
1071 char buf[1];
1072 char cmsg_buf[CMSG_SPACE(sizeof(int))];
1073
1074 iov.iov_base = buf;
1075 iov.iov_len = 1;
1076 msg.msg_iov = &iov;
1077 msg.msg_iovlen = 1;
1078 msg.msg_control = cmsg_buf;
1079 msg.msg_controllen = sizeof(cmsg_buf);
1080
1081 ssize_t n = recvmsg(ipc_sockets[0], &msg, 0);
1082 close(ipc_sockets[0]);
1083 ASSERT_EQ(n, 1);
1084
1085 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
1086 ASSERT_NE(cmsg, NULL);
1087 memcpy(&sock_fd, CMSG_DATA(cmsg), sizeof(int));
1088
1089 /* Wait for child to exit */
1090 waitpid(pid, &status, 0);
1091 ASSERT_TRUE(WIFEXITED(status));
1092 ASSERT_EQ(WEXITSTATUS(status), 0);
1093
1094 /* Get network namespace from socket */
1095 netns_fd = ioctl(sock_fd, SIOCGSKNS);
1096 if (netns_fd < 0) {
1097 free(handle);
1098 close(sock_fd);
1099 if (errno == ENOTTY || errno == EINVAL)
1100 SKIP(return, "SIOCGSKNS not supported");
1101 ASSERT_GE(netns_fd, 0);
1102 }
1103
1104 ASSERT_EQ(fstat(netns_fd, &st), 0);
1105 netns_ino = st.st_ino;
1106
1107 /* Get namespace ID */
1108 ret = ioctl(netns_fd, NS_GET_ID, &netns_id);
1109 if (ret < 0) {
1110 free(handle);
1111 close(sock_fd);
1112 close(netns_fd);
1113 if (errno == ENOTTY || errno == EINVAL)
1114 SKIP(return, "NS_GET_ID not supported");
1115 ASSERT_EQ(ret, 0);
1116 }
1117
1118 /* Get owner user namespace */
1119 userns_fd = ioctl(netns_fd, NS_GET_USERNS);
1120 if (userns_fd < 0) {
1121 free(handle);
1122 close(sock_fd);
1123 close(netns_fd);
1124 if (errno == ENOTTY || errno == EINVAL)
1125 SKIP(return, "NS_GET_USERNS not supported");
1126 ASSERT_GE(userns_fd, 0);
1127 }
1128
1129 /* Get owner namespace ID */
1130 ret = ioctl(userns_fd, NS_GET_ID, &userns_id);
1131 if (ret < 0) {
1132 close(userns_fd);
1133 free(handle);
1134 close(sock_fd);
1135 close(netns_fd);
1136 ASSERT_EQ(ret, 0);
1137 }
1138 close(userns_fd);
1139
1140 TH_LOG("Testing netns %lu (id=%llu) owned by userns id=%llu", netns_ino, netns_id, userns_id);
1141
1142 /* Verify namespace appears in listns() */
1143 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
1144 if (ret < 0) {
1145 free(handle);
1146 close(sock_fd);
1147 close(netns_fd);
1148 if (errno == ENOSYS)
1149 SKIP(return, "listns() not supported");
1150 TH_LOG("listns failed: %s", strerror(errno));
1151 ASSERT_GE(ret, 0);
1152 }
1153
1154 found_netns = false;
1155 found_userns = false;
1156 for (i = 0; i < ret; i++) {
1157 if (ns_ids[i] == netns_id)
1158 found_netns = true;
1159 if (ns_ids[i] == userns_id)
1160 found_userns = true;
1161 }
1162 ASSERT_TRUE(found_netns);
1163 ASSERT_TRUE(found_userns);
1164 TH_LOG("Found netns %llu in listns() output", netns_id);
1165
1166 /* Construct file handle from namespace ID */
1167 nsfs_fh = (struct nsfs_file_handle *)handle->f_handle;
1168 nsfs_fh->ns_id = netns_id;
1169 nsfs_fh->ns_type = 0;
1170 nsfs_fh->ns_inum = 0;
1171
1172 reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1173 if (reopened_fd < 0) {
1174 free(handle);
1175 close(sock_fd);
1176 if (errno == EOPNOTSUPP || errno == ENOSYS || errno == EBADF)
1177 SKIP(return, "open_by_handle_at with FD_NSFS_ROOT not supported");
1178 TH_LOG("open_by_handle_at failed: %s", strerror(errno));
1179 ASSERT_GE(reopened_fd, 0);
1180 }
1181
1182 struct stat reopened_st;
1183 ASSERT_EQ(fstat(reopened_fd, &reopened_st), 0);
1184 ASSERT_EQ(reopened_st.st_ino, netns_ino);
1185
1186 TH_LOG("Successfully reopened netns %lu via file handle (socket-kept)", netns_ino);
1187
1188 close(reopened_fd);
1189 close(netns_fd);
1190
1191 /* Try to reopen via file handle - should fail since namespace is now inactive */
1192 reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1193 ASSERT_LT(reopened_fd, 0);
1194 TH_LOG("Correctly failed to reopen inactive netns: %s", strerror(errno));
1195
1196 /* Get network namespace from socket */
1197 netns_fd = ioctl(sock_fd, SIOCGSKNS);
1198 if (netns_fd < 0) {
1199 free(handle);
1200 close(sock_fd);
1201 if (errno == ENOTTY || errno == EINVAL)
1202 SKIP(return, "SIOCGSKNS not supported");
1203 ASSERT_GE(netns_fd, 0);
1204 }
1205
1206 /* Verify namespace appears in listns() */
1207 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
1208 if (ret < 0) {
1209 free(handle);
1210 close(sock_fd);
1211 close(netns_fd);
1212 if (errno == ENOSYS)
1213 SKIP(return, "listns() not supported");
1214 TH_LOG("listns failed: %s", strerror(errno));
1215 ASSERT_GE(ret, 0);
1216 }
1217
1218 found_netns = false;
1219 found_userns = false;
1220 for (i = 0; i < ret; i++) {
1221 if (ns_ids[i] == netns_id)
1222 found_netns = true;
1223 if (ns_ids[i] == userns_id)
1224 found_userns = true;
1225 }
1226 ASSERT_TRUE(found_netns);
1227 ASSERT_TRUE(found_userns);
1228 TH_LOG("Found netns %llu in listns() output", netns_id);
1229
1230 close(netns_fd);
1231
1232 /* Verify namespace appears in listns() */
1233 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
1234 if (ret < 0) {
1235 free(handle);
1236 close(sock_fd);
1237 close(netns_fd);
1238 if (errno == ENOSYS)
1239 SKIP(return, "listns() not supported");
1240 TH_LOG("listns failed: %s", strerror(errno));
1241 ASSERT_GE(ret, 0);
1242 }
1243
1244 found_netns = false;
1245 found_userns = false;
1246 for (i = 0; i < ret; i++) {
1247 if (ns_ids[i] == netns_id)
1248 found_netns = true;
1249 if (ns_ids[i] == userns_id)
1250 found_userns = true;
1251 }
1252 ASSERT_FALSE(found_netns);
1253 ASSERT_FALSE(found_userns);
1254 TH_LOG("Netns %llu correctly disappeared from listns() after socket closed", netns_id);
1255
1256 close(sock_fd);
1257 free(handle);
1258 }
1259
1260 /*
1261 * Test multi-level namespace resurrection across three user namespace levels.
1262 *
1263 * This test creates a complex namespace hierarchy with three levels of user
1264 * namespaces and a network namespace at the deepest level. It verifies that
1265 * the resurrection semantics work correctly when SIOCGSKNS is called on a
1266 * socket from an inactive namespace tree, and that listns() and
1267 * open_by_handle_at() correctly respect visibility rules.
1268 *
1269 * Hierarchy after child processes exit (all with 0 active refcount):
1270 *
1271 * net_L3A (0) <- Level 3 network namespace
1272 * |
1273 * +
1274 * userns_L3 (0) <- Level 3 user namespace
1275 * |
1276 * +
1277 * userns_L2 (0) <- Level 2 user namespace
1278 * |
1279 * +
1280 * userns_L1 (0) <- Level 1 user namespace
1281 * |
1282 * x
1283 * init_user_ns
1284 *
1285 * The test verifies:
1286 * 1. SIOCGSKNS on a socket from inactive net_L3A resurrects the entire chain
1287 * 2. After resurrection, all namespaces are visible in listns()
1288 * 3. Resurrected namespaces can be reopened via file handles
1289 * 4. Closing the netns FD cascades down: the entire ownership chain
1290 * (userns_L3 -> userns_L2 -> userns_L1) becomes inactive again
1291 * 5. Inactive namespaces disappear from listns() and cannot be reopened
1292 * 6. Calling SIOCGSKNS again on the same socket resurrects the tree again
1293 * 7. After second resurrection, namespaces are visible and can be reopened
1294 */
TEST(siocgskns_multilevel_resurrection)1295 TEST(siocgskns_multilevel_resurrection)
1296 {
1297 int ipc_sockets[2];
1298 pid_t pid_l1, pid_l2, pid_l3;
1299 int status;
1300
1301 /* Namespace file descriptors to be received from child */
1302 int sock_L3A_fd = -1;
1303 int netns_L3A_fd = -1;
1304 __u64 netns_L3A_id;
1305 __u64 userns_L1_id, userns_L2_id, userns_L3_id;
1306
1307 /* For listns() and file handle testing */
1308 struct ns_id_req req = {
1309 .size = sizeof(req),
1310 .spare = 0,
1311 .ns_id = 0,
1312 .ns_type = CLONE_NEWNET | CLONE_NEWUSER,
1313 .spare2 = 0,
1314 .user_ns_id = 0,
1315 };
1316 __u64 ns_ids[256];
1317 int ret, i;
1318 struct file_handle *handle;
1319 struct nsfs_file_handle *nsfs_fh;
1320 int reopened_fd;
1321
1322 /* Allocate file handle for testing */
1323 handle = malloc(sizeof(struct file_handle) + sizeof(struct nsfs_file_handle));
1324 ASSERT_NE(handle, NULL);
1325 handle->handle_bytes = sizeof(struct nsfs_file_handle);
1326 handle->handle_type = FILEID_NSFS;
1327
1328 EXPECT_EQ(socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0);
1329
1330 /*
1331 * Fork level 1 child that creates userns_L1
1332 */
1333 pid_l1 = fork();
1334 ASSERT_GE(pid_l1, 0);
1335
1336 if (pid_l1 == 0) {
1337 /* Level 1 child */
1338 int ipc_L2[2];
1339 close(ipc_sockets[0]);
1340
1341 /* Create userns_L1 */
1342 if (setup_userns() < 0) {
1343 close(ipc_sockets[1]);
1344 exit(1);
1345 }
1346
1347 /* Create socketpair for communicating with L2 child */
1348 if (socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_L2) < 0) {
1349 close(ipc_sockets[1]);
1350 exit(1);
1351 }
1352
1353 /*
1354 * Fork level 2 child that creates userns_L2
1355 */
1356 pid_l2 = fork();
1357 if (pid_l2 < 0) {
1358 close(ipc_sockets[1]);
1359 close(ipc_L2[0]);
1360 close(ipc_L2[1]);
1361 exit(1);
1362 }
1363
1364 if (pid_l2 == 0) {
1365 /* Level 2 child */
1366 int ipc_L3[2];
1367 close(ipc_L2[0]);
1368
1369 /* Create userns_L2 (nested inside userns_L1) */
1370 if (setup_userns() < 0) {
1371 close(ipc_L2[1]);
1372 exit(1);
1373 }
1374
1375 /* Create socketpair for communicating with L3 child */
1376 if (socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_L3) < 0) {
1377 close(ipc_L2[1]);
1378 exit(1);
1379 }
1380
1381 /*
1382 * Fork level 3 child that creates userns_L3 and network namespaces
1383 */
1384 pid_l3 = fork();
1385 if (pid_l3 < 0) {
1386 close(ipc_L2[1]);
1387 close(ipc_L3[0]);
1388 close(ipc_L3[1]);
1389 exit(1);
1390 }
1391
1392 if (pid_l3 == 0) {
1393 /* Level 3 child - the deepest level */
1394 int sock_fd;
1395 close(ipc_L3[0]);
1396
1397 /* Create userns_L3 (nested inside userns_L2) */
1398 if (setup_userns() < 0) {
1399 close(ipc_L3[1]);
1400 exit(1);
1401 }
1402
1403 /* Create network namespace at level 3 */
1404 if (unshare(CLONE_NEWNET) < 0) {
1405 close(ipc_L3[1]);
1406 exit(1);
1407 }
1408
1409 /* Create socket in net_L3A */
1410 sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
1411 if (sock_fd < 0) {
1412 close(ipc_L3[1]);
1413 exit(1);
1414 }
1415
1416 /* Send socket FD to L2 parent */
1417 struct msghdr msg = {0};
1418 struct iovec iov = {0};
1419 char buf[1] = {'X'};
1420 char cmsg_buf[CMSG_SPACE(sizeof(int))];
1421
1422 iov.iov_base = buf;
1423 iov.iov_len = 1;
1424 msg.msg_iov = &iov;
1425 msg.msg_iovlen = 1;
1426 msg.msg_control = cmsg_buf;
1427 msg.msg_controllen = sizeof(cmsg_buf);
1428
1429 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
1430 cmsg->cmsg_level = SOL_SOCKET;
1431 cmsg->cmsg_type = SCM_RIGHTS;
1432 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
1433 memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(int));
1434
1435 if (sendmsg(ipc_L3[1], &msg, 0) < 0) {
1436 close(sock_fd);
1437 close(ipc_L3[1]);
1438 exit(1);
1439 }
1440
1441 close(sock_fd);
1442 close(ipc_L3[1]);
1443 exit(0);
1444 }
1445
1446 /* Level 2 child - receive from L3 and forward to L1 */
1447 close(ipc_L3[1]);
1448
1449 struct msghdr msg = {0};
1450 struct iovec iov = {0};
1451 char buf[1];
1452 char cmsg_buf[CMSG_SPACE(sizeof(int))];
1453 int received_fd;
1454
1455 iov.iov_base = buf;
1456 iov.iov_len = 1;
1457 msg.msg_iov = &iov;
1458 msg.msg_iovlen = 1;
1459 msg.msg_control = cmsg_buf;
1460 msg.msg_controllen = sizeof(cmsg_buf);
1461
1462 ssize_t n = recvmsg(ipc_L3[0], &msg, 0);
1463 close(ipc_L3[0]);
1464
1465 if (n != 1) {
1466 close(ipc_L2[1]);
1467 waitpid(pid_l3, NULL, 0);
1468 exit(1);
1469 }
1470
1471 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
1472 if (!cmsg) {
1473 close(ipc_L2[1]);
1474 waitpid(pid_l3, NULL, 0);
1475 exit(1);
1476 }
1477 memcpy(&received_fd, CMSG_DATA(cmsg), sizeof(int));
1478
1479 /* Wait for L3 child */
1480 waitpid(pid_l3, NULL, 0);
1481
1482 /* Forward the socket FD to L1 parent */
1483 memset(&msg, 0, sizeof(msg));
1484 buf[0] = 'Y';
1485 iov.iov_base = buf;
1486 iov.iov_len = 1;
1487 msg.msg_iov = &iov;
1488 msg.msg_iovlen = 1;
1489 msg.msg_control = cmsg_buf;
1490 msg.msg_controllen = sizeof(cmsg_buf);
1491
1492 cmsg = CMSG_FIRSTHDR(&msg);
1493 cmsg->cmsg_level = SOL_SOCKET;
1494 cmsg->cmsg_type = SCM_RIGHTS;
1495 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
1496 memcpy(CMSG_DATA(cmsg), &received_fd, sizeof(int));
1497
1498 if (sendmsg(ipc_L2[1], &msg, 0) < 0) {
1499 close(received_fd);
1500 close(ipc_L2[1]);
1501 exit(1);
1502 }
1503
1504 close(received_fd);
1505 close(ipc_L2[1]);
1506 exit(0);
1507 }
1508
1509 /* Level 1 child - receive from L2 and forward to parent */
1510 close(ipc_L2[1]);
1511
1512 struct msghdr msg = {0};
1513 struct iovec iov = {0};
1514 char buf[1];
1515 char cmsg_buf[CMSG_SPACE(sizeof(int))];
1516 int received_fd;
1517
1518 iov.iov_base = buf;
1519 iov.iov_len = 1;
1520 msg.msg_iov = &iov;
1521 msg.msg_iovlen = 1;
1522 msg.msg_control = cmsg_buf;
1523 msg.msg_controllen = sizeof(cmsg_buf);
1524
1525 ssize_t n = recvmsg(ipc_L2[0], &msg, 0);
1526 close(ipc_L2[0]);
1527
1528 if (n != 1) {
1529 close(ipc_sockets[1]);
1530 waitpid(pid_l2, NULL, 0);
1531 exit(1);
1532 }
1533
1534 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
1535 if (!cmsg) {
1536 close(ipc_sockets[1]);
1537 waitpid(pid_l2, NULL, 0);
1538 exit(1);
1539 }
1540 memcpy(&received_fd, CMSG_DATA(cmsg), sizeof(int));
1541
1542 /* Wait for L2 child */
1543 waitpid(pid_l2, NULL, 0);
1544
1545 /* Forward the socket FD to parent */
1546 memset(&msg, 0, sizeof(msg));
1547 buf[0] = 'Z';
1548 iov.iov_base = buf;
1549 iov.iov_len = 1;
1550 msg.msg_iov = &iov;
1551 msg.msg_iovlen = 1;
1552 msg.msg_control = cmsg_buf;
1553 msg.msg_controllen = sizeof(cmsg_buf);
1554
1555 cmsg = CMSG_FIRSTHDR(&msg);
1556 cmsg->cmsg_level = SOL_SOCKET;
1557 cmsg->cmsg_type = SCM_RIGHTS;
1558 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
1559 memcpy(CMSG_DATA(cmsg), &received_fd, sizeof(int));
1560
1561 if (sendmsg(ipc_sockets[1], &msg, 0) < 0) {
1562 close(received_fd);
1563 close(ipc_sockets[1]);
1564 exit(1);
1565 }
1566
1567 close(received_fd);
1568 close(ipc_sockets[1]);
1569 exit(0);
1570 }
1571
1572 /* Parent - receive the socket from the deepest level */
1573 close(ipc_sockets[1]);
1574
1575 struct msghdr msg = {0};
1576 struct iovec iov = {0};
1577 char buf[1];
1578 char cmsg_buf[CMSG_SPACE(sizeof(int))];
1579
1580 iov.iov_base = buf;
1581 iov.iov_len = 1;
1582 msg.msg_iov = &iov;
1583 msg.msg_iovlen = 1;
1584 msg.msg_control = cmsg_buf;
1585 msg.msg_controllen = sizeof(cmsg_buf);
1586
1587 ssize_t n = recvmsg(ipc_sockets[0], &msg, 0);
1588 close(ipc_sockets[0]);
1589
1590 if (n != 1) {
1591 free(handle);
1592 waitpid(pid_l1, NULL, 0);
1593 SKIP(return, "Failed to receive socket from child");
1594 }
1595
1596 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
1597 if (!cmsg) {
1598 free(handle);
1599 waitpid(pid_l1, NULL, 0);
1600 SKIP(return, "Failed to receive socket from child");
1601 }
1602 memcpy(&sock_L3A_fd, CMSG_DATA(cmsg), sizeof(int));
1603
1604 /* Wait for L1 child */
1605 waitpid(pid_l1, &status, 0);
1606 ASSERT_TRUE(WIFEXITED(status));
1607 ASSERT_EQ(WEXITSTATUS(status), 0);
1608
1609 /*
1610 * At this point, all child processes have exited. The socket itself
1611 * doesn't keep the namespace active - we need to call SIOCGSKNS which
1612 * will resurrect the entire namespace tree by taking active references.
1613 */
1614
1615 /* Get network namespace from socket - this resurrects the tree */
1616 netns_L3A_fd = ioctl(sock_L3A_fd, SIOCGSKNS);
1617 if (netns_L3A_fd < 0) {
1618 free(handle);
1619 close(sock_L3A_fd);
1620 if (errno == ENOTTY || errno == EINVAL)
1621 SKIP(return, "SIOCGSKNS not supported");
1622 ASSERT_GE(netns_L3A_fd, 0);
1623 }
1624
1625 /* Get namespace ID for net_L3A */
1626 ret = ioctl(netns_L3A_fd, NS_GET_ID, &netns_L3A_id);
1627 if (ret < 0) {
1628 free(handle);
1629 close(sock_L3A_fd);
1630 close(netns_L3A_fd);
1631 if (errno == ENOTTY || errno == EINVAL)
1632 SKIP(return, "NS_GET_ID not supported");
1633 ASSERT_EQ(ret, 0);
1634 }
1635
1636 /* Get owner user namespace chain: userns_L3 -> userns_L2 -> userns_L1 */
1637 int userns_L3_fd = ioctl(netns_L3A_fd, NS_GET_USERNS);
1638 if (userns_L3_fd < 0) {
1639 free(handle);
1640 close(sock_L3A_fd);
1641 close(netns_L3A_fd);
1642 if (errno == ENOTTY || errno == EINVAL)
1643 SKIP(return, "NS_GET_USERNS not supported");
1644 ASSERT_GE(userns_L3_fd, 0);
1645 }
1646
1647 ret = ioctl(userns_L3_fd, NS_GET_ID, &userns_L3_id);
1648 ASSERT_EQ(ret, 0);
1649
1650 int userns_L2_fd = ioctl(userns_L3_fd, NS_GET_USERNS);
1651 ASSERT_GE(userns_L2_fd, 0);
1652 ret = ioctl(userns_L2_fd, NS_GET_ID, &userns_L2_id);
1653 ASSERT_EQ(ret, 0);
1654
1655 int userns_L1_fd = ioctl(userns_L2_fd, NS_GET_USERNS);
1656 ASSERT_GE(userns_L1_fd, 0);
1657 ret = ioctl(userns_L1_fd, NS_GET_ID, &userns_L1_id);
1658 ASSERT_EQ(ret, 0);
1659
1660 close(userns_L1_fd);
1661 close(userns_L2_fd);
1662 close(userns_L3_fd);
1663
1664 TH_LOG("Multi-level hierarchy: net_L3A (id=%llu) -> userns_L3 (id=%llu) -> userns_L2 (id=%llu) -> userns_L1 (id=%llu)",
1665 netns_L3A_id, userns_L3_id, userns_L2_id, userns_L1_id);
1666
1667 /*
1668 * Test 1: Verify net_L3A is visible in listns() after resurrection.
1669 * The entire ownership chain should be resurrected and visible.
1670 */
1671 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
1672 if (ret < 0) {
1673 free(handle);
1674 close(sock_L3A_fd);
1675 close(netns_L3A_fd);
1676 if (errno == ENOSYS)
1677 SKIP(return, "listns() not supported");
1678 ASSERT_GE(ret, 0);
1679 }
1680
1681 bool found_netns_L3A = false;
1682 bool found_userns_L1 = false;
1683 bool found_userns_L2 = false;
1684 bool found_userns_L3 = false;
1685
1686 for (i = 0; i < ret; i++) {
1687 if (ns_ids[i] == netns_L3A_id)
1688 found_netns_L3A = true;
1689 if (ns_ids[i] == userns_L1_id)
1690 found_userns_L1 = true;
1691 if (ns_ids[i] == userns_L2_id)
1692 found_userns_L2 = true;
1693 if (ns_ids[i] == userns_L3_id)
1694 found_userns_L3 = true;
1695 }
1696
1697 ASSERT_TRUE(found_netns_L3A);
1698 ASSERT_TRUE(found_userns_L1);
1699 ASSERT_TRUE(found_userns_L2);
1700 ASSERT_TRUE(found_userns_L3);
1701 TH_LOG("Resurrection verified: all namespaces in hierarchy visible in listns()");
1702
1703 /*
1704 * Test 2: Verify net_L3A can be reopened via file handle.
1705 */
1706 nsfs_fh = (struct nsfs_file_handle *)handle->f_handle;
1707 nsfs_fh->ns_id = netns_L3A_id;
1708 nsfs_fh->ns_type = 0;
1709 nsfs_fh->ns_inum = 0;
1710
1711 reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1712 if (reopened_fd < 0) {
1713 free(handle);
1714 close(sock_L3A_fd);
1715 close(netns_L3A_fd);
1716 if (errno == EOPNOTSUPP || errno == ENOSYS || errno == EBADF)
1717 SKIP(return, "open_by_handle_at with FD_NSFS_ROOT not supported");
1718 TH_LOG("open_by_handle_at failed: %s", strerror(errno));
1719 ASSERT_GE(reopened_fd, 0);
1720 }
1721
1722 close(reopened_fd);
1723 TH_LOG("File handle test passed: net_L3A can be reopened");
1724
1725 /*
1726 * Test 3: Verify that when we close the netns FD (dropping the last
1727 * active reference), the entire tree becomes inactive and disappears
1728 * from listns(). The cascade goes: net_L3A drops -> userns_L3 drops ->
1729 * userns_L2 drops -> userns_L1 drops.
1730 */
1731 close(netns_L3A_fd);
1732
1733 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
1734 ASSERT_GE(ret, 0);
1735
1736 found_netns_L3A = false;
1737 found_userns_L1 = false;
1738 found_userns_L2 = false;
1739 found_userns_L3 = false;
1740
1741 for (i = 0; i < ret; i++) {
1742 if (ns_ids[i] == netns_L3A_id)
1743 found_netns_L3A = true;
1744 if (ns_ids[i] == userns_L1_id)
1745 found_userns_L1 = true;
1746 if (ns_ids[i] == userns_L2_id)
1747 found_userns_L2 = true;
1748 if (ns_ids[i] == userns_L3_id)
1749 found_userns_L3 = true;
1750 }
1751
1752 ASSERT_FALSE(found_netns_L3A);
1753 ASSERT_FALSE(found_userns_L1);
1754 ASSERT_FALSE(found_userns_L2);
1755 ASSERT_FALSE(found_userns_L3);
1756 TH_LOG("Cascade test passed: all namespaces disappeared after netns FD closed");
1757
1758 /*
1759 * Test 4: Verify file handle no longer works for inactive namespace.
1760 */
1761 reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1762 if (reopened_fd >= 0) {
1763 close(reopened_fd);
1764 free(handle);
1765 ASSERT_TRUE(false); /* Should have failed */
1766 }
1767 TH_LOG("Inactive namespace correctly cannot be reopened via file handle");
1768
1769 /*
1770 * Test 5: Verify that calling SIOCGSKNS again resurrects the tree again.
1771 * The socket is still valid, so we can call SIOCGSKNS on it to resurrect
1772 * the namespace tree once more.
1773 */
1774 netns_L3A_fd = ioctl(sock_L3A_fd, SIOCGSKNS);
1775 ASSERT_GE(netns_L3A_fd, 0);
1776
1777 TH_LOG("Called SIOCGSKNS again to resurrect the namespace tree");
1778
1779 /* Verify the namespace tree is resurrected and visible in listns() */
1780 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
1781 ASSERT_GE(ret, 0);
1782
1783 found_netns_L3A = false;
1784 found_userns_L1 = false;
1785 found_userns_L2 = false;
1786 found_userns_L3 = false;
1787
1788 for (i = 0; i < ret; i++) {
1789 if (ns_ids[i] == netns_L3A_id)
1790 found_netns_L3A = true;
1791 if (ns_ids[i] == userns_L1_id)
1792 found_userns_L1 = true;
1793 if (ns_ids[i] == userns_L2_id)
1794 found_userns_L2 = true;
1795 if (ns_ids[i] == userns_L3_id)
1796 found_userns_L3 = true;
1797 }
1798
1799 ASSERT_TRUE(found_netns_L3A);
1800 ASSERT_TRUE(found_userns_L1);
1801 ASSERT_TRUE(found_userns_L2);
1802 ASSERT_TRUE(found_userns_L3);
1803 TH_LOG("Second resurrection verified: all namespaces in hierarchy visible in listns() again");
1804
1805 /* Verify we can reopen via file handle again */
1806 reopened_fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1807 if (reopened_fd < 0) {
1808 free(handle);
1809 close(sock_L3A_fd);
1810 close(netns_L3A_fd);
1811 TH_LOG("open_by_handle_at failed after second resurrection: %s", strerror(errno));
1812 ASSERT_GE(reopened_fd, 0);
1813 }
1814
1815 close(reopened_fd);
1816 TH_LOG("File handle test passed: net_L3A can be reopened after second resurrection");
1817
1818 /* Final cleanup */
1819 close(sock_L3A_fd);
1820 close(netns_L3A_fd);
1821 free(handle);
1822 }
1823
1824 TEST_HARNESS_MAIN
1825