xref: /linux/tools/testing/selftests/namespaces/siocgskns_test.c (revision 2b9fa5bf0c417c444d3c572dfe2138da7fdf11cd)
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 	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  */
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  */
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  */
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 TEST_HARNESS_MAIN
337