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