xref: /linux/tools/testing/selftests/namespaces/siocgskns_test.c (revision c0f06da56860f185268b77cb23c4b3c85518e87a)
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  */
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  */
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 	if (WEXITSTATUS(status) != 0)
167 		SKIP(close(sock_fd); return, "Child failed to create namespace");
168 
169 	/* Get network namespace from socket */
170 	netns_fd = ioctl(sock_fd, SIOCGSKNS);
171 	if (netns_fd < 0) {
172 		close(sock_fd);
173 		if (errno == ENOTTY || errno == EINVAL)
174 			SKIP(return, "SIOCGSKNS not supported");
175 		ASSERT_GE(netns_fd, 0);
176 	}
177 
178 	ASSERT_EQ(fstat(netns_fd, &st), 0);
179 
180 	/*
181 	 * Namespace should still be active because socket FD keeps it alive.
182 	 * Try to access it via /proc/self/fd/<fd>.
183 	 */
184 	char path[64];
185 	snprintf(path, sizeof(path), "/proc/self/fd/%d", netns_fd);
186 	test_fd = open(path, O_RDONLY);
187 	ASSERT_GE(test_fd, 0);
188 	close(test_fd);
189 	close(netns_fd);
190 
191 	/* Close socket - namespace should become inactive */
192 	close(sock_fd);
193 
194 	/* Try SIOCGSKNS again - should fail since socket is closed */
195 	ASSERT_LT(ioctl(sock_fd, SIOCGSKNS), 0);
196 }
197 
198 TEST_HARNESS_MAIN
199