socket-getsetsock-cmd.c 7.5 KB

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