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