• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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