xref: /linux/tools/testing/selftests/net/af_unix/scm_rights.c (revision 6e7fd890f1d6ac83805409e9c346240de2705584)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright Amazon.com Inc. or its affiliates. */
3 #define _GNU_SOURCE
4 #include <sched.h>
5 
6 #include <stdio.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 #include <sys/un.h>
12 
13 #include "../../kselftest_harness.h"
14 
15 FIXTURE(scm_rights)
16 {
17 	int fd[32];
18 };
19 
20 FIXTURE_VARIANT(scm_rights)
21 {
22 	char name[32];
23 	int type;
24 	int flags;
25 	bool test_listener;
26 };
27 
28 FIXTURE_VARIANT_ADD(scm_rights, dgram)
29 {
30 	.name = "UNIX ",
31 	.type = SOCK_DGRAM,
32 	.flags = 0,
33 	.test_listener = false,
34 };
35 
36 FIXTURE_VARIANT_ADD(scm_rights, stream)
37 {
38 	.name = "UNIX-STREAM ",
39 	.type = SOCK_STREAM,
40 	.flags = 0,
41 	.test_listener = false,
42 };
43 
44 FIXTURE_VARIANT_ADD(scm_rights, stream_oob)
45 {
46 	.name = "UNIX-STREAM ",
47 	.type = SOCK_STREAM,
48 	.flags = MSG_OOB,
49 	.test_listener = false,
50 };
51 
52 FIXTURE_VARIANT_ADD(scm_rights, stream_listener)
53 {
54 	.name = "UNIX-STREAM ",
55 	.type = SOCK_STREAM,
56 	.flags = 0,
57 	.test_listener = true,
58 };
59 
60 FIXTURE_VARIANT_ADD(scm_rights, stream_listener_oob)
61 {
62 	.name = "UNIX-STREAM ",
63 	.type = SOCK_STREAM,
64 	.flags = MSG_OOB,
65 	.test_listener = true,
66 };
67 
68 static int count_sockets(struct __test_metadata *_metadata,
69 			 const FIXTURE_VARIANT(scm_rights) *variant)
70 {
71 	int sockets = -1, len, ret;
72 	char *line = NULL;
73 	size_t unused;
74 	FILE *f;
75 
76 	f = fopen("/proc/net/protocols", "r");
77 	ASSERT_NE(NULL, f);
78 
79 	len = strlen(variant->name);
80 
81 	while (getline(&line, &unused, f) != -1) {
82 		int unused2;
83 
84 		if (strncmp(line, variant->name, len))
85 			continue;
86 
87 		ret = sscanf(line + len, "%d %d", &unused2, &sockets);
88 		ASSERT_EQ(2, ret);
89 
90 		break;
91 	}
92 
93 	free(line);
94 
95 	ret = fclose(f);
96 	ASSERT_EQ(0, ret);
97 
98 	return sockets;
99 }
100 
101 FIXTURE_SETUP(scm_rights)
102 {
103 	int ret;
104 
105 	ret = unshare(CLONE_NEWNET);
106 	ASSERT_EQ(0, ret);
107 
108 	ret = count_sockets(_metadata, variant);
109 	ASSERT_EQ(0, ret);
110 }
111 
112 FIXTURE_TEARDOWN(scm_rights)
113 {
114 	int ret;
115 
116 	sleep(1);
117 
118 	ret = count_sockets(_metadata, variant);
119 	ASSERT_EQ(0, ret);
120 }
121 
122 static void create_listeners(struct __test_metadata *_metadata,
123 			     FIXTURE_DATA(scm_rights) *self,
124 			     int n)
125 {
126 	struct sockaddr_un addr = {
127 		.sun_family = AF_UNIX,
128 	};
129 	socklen_t addrlen;
130 	int i, ret;
131 
132 	for (i = 0; i < n * 2; i += 2) {
133 		self->fd[i] = socket(AF_UNIX, SOCK_STREAM, 0);
134 		ASSERT_LE(0, self->fd[i]);
135 
136 		addrlen = sizeof(addr.sun_family);
137 		ret = bind(self->fd[i], (struct sockaddr *)&addr, addrlen);
138 		ASSERT_EQ(0, ret);
139 
140 		ret = listen(self->fd[i], -1);
141 		ASSERT_EQ(0, ret);
142 
143 		addrlen = sizeof(addr);
144 		ret = getsockname(self->fd[i], (struct sockaddr *)&addr, &addrlen);
145 		ASSERT_EQ(0, ret);
146 
147 		self->fd[i + 1] = socket(AF_UNIX, SOCK_STREAM, 0);
148 		ASSERT_LE(0, self->fd[i + 1]);
149 
150 		ret = connect(self->fd[i + 1], (struct sockaddr *)&addr, addrlen);
151 		ASSERT_EQ(0, ret);
152 	}
153 }
154 
155 static void create_socketpairs(struct __test_metadata *_metadata,
156 			       FIXTURE_DATA(scm_rights) *self,
157 			       const FIXTURE_VARIANT(scm_rights) *variant,
158 			       int n)
159 {
160 	int i, ret;
161 
162 	ASSERT_GE(sizeof(self->fd) / sizeof(int), n);
163 
164 	for (i = 0; i < n * 2; i += 2) {
165 		ret = socketpair(AF_UNIX, variant->type, 0, self->fd + i);
166 		ASSERT_EQ(0, ret);
167 	}
168 }
169 
170 static void __create_sockets(struct __test_metadata *_metadata,
171 			     FIXTURE_DATA(scm_rights) *self,
172 			     const FIXTURE_VARIANT(scm_rights) *variant,
173 			     int n)
174 {
175 	ASSERT_LE(n * 2, sizeof(self->fd) / sizeof(self->fd[0]));
176 
177 	if (variant->test_listener)
178 		create_listeners(_metadata, self, n);
179 	else
180 		create_socketpairs(_metadata, self, variant, n);
181 }
182 
183 static void __close_sockets(struct __test_metadata *_metadata,
184 			    FIXTURE_DATA(scm_rights) *self,
185 			    int n)
186 {
187 	int i, ret;
188 
189 	ASSERT_GE(sizeof(self->fd) / sizeof(int), n);
190 
191 	for (i = 0; i < n * 2; i++) {
192 		ret = close(self->fd[i]);
193 		ASSERT_EQ(0, ret);
194 	}
195 }
196 
197 void __send_fd(struct __test_metadata *_metadata,
198 	       const FIXTURE_DATA(scm_rights) *self,
199 	       const FIXTURE_VARIANT(scm_rights) *variant,
200 	       int inflight, int receiver)
201 {
202 #define MSG "x"
203 #define MSGLEN 1
204 	struct {
205 		struct cmsghdr cmsghdr;
206 		int fd[2];
207 	} cmsg = {
208 		.cmsghdr = {
209 			.cmsg_len = CMSG_LEN(sizeof(cmsg.fd)),
210 			.cmsg_level = SOL_SOCKET,
211 			.cmsg_type = SCM_RIGHTS,
212 		},
213 		.fd = {
214 			self->fd[inflight * 2],
215 			self->fd[inflight * 2],
216 		},
217 	};
218 	struct iovec iov = {
219 		.iov_base = MSG,
220 		.iov_len = MSGLEN,
221 	};
222 	struct msghdr msg = {
223 		.msg_name = NULL,
224 		.msg_namelen = 0,
225 		.msg_iov = &iov,
226 		.msg_iovlen = 1,
227 		.msg_control = &cmsg,
228 		.msg_controllen = CMSG_SPACE(sizeof(cmsg.fd)),
229 	};
230 	int ret;
231 
232 	ret = sendmsg(self->fd[receiver * 2 + 1], &msg, variant->flags);
233 	ASSERT_EQ(MSGLEN, ret);
234 }
235 
236 #define create_sockets(n)					\
237 	__create_sockets(_metadata, self, variant, n)
238 #define close_sockets(n)					\
239 	__close_sockets(_metadata, self, n)
240 #define send_fd(inflight, receiver)				\
241 	__send_fd(_metadata, self, variant, inflight, receiver)
242 
243 TEST_F(scm_rights, self_ref)
244 {
245 	create_sockets(2);
246 
247 	send_fd(0, 0);
248 
249 	send_fd(1, 1);
250 
251 	close_sockets(2);
252 }
253 
254 TEST_F(scm_rights, triangle)
255 {
256 	create_sockets(6);
257 
258 	send_fd(0, 1);
259 	send_fd(1, 2);
260 	send_fd(2, 0);
261 
262 	send_fd(3, 4);
263 	send_fd(4, 5);
264 	send_fd(5, 3);
265 
266 	close_sockets(6);
267 }
268 
269 TEST_F(scm_rights, cross_edge)
270 {
271 	create_sockets(8);
272 
273 	send_fd(0, 1);
274 	send_fd(1, 2);
275 	send_fd(2, 0);
276 	send_fd(1, 3);
277 	send_fd(3, 2);
278 
279 	send_fd(4, 5);
280 	send_fd(5, 6);
281 	send_fd(6, 4);
282 	send_fd(5, 7);
283 	send_fd(7, 6);
284 
285 	close_sockets(8);
286 }
287 
288 TEST_F(scm_rights, backtrack_from_scc)
289 {
290 	create_sockets(10);
291 
292 	send_fd(0, 1);
293 	send_fd(0, 4);
294 	send_fd(1, 2);
295 	send_fd(2, 3);
296 	send_fd(3, 1);
297 
298 	send_fd(5, 6);
299 	send_fd(5, 9);
300 	send_fd(6, 7);
301 	send_fd(7, 8);
302 	send_fd(8, 6);
303 
304 	close_sockets(10);
305 }
306 
307 TEST_HARNESS_MAIN
308