1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Check that CMD operations on sockets are consistent.
4 */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdint.h>
8 #include <assert.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <linux/sockios.h>
12 #include <sys/ioctl.h>
13
14 #include "liburing.h"
15 #include "helpers.h"
16
17 #define USERDATA 0x1234
18 #define MSG "foobarbaz"
19
20 static int no_io_cmd;
21
22 struct fds {
23 int tx;
24 int rx;
25 };
26
27 /* Create 2 sockets (tx, rx) given the socket type */
create_sockets(bool stream)28 static struct fds create_sockets(bool stream)
29 {
30 struct fds retval;
31 int fd[2];
32
33 t_create_socket_pair(fd, stream);
34
35 retval.tx = fd[0];
36 retval.rx = fd[1];
37
38 return retval;
39 }
40
create_sqe_and_submit(struct io_uring * ring,int32_t fd,int op)41 static int create_sqe_and_submit(struct io_uring *ring, int32_t fd, int op)
42 {
43 struct io_uring_sqe *sqe;
44 int ret;
45
46 assert(fd > 0);
47 sqe = io_uring_get_sqe(ring);
48 assert(sqe != NULL);
49
50 io_uring_prep_cmd_sock(sqe, op, fd, 0, 0, NULL, 0);
51 sqe->user_data = USERDATA;
52
53 /* Submitting SQE */
54 ret = io_uring_submit_and_wait(ring, 1);
55 if (ret <= 0)
56 return ret;
57
58 return 0;
59 }
60
receive_cqe(struct io_uring * ring)61 static int receive_cqe(struct io_uring *ring)
62 {
63 struct io_uring_cqe *cqe;
64 int err;
65
66 err = io_uring_wait_cqe(ring, &cqe);
67 assert(err == 0);
68 assert(cqe->user_data == USERDATA);
69 err = cqe->res;
70 io_uring_cqe_seen(ring, cqe);
71
72 /* Return the result of the operation */
73 return err;
74 }
75
send_data(struct fds * s,char * str)76 static ssize_t send_data(struct fds *s, char *str)
77 {
78 size_t written_bytes;
79
80 written_bytes = write(s->tx, str, strlen(str));
81 assert(written_bytes == strlen(MSG));
82
83 return written_bytes;
84 }
85
run_test(bool stream)86 static int run_test(bool stream)
87 {
88 struct fds sockfds;
89 ssize_t bytes_in, bytes_out;
90 struct io_uring ring;
91 size_t written_bytes;
92 int error;
93
94 /* Create three sockets */
95 sockfds = create_sockets(stream);
96 assert(sockfds.tx > 0);
97 assert(sockfds.rx > 0);
98 /* Send data sing the sockfds->send */
99 written_bytes = send_data(&sockfds, MSG);
100
101 /* Simply io_uring ring creation */
102 error = t_create_ring(1, &ring, 0);
103 if (error == T_SETUP_SKIP)
104 return error;
105 else if (error != T_SETUP_OK)
106 return T_EXIT_FAIL;
107
108 error = create_sqe_and_submit(&ring, sockfds.rx,
109 SOCKET_URING_OP_SIOCINQ);
110 if (error)
111 return T_EXIT_FAIL;
112 bytes_in = receive_cqe(&ring);
113 if (bytes_in < 0) {
114 if (bytes_in == -EINVAL || bytes_in == -EOPNOTSUPP) {
115 no_io_cmd = 1;
116 return T_EXIT_SKIP;
117 }
118 fprintf(stderr, "Bad return value %ld\n", (long) bytes_in);
119 return T_EXIT_FAIL;
120 }
121
122 error = create_sqe_and_submit(&ring, sockfds.tx,
123 SOCKET_URING_OP_SIOCOUTQ);
124 if (error)
125 return T_EXIT_FAIL;
126
127 bytes_out = receive_cqe(&ring);
128 if (bytes_in == -ENOTSUP || bytes_out == -ENOTSUP) {
129 fprintf(stderr, "Skipping tests. -ENOTSUP returned\n");
130 return T_EXIT_SKIP;
131 }
132
133 /*
134 * Assert the number of written bytes are either in the socket buffer
135 * or on the receive side
136 */
137 if (bytes_in + bytes_out != written_bytes) {
138 fprintf(stderr, "values does not match: %zu+%zu != %zu\n",
139 bytes_in, bytes_out, written_bytes);
140 return T_EXIT_FAIL;
141 }
142
143 io_uring_queue_exit(&ring);
144
145 return T_EXIT_PASS;
146 }
147
148 /*
149 * Make sure that siocoutq and siocinq returns the same value
150 * using ioctl(2) and uring commands for raw sockets
151 */
run_test_raw(void)152 static int run_test_raw(void)
153 {
154 int ioctl_siocoutq, ioctl_siocinq;
155 int uring_siocoutq, uring_siocinq;
156 struct io_uring ring;
157 int retry = 0, sock, error;
158
159 sock = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
160 if (sock == -1) {
161 /* You need root to create raw socket */
162 perror("Not able to create a raw socket");
163 return T_EXIT_SKIP;
164 }
165
166 /* Get the same operation using uring cmd */
167 error = t_create_ring(1, &ring, 0);
168 if (error == T_SETUP_SKIP)
169 return error;
170 else if (error != T_SETUP_OK)
171 return T_EXIT_FAIL;
172
173 again:
174 /* Simple SIOCOUTQ using ioctl */
175 error = ioctl(sock, SIOCOUTQ, &ioctl_siocoutq);
176 if (error < 0) {
177 fprintf(stderr, "Failed to run ioctl(SIOCOUTQ): %d\n", error);
178 return T_EXIT_FAIL;
179 }
180
181 error = ioctl(sock, SIOCINQ, &ioctl_siocinq);
182 if (error < 0) {
183 fprintf(stderr, "Failed to run ioctl(SIOCINQ): %d\n", error);
184 return T_EXIT_FAIL;
185 }
186
187 create_sqe_and_submit(&ring, sock, SOCKET_URING_OP_SIOCOUTQ);
188 uring_siocoutq = receive_cqe(&ring);
189
190 create_sqe_and_submit(&ring, sock, SOCKET_URING_OP_SIOCINQ);
191 uring_siocinq = receive_cqe(&ring);
192
193 /* Compare that both values (ioctl and uring CMD) should be similar */
194 if (uring_siocoutq != ioctl_siocoutq) {
195 if (!retry) {
196 retry = 1;
197 goto again;
198 }
199 fprintf(stderr, "values does not match: %d != %d\n",
200 uring_siocoutq, ioctl_siocoutq);
201 return T_EXIT_FAIL;
202 }
203 if (uring_siocinq != ioctl_siocinq) {
204 if (!retry) {
205 retry = 1;
206 goto again;
207 }
208 fprintf(stderr, "values does not match: %d != %d\n",
209 uring_siocinq, ioctl_siocinq);
210 return T_EXIT_FAIL;
211 }
212
213 return T_EXIT_PASS;
214 }
215
main(int argc,char * argv[])216 int main(int argc, char *argv[])
217 {
218 int err;
219
220 if (argc > 1)
221 return 0;
222
223 /* Test SOCK_STREAM */
224 err = run_test(true);
225 if (err)
226 return err;
227 if (no_io_cmd)
228 return T_EXIT_SKIP;
229
230 /* Test SOCK_DGRAM */
231 err = run_test(false);
232 if (err)
233 return err;
234
235 /* Test raw sockets */
236 return run_test_raw();
237 }
238