• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: MIT */
2 /*
3  * Description: Check that {g,s}etsockopt CMD operations on sockets are
4  * consistent.
5  *
6  * The tests basically do the same socket operation using regular system calls
7  * and io_uring commands, and then compare the results.
8  */
9 
10 #include <stdio.h>
11 #include <assert.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <linux/tcp.h>
15 
16 #include "liburing.h"
17 #include "helpers.h"
18 
19 #define USERDATA 0xff42ff
20 #define MSG "foobarbaz"
21 
22 static int no_sock_opt;
23 
24 struct fds {
25 	int tx;
26 	int rx;
27 };
28 
create_sockets(void)29 static struct fds create_sockets(void)
30 {
31 	struct fds retval;
32 	int fd[2];
33 
34 	t_create_socket_pair(fd, true);
35 
36 	retval.tx = fd[0];
37 	retval.rx = fd[1];
38 
39 	return retval;
40 }
41 
create_ring(void)42 static struct io_uring create_ring(void)
43 {
44 	struct io_uring ring;
45 	int ring_flags = 0;
46 	int err;
47 
48 	err = io_uring_queue_init(32, &ring, ring_flags);
49 	assert(err == 0);
50 
51 	return ring;
52 }
53 
submit_cmd_sqe(struct io_uring * ring,int32_t fd,int op,int level,int optname,void * optval,int optlen,bool async)54 static int submit_cmd_sqe(struct io_uring *ring, int32_t fd,
55 			  int op, int level, int optname,
56 			  void *optval, int optlen,
57 			  bool async)
58 {
59 	struct io_uring_sqe *sqe;
60 	int err;
61 
62 	assert(fd > 0);
63 
64 	sqe = io_uring_get_sqe(ring);
65 	assert(sqe != NULL);
66 
67 	io_uring_prep_cmd_sock(sqe, op, fd, level, optname, optval, optlen);
68 	sqe->user_data = USERDATA;
69 	if (async)
70 		sqe->flags |= IOSQE_ASYNC;
71 
72 	/* Submitting SQE */
73 	err = io_uring_submit_and_wait(ring, 1);
74 	if (err != 1)
75 		fprintf(stderr, "Failure: io_uring_submit_and_wait returned %d\n", err);
76 
77 	return err;
78 }
79 
receive_cqe(struct io_uring * ring)80 static int receive_cqe(struct io_uring *ring)
81 {
82 	struct io_uring_cqe *cqe;
83 	int err;
84 
85 	err = io_uring_wait_cqe(ring, &cqe);
86 	assert(err ==  0);
87 	assert(cqe->user_data == USERDATA);
88 	io_uring_cqe_seen(ring, cqe);
89 
90 	/* Return the result of the operation */
91 	return cqe->res;
92 }
93 
94 /*
95  * Run getsock operation using SO_RCVBUF using io_uring cmd operation and
96  * getsockopt(2) and compare the results.
97  */
run_get_rcvbuf(struct io_uring * ring,struct fds * sockfds,bool async)98 static int run_get_rcvbuf(struct io_uring *ring, struct fds *sockfds, bool async)
99 {
100 	int sval, uval, ulen, err;
101 	unsigned int slen;
102 
103 	/* System call values */
104 	slen = sizeof(sval);
105 	/* io_uring values */
106 	ulen = sizeof(uval);
107 
108 	/* get through io_uring cmd */
109 	err = submit_cmd_sqe(ring, sockfds->rx, SOCKET_URING_OP_GETSOCKOPT,
110 			     SOL_SOCKET, SO_RCVBUF, &uval, ulen, async);
111 	assert(err == 1);
112 
113 	/* Wait for the CQE */
114 	err = receive_cqe(ring);
115 	if (err == -EOPNOTSUPP)
116 		return T_EXIT_SKIP;
117 	if (err < 0) {
118 		fprintf(stderr, "Error received. %d\n", err);
119 		return T_EXIT_FAIL;
120 	}
121 	/* The output of CQE->res contains the length */
122 	ulen = err;
123 
124 	/* Executes the same operation using system call */
125 	err = getsockopt(sockfds->rx, SOL_SOCKET, SO_RCVBUF, &sval, &slen);
126 	assert(err == 0);
127 
128 	/* Make sure that io_uring operation returns the same value as the systemcall */
129 	assert(ulen == slen);
130 	assert(uval == sval);
131 
132 	return T_EXIT_PASS;
133 }
134 
135 /*
136  * Run getsock operation using SO_PEERNAME using io_uring cmd operation
137  * and getsockopt(2) and compare the results.
138  */
run_get_peername(struct io_uring * ring,struct fds * sockfds,bool async)139 static int run_get_peername(struct io_uring *ring, struct fds *sockfds, bool async)
140 {
141 	struct sockaddr sval, uval = {};
142 	socklen_t slen = sizeof(sval);
143 	socklen_t ulen = sizeof(uval);
144 	int err;
145 
146 	/* Get values from the systemcall */
147 	err = getsockopt(sockfds->tx, SOL_SOCKET, SO_PEERNAME, &sval, &slen);
148 	assert(err == 0);
149 
150 	/* Getting SO_PEERNAME */
151 	err = submit_cmd_sqe(ring, sockfds->rx, SOCKET_URING_OP_GETSOCKOPT,
152 			     SOL_SOCKET, SO_PEERNAME, &uval, ulen, async);
153 	assert(err == 1);
154 
155 	/* Wait for the CQE */
156 	err = receive_cqe(ring);
157 	if (err == -EOPNOTSUPP || err == -EINVAL) {
158 		no_sock_opt = 1;
159 		return T_EXIT_SKIP;
160 	}
161 
162 	if (err < 0) {
163 		fprintf(stderr, "%s: Error in the CQE: %d\n", __func__, err);
164 		return T_EXIT_FAIL;
165 	}
166 
167 	/* The length comes from cqe->res, which is returned from receive_cqe() */
168 	ulen = err;
169 
170 	/* Make sure that io_uring operation returns the same values as the systemcall */
171 	assert(sval.sa_family == uval.sa_family);
172 	assert(slen == ulen);
173 
174 	return T_EXIT_PASS;
175 }
176 
177 /*
178  * Run getsockopt tests. Basically comparing io_uring output and systemcall results
179  */
run_getsockopt_test(struct io_uring * ring,struct fds * sockfds)180 static int run_getsockopt_test(struct io_uring *ring, struct fds *sockfds)
181 {
182 	int err;
183 
184 	err = run_get_peername(ring, sockfds, false);
185 	if (err)
186 		return err;
187 
188 	err = run_get_peername(ring, sockfds, true);
189 	if (err)
190 		return err;
191 
192 	err = run_get_rcvbuf(ring, sockfds, false);
193 	if (err)
194 		return err;
195 
196 	return run_get_rcvbuf(ring, sockfds, true);
197 }
198 
199 /*
200  * Given a `val` value, set it in SO_REUSEPORT using io_uring cmd, and read using
201  * getsockopt(2), and make sure they match.
202  */
run_setsockopt_reuseport(struct io_uring * ring,struct fds * sockfds,int val,bool async)203 static int run_setsockopt_reuseport(struct io_uring *ring, struct fds *sockfds,
204 				    int val, bool async)
205 {
206 	unsigned int slen, ulen;
207 	int sval, uval = val;
208 	int err;
209 
210 	slen = sizeof(sval);
211 	ulen = sizeof(uval);
212 
213 	/* Setting SO_REUSEPORT */
214 	err = submit_cmd_sqe(ring, sockfds->rx, SOCKET_URING_OP_SETSOCKOPT,
215 			     SOL_SOCKET, SO_REUSEPORT, &uval, ulen, async);
216 	assert(err == 1);
217 
218 	err = receive_cqe(ring);
219 	if (err == -EOPNOTSUPP)
220 		return T_EXIT_SKIP;
221 
222 	/* Get values from the systemcall */
223 	err = getsockopt(sockfds->rx, SOL_SOCKET, SO_REUSEPORT, &sval, &slen);
224 	assert(err == 0);
225 
226 	/* Make sure the set using io_uring cmd matches what systemcall returns */
227 	assert(uval == sval);
228 	assert(ulen == slen);
229 
230 	return T_EXIT_PASS;
231 }
232 
233 /*
234  * Given a `val` value, set the TCP_USER_TIMEOUT using io_uring and read using
235  * getsockopt(2). Make sure they match
236  */
run_setsockopt_usertimeout(struct io_uring * ring,struct fds * sockfds,int val,bool async)237 static int run_setsockopt_usertimeout(struct io_uring *ring, struct fds *sockfds,
238 				      int val, bool async)
239 {
240 	int optname = TCP_USER_TIMEOUT;
241 	int level = IPPROTO_TCP;
242 	unsigned int slen, ulen;
243 	int sval, uval, err;
244 
245 	slen = sizeof(uval);
246 	ulen = sizeof(uval);
247 
248 	uval = val;
249 
250 	/* Setting timeout */
251 	err = submit_cmd_sqe(ring, sockfds->rx, SOCKET_URING_OP_SETSOCKOPT,
252 			     level, optname, &uval, ulen, async);
253 	assert(err == 1);
254 
255 	err = receive_cqe(ring);
256 	if (err == -EOPNOTSUPP)
257 		return T_EXIT_SKIP;
258 	if (err < 0) {
259 		fprintf(stderr, "%s: Got an error: %d\n", __func__, err);
260 		return T_EXIT_FAIL;
261 	}
262 
263 	/* Get the value from the systemcall, to make sure it was set */
264 	err = getsockopt(sockfds->rx, level, optname, &sval, &slen);
265 	assert(err == 0);
266 	assert(uval == sval);
267 
268 	return T_EXIT_PASS;
269 }
270 
271 /* Test setsockopt() for SOL_SOCKET */
run_setsockopt_test(struct io_uring * ring,struct fds * sockfds)272 static int run_setsockopt_test(struct io_uring *ring, struct fds *sockfds)
273 {
274 	int err, i;
275 	int j;
276 
277 	for (j = 0; j < 2; j++) {
278 		bool async = j & 1;
279 
280 		for (i = 0; i <= 1; i++) {
281 			err = run_setsockopt_reuseport(ring, sockfds, i, async);
282 			if (err)
283 				return err;
284 		}
285 
286 		for (i = 1; i <= 10; i++) {
287 			err = run_setsockopt_usertimeout(ring, sockfds, i, async);
288 			if (err)
289 				return err;
290 		}
291 	}
292 
293 	return err;
294 }
295 
296 /* Send data through the sockets */
send_data(struct fds * s)297 static void send_data(struct fds *s)
298 {
299 	int written_bytes;
300 	/* Send data sing the sockstruct->send */
301 	written_bytes = write(s->tx, MSG, strlen(MSG));
302 	assert(written_bytes == strlen(MSG));
303 }
304 
main(int argc,char * argv[])305 int main(int argc, char *argv[])
306 {
307 	struct fds sockfds;
308 	struct io_uring ring;
309 	int err;
310 
311 	if (argc > 1)
312 		return T_EXIT_SKIP;
313 
314 	/* Simply io_uring ring creation */
315 	ring = create_ring();
316 
317 	/* Create sockets */
318 	sockfds = create_sockets();
319 
320 	send_data(&sockfds);
321 
322 	err = run_getsockopt_test(&ring, &sockfds);
323 	if (err) {
324 		if (err == T_EXIT_SKIP) {
325 			fprintf(stderr, "Skipping tests.\n");
326 			return T_EXIT_SKIP;
327 		}
328 		fprintf(stderr, "Failed to run test: %d\n", err);
329 		return err;
330 	}
331 	if (no_sock_opt)
332 		return T_EXIT_SKIP;
333 
334 	err = run_setsockopt_test(&ring, &sockfds);
335 	if (err) {
336 		if (err == T_EXIT_SKIP) {
337 			fprintf(stderr, "Skipping tests.\n");
338 			return T_EXIT_SKIP;
339 		}
340 		fprintf(stderr, "Failed to run test: %d\n", err);
341 		return err;
342 	}
343 
344 	io_uring_queue_exit(&ring);
345 	return err;
346 }
347