1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Configure and operate a TCP socket solely with io_uring.
4 */
5 #include <stdio.h>
6 #include <string.h>
7 #include <liburing.h>
8 #include <err.h>
9 #include <sys/mman.h>
10 #include <sys/wait.h>
11 #include <sys/socket.h>
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <netinet/ip.h>
15 #include "liburing.h"
16 #include "helpers.h"
17
msec_to_ts(struct __kernel_timespec * ts,unsigned int msec)18 static void msec_to_ts(struct __kernel_timespec *ts, unsigned int msec)
19 {
20 ts->tv_sec = msec / 1000;
21 ts->tv_nsec = (msec % 1000) * 1000000;
22 }
23
24 static const char *magic = "Hello World!";
25 static int use_port = 8000;
26
27 enum {
28 SRV_INDEX = 0,
29 CLI_INDEX,
30 CONN_INDEX,
31 };
32
connect_client(struct io_uring * ring,unsigned short peer_port)33 static int connect_client(struct io_uring *ring, unsigned short peer_port)
34 {
35 struct __kernel_timespec ts;
36 struct io_uring_sqe *sqe;
37 struct io_uring_cqe *cqe;
38 int head, ret, submitted = 0;
39 struct sockaddr_in peer_addr;
40 socklen_t addr_len = sizeof(peer_addr);
41
42 peer_addr.sin_family = AF_INET;
43 peer_addr.sin_port = peer_port;
44 peer_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
45
46 sqe = io_uring_get_sqe(ring);
47 io_uring_prep_socket_direct(sqe, AF_INET, SOCK_STREAM, 0,
48 CLI_INDEX, 0);
49 sqe->flags |= IOSQE_IO_LINK;
50
51 sqe = io_uring_get_sqe(ring);
52 io_uring_prep_connect(sqe, CLI_INDEX, (struct sockaddr*) &peer_addr, addr_len);
53 sqe->flags |= IOSQE_FIXED_FILE | IOSQE_IO_LINK;
54
55 sqe = io_uring_get_sqe(ring);
56 io_uring_prep_send(sqe, CLI_INDEX, magic, strlen(magic), 0);
57 sqe->flags |= IOSQE_FIXED_FILE;
58
59 submitted = ret = io_uring_submit(ring);
60 if (ret < 0)
61 return T_SETUP_SKIP;
62
63 msec_to_ts(&ts, 300);
64 ret = io_uring_wait_cqes(ring, &cqe, submitted, &ts, NULL);
65 if (ret < 0)
66 return T_SETUP_SKIP;
67
68 io_uring_for_each_cqe(ring, head, cqe) {
69 ret = cqe->res;
70 if (ret < 0)
71 return T_SETUP_SKIP;
72 } io_uring_cq_advance(ring, submitted);
73
74 return T_SETUP_OK;
75 }
76
setup_srv(struct io_uring * ring,struct sockaddr_in * server_addr)77 static int setup_srv(struct io_uring *ring, struct sockaddr_in *server_addr)
78 {
79 struct io_uring_sqe *sqe;
80 struct io_uring_cqe *cqe;
81 struct __kernel_timespec ts;
82 int ret, val, submitted;
83 unsigned head;
84
85 memset(server_addr, 0, sizeof(struct sockaddr_in));
86 server_addr->sin_family = AF_INET;
87 server_addr->sin_port = htons(use_port++);
88 server_addr->sin_addr.s_addr = htons(INADDR_ANY);
89
90 sqe = io_uring_get_sqe(ring);
91 io_uring_prep_socket_direct(sqe, AF_INET, SOCK_STREAM, 0, SRV_INDEX, 0);
92 sqe->flags |= IOSQE_IO_LINK;
93
94 sqe = io_uring_get_sqe(ring);
95 val = 1;
96 io_uring_prep_cmd_sock(sqe, SOCKET_URING_OP_SETSOCKOPT, 0, SOL_SOCKET,
97 SO_REUSEADDR, &val, sizeof(val));
98 sqe->flags |= IOSQE_FIXED_FILE | IOSQE_IO_LINK;
99
100 sqe = io_uring_get_sqe(ring);
101 io_uring_prep_bind(sqe, SRV_INDEX, (struct sockaddr *) server_addr,
102 sizeof(struct sockaddr_in));
103 sqe->flags |= IOSQE_FIXED_FILE | IOSQE_IO_LINK;
104
105 sqe = io_uring_get_sqe(ring);
106 io_uring_prep_listen(sqe, SRV_INDEX, 1);
107 sqe->flags |= IOSQE_FIXED_FILE;
108
109 submitted = ret = io_uring_submit(ring);
110 if (ret < 0) {
111 fprintf(stderr, "submission failed. %d\n", ret);
112 return T_EXIT_FAIL;
113 }
114
115 msec_to_ts(&ts, 300);
116 ret = io_uring_wait_cqes(ring, &cqe, ret, &ts, NULL);
117 if (ret < 0) {
118 fprintf(stderr, "submission failed. %d\n", ret);
119 return T_EXIT_FAIL;
120 }
121
122 io_uring_for_each_cqe(ring, head, cqe) {
123 ret = cqe->res;
124 if (ret < 0) {
125 fprintf(stderr, "Server startup failed. step %d got %d \n", head, ret);
126 return T_EXIT_FAIL;
127 }
128 } io_uring_cq_advance(ring, submitted);
129
130 return T_SETUP_OK;
131 }
132
test_good_server(unsigned int ring_flags)133 static int test_good_server(unsigned int ring_flags)
134 {
135 struct sockaddr_in server_addr;
136 struct __kernel_timespec ts;
137 struct io_uring_sqe *sqe;
138 struct io_uring_cqe *cqe;
139 struct io_uring ring;
140 int ret;
141 int fds[3];
142 char buf[1024];
143
144 memset(fds, -1, sizeof(fds));
145
146 ret = t_create_ring(10, &ring, ring_flags | IORING_SETUP_SUBMIT_ALL);
147 if (ret < 0) {
148 fprintf(stderr, "queue_init: %s\n", strerror(-ret));
149 return T_SETUP_SKIP;
150 }
151
152 ret = io_uring_register_files(&ring, fds, 3);
153 if (ret) {
154 fprintf(stderr, "server file register %d\n", ret);
155 return T_SETUP_SKIP;
156 }
157
158 ret = setup_srv(&ring, &server_addr);
159 if (ret != T_SETUP_OK) {
160 fprintf(stderr, "srv startup failed.\n");
161 return T_EXIT_FAIL;
162 }
163
164 if (connect_client(&ring, server_addr.sin_port) != T_SETUP_OK) {
165 fprintf(stderr, "cli startup failed.\n");
166 return T_SETUP_SKIP;
167 }
168
169 /* Wait for a request */
170 sqe = io_uring_get_sqe(&ring);
171 io_uring_prep_accept_direct(sqe, SRV_INDEX, NULL, NULL, 0, CONN_INDEX);
172 sqe->flags |= IOSQE_FIXED_FILE;
173
174 io_uring_submit(&ring);
175 io_uring_wait_cqe(&ring, &cqe);
176 if (cqe->res < 0) {
177 fprintf(stderr, "accept failed. %d\n", cqe->res);
178 return T_EXIT_FAIL;
179 }
180 io_uring_cqe_seen(&ring, cqe);
181
182 sqe = io_uring_get_sqe(&ring);
183 io_uring_prep_recv(sqe, CONN_INDEX, buf, sizeof(buf), 0);
184 sqe->flags |= IOSQE_FIXED_FILE;
185
186 io_uring_submit(&ring);
187 io_uring_wait_cqe_timeout(&ring, &cqe, &ts);
188
189 if (cqe->res < 0) {
190 fprintf(stderr, "bad receive cqe. %d\n", cqe->res);
191 return T_EXIT_FAIL;
192 }
193 ret = cqe->res;
194 io_uring_cqe_seen(&ring, cqe);
195
196 io_uring_queue_exit(&ring);
197
198 if (ret != strlen(magic) || strncmp(buf, magic, ret)) {
199 fprintf(stderr, "didn't receive expected string. Got %d '%s'\n", ret, buf);
200 return T_EXIT_FAIL;
201 }
202
203 return T_EXIT_PASS;
204 }
205
test_bad_bind(void)206 static int test_bad_bind(void)
207 {
208 struct sockaddr_in server_addr;
209 struct io_uring_sqe *sqe;
210 struct io_uring_cqe *cqe;
211 struct io_uring ring;
212 int sock = -1, err;
213 int ret = T_EXIT_FAIL;
214
215 memset(&server_addr, 0, sizeof(struct sockaddr_in));
216 server_addr.sin_family = AF_INET;
217 server_addr.sin_port = htons(9001);
218 server_addr.sin_addr.s_addr = htons(INADDR_ANY);
219
220 err = t_create_ring(1, &ring, 0);
221 if (err < 0) {
222 fprintf(stderr, "queue_init: %s\n", strerror(-ret));
223 return T_SETUP_SKIP;
224 }
225
226 sock = socket(AF_INET, SOCK_STREAM, 0);
227 if (sock < 0) {
228 perror("socket");
229 goto fail;
230 }
231
232 /* Bind with size 0 */
233 sqe = io_uring_get_sqe(&ring);
234 io_uring_prep_bind(sqe, sock, (struct sockaddr *) &server_addr, 0);
235 err = io_uring_submit(&ring);
236 if (err < 0)
237 goto fail;
238
239 err = io_uring_wait_cqe(&ring, &cqe);
240 if (err)
241 goto fail;
242
243 if (cqe->res != -EINVAL)
244 goto fail;
245 io_uring_cqe_seen(&ring, cqe);
246
247 /* Bind with bad fd */
248 sqe = io_uring_get_sqe(&ring);
249 io_uring_prep_bind(sqe, 0, (struct sockaddr *) &server_addr, sizeof(struct sockaddr_in));
250 err = io_uring_submit(&ring);
251 if (err < 0)
252 goto fail;
253
254 err = io_uring_wait_cqe(&ring, &cqe);
255 if (err)
256 goto fail;
257 if (cqe->res != -ENOTSOCK)
258 goto fail;
259 io_uring_cqe_seen(&ring, cqe);
260
261 ret = T_EXIT_PASS;
262
263 /* bind with weird value */
264 sqe = io_uring_get_sqe(&ring);
265 io_uring_prep_bind(sqe, sock, (struct sockaddr *) &server_addr, sizeof(struct sockaddr_in));
266 sqe->rw_flags = 1;
267 err = io_uring_submit(&ring);
268 if (err < 0)
269 goto fail;
270
271 err = io_uring_wait_cqe(&ring, &cqe);
272 if (err)
273 goto fail;
274 if (cqe->res != -EINVAL)
275 goto fail;
276 io_uring_cqe_seen(&ring, cqe);
277
278 ret = T_EXIT_PASS;
279
280 fail:
281 io_uring_queue_exit(&ring);
282 if (sock != -1)
283 close(sock);
284 return ret;
285 }
286
test_bad_listen(void)287 static int test_bad_listen(void)
288 {
289 struct sockaddr_in server_addr;
290 struct io_uring_sqe *sqe;
291 struct io_uring_cqe *cqe;
292 struct io_uring ring;
293 int sock = -1, err;
294 int ret = T_EXIT_FAIL;
295
296 memset(&server_addr, 0, sizeof(struct sockaddr_in));
297 server_addr.sin_family = AF_INET;
298 server_addr.sin_port = htons(8001);
299 server_addr.sin_addr.s_addr = htons(INADDR_ANY);
300
301 err = t_create_ring(1, &ring, 0);
302 if (err < 0) {
303 fprintf(stderr, "queue_init: %d\n", err);
304 return T_SETUP_SKIP;
305 }
306
307 sock = socket(AF_INET, SOCK_STREAM, 0);
308 if (sock < 0) {
309 perror("socket");
310 goto fail;
311 }
312
313 err = t_bind_ephemeral_port(sock, &server_addr);
314 if (err) {
315 fprintf(stderr, "bind: %s\n", strerror(-err));
316 goto fail;
317 }
318
319 /* listen on bad sock */
320 sqe = io_uring_get_sqe(&ring);
321 io_uring_prep_listen(sqe, 0, 1);
322 err = io_uring_submit(&ring);
323 if (err < 0)
324 goto fail;
325
326 err = io_uring_wait_cqe(&ring, &cqe);
327 if (err)
328 goto fail;
329
330 if (cqe->res != -ENOTSOCK)
331 goto fail;
332 io_uring_cqe_seen(&ring, cqe);
333
334 /* listen with weird parameters */
335 sqe = io_uring_get_sqe(&ring);
336 io_uring_prep_listen(sqe, sock, 1);
337 sqe->addr2 = 0xffffff;
338 err = io_uring_submit(&ring);
339 if (err < 0)
340 goto fail;
341
342 err = io_uring_wait_cqe(&ring, &cqe);
343 if (err)
344 goto fail;
345
346 if (cqe->res != -EINVAL)
347 goto fail;
348 io_uring_cqe_seen(&ring, cqe);
349
350 ret = T_EXIT_PASS;
351 fail:
352 io_uring_queue_exit(&ring);
353 if (sock != -1)
354 close(sock);
355 return ret;
356 }
357
main(int argc,char * argv[])358 int main(int argc, char *argv[])
359 {
360 struct io_uring_probe *probe;
361 int ret;
362
363 if (argc > 1)
364 return 0;
365
366 /*
367 * This test is not supported on older kernels. Check for
368 * OP_LISTEN, since that is the last feature required to support
369 * it.
370 */
371 probe = io_uring_get_probe();
372 if (!probe)
373 return T_EXIT_SKIP;
374 if (!io_uring_opcode_supported(probe, IORING_OP_LISTEN))
375 return T_EXIT_SKIP;
376
377 ret = test_good_server(0);
378 if (ret) {
379 fprintf(stderr, "good 0 failed\n");
380 return T_EXIT_FAIL;
381 }
382
383 ret = test_good_server(IORING_SETUP_SINGLE_ISSUER|IORING_SETUP_DEFER_TASKRUN);
384 if (ret) {
385 fprintf(stderr, "good defer failed\n");
386 return T_EXIT_FAIL;
387 }
388
389 ret = test_good_server(IORING_SETUP_SQPOLL);
390 if (ret) {
391 fprintf(stderr, "good sqpoll failed\n");
392 return T_EXIT_FAIL;
393 }
394
395 ret = test_bad_bind();
396 if (ret) {
397 fprintf(stderr, "bad bind failed\n");
398 return T_EXIT_FAIL;
399 }
400
401 ret = test_bad_listen();
402 if (ret) {
403 fprintf(stderr, "bad listen failed\n");
404 return T_EXIT_FAIL;
405 }
406
407 return T_EXIT_PASS;
408 }
409