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