xref: /aosp_15_r20/external/liburing/test/connect.c (revision 25da2bea747f3a93b4c30fd9708b0618ef55a0e6)
1 /* SPDX-License-Identifier: MIT */
2 /*
3  * Check that IORING_OP_CONNECT works, with and without other side
4  * being open.
5  */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <unistd.h>
13 #include <poll.h>
14 #include <sys/socket.h>
15 #include <netinet/in.h>
16 #include <netinet/tcp.h>
17 #include <arpa/inet.h>
18 
19 #include "liburing.h"
20 
21 static int no_connect;
22 static unsigned short use_port;
23 static unsigned int use_addr;
24 
create_socket(void)25 static int create_socket(void)
26 {
27 	int fd;
28 
29 	fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
30 	if (fd == -1) {
31 		perror("socket()");
32 		return -1;
33 	}
34 
35 	return fd;
36 }
37 
submit_and_wait(struct io_uring * ring,int * res)38 static int submit_and_wait(struct io_uring *ring, int *res)
39 {
40 	struct io_uring_cqe *cqe;
41 	int ret;
42 
43 	ret = io_uring_submit_and_wait(ring, 1);
44 	if (ret != 1) {
45 		fprintf(stderr, "io_using_submit: got %d\n", ret);
46 		return 1;
47 	}
48 
49 	ret = io_uring_peek_cqe(ring, &cqe);
50 	if (ret) {
51 		fprintf(stderr, "io_uring_peek_cqe(): no cqe returned");
52 		return 1;
53 	}
54 
55 	*res = cqe->res;
56 	io_uring_cqe_seen(ring, cqe);
57 	return 0;
58 }
59 
wait_for(struct io_uring * ring,int fd,int mask)60 static int wait_for(struct io_uring *ring, int fd, int mask)
61 {
62 	struct io_uring_sqe *sqe;
63 	int ret, res;
64 
65 	sqe = io_uring_get_sqe(ring);
66 	if (!sqe) {
67 		fprintf(stderr, "unable to get sqe\n");
68 		return -1;
69 	}
70 
71 	io_uring_prep_poll_add(sqe, fd, mask);
72 	sqe->user_data = 2;
73 
74 	ret = submit_and_wait(ring, &res);
75 	if (ret)
76 		return -1;
77 
78 	if (res < 0) {
79 		fprintf(stderr, "poll(): failed with %d\n", res);
80 		return -1;
81 	}
82 
83 	return res;
84 }
85 
listen_on_socket(int fd)86 static int listen_on_socket(int fd)
87 {
88 	struct sockaddr_in addr;
89 	int ret;
90 
91 	memset(&addr, 0, sizeof(addr));
92 	addr.sin_family = AF_INET;
93 	addr.sin_port = use_port;
94 	addr.sin_addr.s_addr = use_addr;
95 
96 	ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
97 	if (ret == -1) {
98 		perror("bind()");
99 		return -1;
100 	}
101 
102 	ret = listen(fd, 128);
103 	if (ret == -1) {
104 		perror("listen()");
105 		return -1;
106 	}
107 
108 	return 0;
109 }
110 
configure_connect(int fd,struct sockaddr_in * addr)111 static int configure_connect(int fd, struct sockaddr_in* addr)
112 {
113 	int ret, val = 1;
114 
115 	ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
116 	if (ret == -1) {
117 		perror("setsockopt()");
118 		return -1;
119 	}
120 
121 	ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
122 	if (ret == -1) {
123 		perror("setsockopt()");
124 		return -1;
125 	}
126 
127 	memset(addr, 0, sizeof(*addr));
128 	addr->sin_family = AF_INET;
129 	addr->sin_port = use_port;
130 	ret = inet_aton("127.0.0.1", &addr->sin_addr);
131 	return ret;
132 }
133 
connect_socket(struct io_uring * ring,int fd,int * code)134 static int connect_socket(struct io_uring *ring, int fd, int *code)
135 {
136 	struct sockaddr_in addr;
137 	int ret, res;
138 	socklen_t code_len = sizeof(*code);
139 	struct io_uring_sqe *sqe;
140 
141 	if (configure_connect(fd, &addr) == -1)
142 		return -1;
143 
144 	sqe = io_uring_get_sqe(ring);
145 	if (!sqe) {
146 		fprintf(stderr, "unable to get sqe\n");
147 		return -1;
148 	}
149 
150 	io_uring_prep_connect(sqe, fd, (struct sockaddr*)&addr, sizeof(addr));
151 	sqe->user_data = 1;
152 
153 	ret = submit_and_wait(ring, &res);
154 	if (ret)
155 		return -1;
156 
157 	if (res == -EINPROGRESS) {
158 		ret = wait_for(ring, fd, POLLOUT | POLLHUP | POLLERR);
159 		if (ret == -1)
160 			return -1;
161 
162 		int ev = (ret & POLLOUT) || (ret & POLLHUP) || (ret & POLLERR);
163 		if (!ev) {
164 			fprintf(stderr, "poll(): returned invalid value %#x\n", ret);
165 			return -1;
166 		}
167 
168 		ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, code, &code_len);
169 		if (ret == -1) {
170 			perror("getsockopt()");
171 			return -1;
172 		}
173 	} else
174 		*code = res;
175 	return 0;
176 }
177 
test_connect_with_no_peer(struct io_uring * ring)178 static int test_connect_with_no_peer(struct io_uring *ring)
179 {
180 	int connect_fd;
181 	int ret, code;
182 
183 	connect_fd = create_socket();
184 	if (connect_fd == -1)
185 		return -1;
186 
187 	ret = connect_socket(ring, connect_fd, &code);
188 	if (ret == -1)
189 		goto err;
190 
191 	if (code != -ECONNREFUSED) {
192 		if (code == -EINVAL || code == -EBADF || code == -EOPNOTSUPP) {
193 			fprintf(stdout, "No connect support, skipping\n");
194 			no_connect = 1;
195 			goto out;
196 		}
197 		fprintf(stderr, "connect failed with %d\n", code);
198 		goto err;
199 	}
200 
201 out:
202 	close(connect_fd);
203 	return 0;
204 
205 err:
206 	close(connect_fd);
207 	return -1;
208 }
209 
test_connect(struct io_uring * ring)210 static int test_connect(struct io_uring *ring)
211 {
212 	int accept_fd;
213 	int connect_fd;
214 	int ret, code;
215 
216 	accept_fd = create_socket();
217 	if (accept_fd == -1)
218 		return -1;
219 
220 	ret = listen_on_socket(accept_fd);
221 	if (ret == -1)
222 		goto err1;
223 
224 	connect_fd = create_socket();
225 	if (connect_fd == -1)
226 		goto err1;
227 
228 	ret = connect_socket(ring, connect_fd, &code);
229 	if (ret == -1)
230 		goto err2;
231 
232 	if (code != 0) {
233 		fprintf(stderr, "connect failed with %d\n", code);
234 		goto err2;
235 	}
236 
237 	close(connect_fd);
238 	close(accept_fd);
239 
240 	return 0;
241 
242 err2:
243 	close(connect_fd);
244 
245 err1:
246 	close(accept_fd);
247 	return -1;
248 }
249 
test_connect_timeout(struct io_uring * ring)250 static int test_connect_timeout(struct io_uring *ring)
251 {
252 	int connect_fd[2] = {-1, -1};
253 	int accept_fd = -1;
254 	int ret, code;
255 	struct sockaddr_in addr;
256 	struct io_uring_sqe *sqe;
257 	struct __kernel_timespec ts = {.tv_sec = 0, .tv_nsec = 100000};
258 
259 	connect_fd[0] = create_socket();
260 	if (connect_fd[0] == -1)
261 		return -1;
262 
263 	connect_fd[1] = create_socket();
264 	if (connect_fd[1] == -1)
265 		goto err;
266 
267 	accept_fd = create_socket();
268 	if (accept_fd == -1)
269 		goto err;
270 
271 	if (configure_connect(connect_fd[0], &addr) == -1)
272 		goto err;
273 
274 	if (configure_connect(connect_fd[1], &addr) == -1)
275 		goto err;
276 
277 	ret = bind(accept_fd, (struct sockaddr*)&addr, sizeof(addr));
278 	if (ret == -1) {
279 		perror("bind()");
280 		goto err;
281 	}
282 
283 	ret = listen(accept_fd, 0);  // no backlog in order to block connect_fd[1]
284 	if (ret == -1) {
285 		perror("listen()");
286 		goto err;
287 	}
288 
289 	// We first connect with one client socket in order to fill the accept queue.
290 	ret = connect_socket(ring, connect_fd[0], &code);
291 	if (ret == -1 || code != 0) {
292 		fprintf(stderr, "unable to connect\n");
293 		goto err;
294 	}
295 
296 	// We do not offload completion events from listening socket on purpose.
297 	// This way we create a state where the second connect request being stalled by OS.
298 	sqe = io_uring_get_sqe(ring);
299 	if (!sqe) {
300 		fprintf(stderr, "unable to get sqe\n");
301 		goto err;
302 	}
303 
304 	io_uring_prep_connect(sqe, connect_fd[1], (struct sockaddr*)&addr, sizeof(addr));
305 	sqe->user_data = 1;
306 	sqe->flags |= IOSQE_IO_LINK;
307 
308 	sqe = io_uring_get_sqe(ring);
309 	if (!sqe) {
310 		fprintf(stderr, "unable to get sqe\n");
311 		goto err;
312 	}
313 	io_uring_prep_link_timeout(sqe, &ts, 0);
314 	sqe->user_data = 2;
315 
316 	ret = io_uring_submit(ring);
317 	if (ret != 2) {
318 		fprintf(stderr, "submitted %d\n", ret);
319 		return -1;
320 	}
321 
322 	for (int i = 0; i < 2; i++) {
323 		int expected;
324 		struct io_uring_cqe *cqe;
325 
326 		ret = io_uring_wait_cqe(ring, &cqe);
327 		if (ret) {
328 			fprintf(stderr, "wait_cqe=%d\n", ret);
329 			return -1;
330 		}
331 
332 		expected = (cqe->user_data == 1) ? -ECANCELED : -ETIME;
333 		if (expected != cqe->res) {
334 			fprintf(stderr, "cqe %d, res %d, wanted %d\n",
335 					(int)cqe->user_data, cqe->res, expected);
336 			goto err;
337 		}
338 		io_uring_cqe_seen(ring, cqe);
339 	}
340 
341 	close(connect_fd[0]);
342 	close(connect_fd[1]);
343 	close(accept_fd);
344 	return 0;
345 
346 err:
347 	if (connect_fd[0] != -1)
348 		close(connect_fd[0]);
349 	if (connect_fd[1] != -1)
350 		close(connect_fd[1]);
351 
352 	if (accept_fd != -1)
353 		close(accept_fd);
354 	return -1;
355 }
356 
main(int argc,char * argv[])357 int main(int argc, char *argv[])
358 {
359 	struct io_uring ring;
360 	int ret;
361 
362 	if (argc > 1)
363 		return 0;
364 
365 	ret = io_uring_queue_init(8, &ring, 0);
366 	if (ret) {
367 		fprintf(stderr, "io_uring_queue_setup() = %d\n", ret);
368 		return 1;
369 	}
370 
371 	srand(getpid());
372 	use_port = (rand() % 61440) + 4096;
373 	use_port = htons(use_port);
374 	use_addr = inet_addr("127.0.0.1");
375 
376 	ret = test_connect_with_no_peer(&ring);
377 	if (ret == -1) {
378 		fprintf(stderr, "test_connect_with_no_peer(): failed\n");
379 		return 1;
380 	}
381 	if (no_connect)
382 		return 0;
383 
384 	ret = test_connect(&ring);
385 	if (ret == -1) {
386 		fprintf(stderr, "test_connect(): failed\n");
387 		return 1;
388 	}
389 
390 	ret = test_connect_timeout(&ring);
391 	if (ret == -1) {
392 		fprintf(stderr, "test_connect_timeout(): failed\n");
393 		return 1;
394 	}
395 
396 	io_uring_queue_exit(&ring);
397 	return 0;
398 }
399