tst-udp-nonblocking.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /* Test non-blocking use of the UDP client.
  2. Copyright (C) 2017-2019 Free Software Foundation, Inc.
  3. This file is part of the GNU C Library.
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. The GNU C Library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with the GNU C Library; if not, see
  14. <http://www.gnu.org/licenses/>. */
  15. #include <netinet/in.h>
  16. #include <rpc/clnt.h>
  17. #include <rpc/svc.h>
  18. #include <stdbool.h>
  19. #include <string.h>
  20. #include <support/check.h>
  21. #include <support/namespace.h>
  22. #include <support/test-driver.h>
  23. #include <support/xsocket.h>
  24. #include <support/xunistd.h>
  25. #include <sys/socket.h>
  26. #include <time.h>
  27. #include <unistd.h>
  28. /* Test data serialization and deserialization. */
  29. struct test_query
  30. {
  31. uint32_t a;
  32. uint32_t b;
  33. uint32_t timeout_ms;
  34. };
  35. static bool_t
  36. xdr_test_query (XDR *xdrs, void *data, ...)
  37. {
  38. struct test_query *p = data;
  39. return xdr_uint32_t (xdrs, &p->a)
  40. && xdr_uint32_t (xdrs, &p->b)
  41. && xdr_uint32_t (xdrs, &p->timeout_ms);
  42. }
  43. struct test_response
  44. {
  45. uint32_t server_id;
  46. uint32_t seq;
  47. uint32_t sum;
  48. };
  49. static bool_t
  50. xdr_test_response (XDR *xdrs, void *data, ...)
  51. {
  52. struct test_response *p = data;
  53. return xdr_uint32_t (xdrs, &p->server_id)
  54. && xdr_uint32_t (xdrs, &p->seq)
  55. && xdr_uint32_t (xdrs, &p->sum);
  56. }
  57. /* Implementation of the test server. */
  58. enum
  59. {
  60. /* Number of test servers to run. */
  61. SERVER_COUNT = 3,
  62. /* RPC parameters, chosen at random. */
  63. PROGNUM = 8242,
  64. VERSNUM = 19654,
  65. /* Main RPC operation. */
  66. PROC_ADD = 1,
  67. /* Request process termination. */
  68. PROC_EXIT,
  69. /* Special exit status to mark successful processing. */
  70. EXIT_MARKER = 55,
  71. };
  72. /* Set by the parent process to tell test servers apart. */
  73. static int server_id;
  74. /* Implementation of the test server. */
  75. static void
  76. server_dispatch (struct svc_req *request, SVCXPRT *transport)
  77. {
  78. /* Query sequence number. */
  79. static uint32_t seq = 0;
  80. ++seq;
  81. static bool proc_add_seen;
  82. if (test_verbose)
  83. printf ("info: server_dispatch server_id=%d seq=%u rq_proc=%lu\n",
  84. server_id, seq, request->rq_proc);
  85. switch (request->rq_proc)
  86. {
  87. case PROC_ADD:
  88. {
  89. struct test_query query;
  90. memset (&query, 0xc0, sizeof (query));
  91. TEST_VERIFY_EXIT
  92. (svc_getargs (transport, xdr_test_query,
  93. (void *) &query));
  94. if (test_verbose)
  95. printf (" a=%u b=%u timeout_ms=%u\n",
  96. query.a, query.b, query.timeout_ms);
  97. usleep (query.timeout_ms * 1000);
  98. struct test_response response =
  99. {
  100. .server_id = server_id,
  101. .seq = seq,
  102. .sum = query.a + query.b,
  103. };
  104. TEST_VERIFY (svc_sendreply (transport, xdr_test_response,
  105. (void *) &response));
  106. if (test_verbose)
  107. printf (" server id %d response seq=%u sent\n", server_id, seq);
  108. proc_add_seen = true;
  109. }
  110. break;
  111. case PROC_EXIT:
  112. TEST_VERIFY (proc_add_seen);
  113. TEST_VERIFY (svc_sendreply (transport, (xdrproc_t) xdr_void, NULL));
  114. _exit (EXIT_MARKER);
  115. break;
  116. default:
  117. FAIL_EXIT1 ("invalid rq_proc value: %lu", request->rq_proc);
  118. break;
  119. }
  120. }
  121. /* Return the number seconds since an arbitrary point in time. */
  122. static double
  123. get_ticks (void)
  124. {
  125. {
  126. struct timespec ts;
  127. if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0)
  128. return ts.tv_sec + ts.tv_nsec * 1e-9;
  129. }
  130. {
  131. struct timeval tv;
  132. TEST_VERIFY_EXIT (gettimeofday (&tv, NULL) == 0);
  133. return tv.tv_sec + tv.tv_usec * 1e-6;
  134. }
  135. }
  136. static int
  137. do_test (void)
  138. {
  139. support_become_root ();
  140. support_enter_network_namespace ();
  141. /* Information about the test servers. */
  142. struct
  143. {
  144. SVCXPRT *transport;
  145. struct sockaddr_in address;
  146. pid_t pid;
  147. uint32_t xid;
  148. } servers[SERVER_COUNT];
  149. /* Spawn the test servers. */
  150. for (int i = 0; i < SERVER_COUNT; ++i)
  151. {
  152. servers[i].transport = svcudp_create (RPC_ANYSOCK);
  153. TEST_VERIFY_EXIT (servers[i].transport != NULL);
  154. servers[i].address = (struct sockaddr_in)
  155. {
  156. .sin_family = AF_INET,
  157. .sin_addr.s_addr = htonl (INADDR_LOOPBACK),
  158. .sin_port = htons (servers[i].transport->xp_port),
  159. };
  160. servers[i].xid = 0xabcd0101 + i;
  161. if (test_verbose)
  162. printf ("info: setting up server %d xid=%x on port %d\n",
  163. i, servers[i].xid, servers[i].transport->xp_port);
  164. server_id = i;
  165. servers[i].pid = xfork ();
  166. if (servers[i].pid == 0)
  167. {
  168. TEST_VERIFY (svc_register (servers[i].transport,
  169. PROGNUM, VERSNUM, server_dispatch, 0));
  170. svc_run ();
  171. FAIL_EXIT1 ("supposed to be unreachable");
  172. }
  173. /* We need to close the socket so that we do not accidentally
  174. consume the request. */
  175. TEST_VERIFY (close (servers[i].transport->xp_sock) == 0);
  176. }
  177. /* The following code mirrors what ypbind does. */
  178. /* Copied from clnt_udp.c (like ypbind). */
  179. struct cu_data
  180. {
  181. int cu_sock;
  182. bool_t cu_closeit;
  183. struct sockaddr_in cu_raddr;
  184. int cu_rlen;
  185. struct timeval cu_wait;
  186. struct timeval cu_total;
  187. struct rpc_err cu_error;
  188. XDR cu_outxdrs;
  189. u_int cu_xdrpos;
  190. u_int cu_sendsz;
  191. char *cu_outbuf;
  192. u_int cu_recvsz;
  193. char cu_inbuf[1];
  194. };
  195. int client_socket = xsocket (AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
  196. CLIENT *clnt = clntudp_create (&servers[0].address, PROGNUM, VERSNUM,
  197. /* 5 seconds per-response timeout. */
  198. ((struct timeval) { 5, 0 }),
  199. &client_socket);
  200. TEST_VERIFY (clnt != NULL);
  201. clnt->cl_auth = authunix_create_default ();
  202. {
  203. struct timeval zero = { 0, 0 };
  204. TEST_VERIFY (clnt_control (clnt, CLSET_TIMEOUT, (void *) &zero));
  205. }
  206. /* Poke at internal data structures (like ypbind). */
  207. struct cu_data *cu = (struct cu_data *) clnt->cl_private;
  208. /* Send a ping to each server. */
  209. double before_pings = get_ticks ();
  210. for (int i = 0; i < SERVER_COUNT; ++i)
  211. {
  212. if (test_verbose)
  213. printf ("info: sending server %d ping\n", i);
  214. /* Reset the xid because it is changed by each invocation of
  215. clnt_call. Subtract one to compensate for the xid update
  216. during the call. */
  217. *((uint32_t *) (cu->cu_outbuf)) = servers[i].xid - 1;
  218. cu->cu_raddr = servers[i].address;
  219. struct test_query query = { .a = 100, .b = i + 1 };
  220. if (i == 1)
  221. /* Shorter timeout to prefer this server. These timeouts must
  222. be much shorter than the 5-second per-response timeout
  223. configured with clntudp_create. */
  224. query.timeout_ms = 750;
  225. else
  226. query.timeout_ms = 1500;
  227. struct test_response response = { 0 };
  228. /* NB: Do not check the return value. The server reply will
  229. prove that the call worked. */
  230. double before_one_ping = get_ticks ();
  231. clnt_call (clnt, PROC_ADD,
  232. xdr_test_query, (void *) &query,
  233. xdr_test_response, (void *) &response,
  234. ((struct timeval) { 0, 0 }));
  235. double after_one_ping = get_ticks ();
  236. if (test_verbose)
  237. printf ("info: non-blocking send took %f seconds\n",
  238. after_one_ping - before_one_ping);
  239. /* clnt_call should return immediately. Accept some delay in
  240. case the process is descheduled. */
  241. TEST_VERIFY (after_one_ping - before_one_ping < 0.3);
  242. }
  243. /* Collect the non-blocking response. */
  244. if (test_verbose)
  245. printf ("info: collecting response\n");
  246. struct test_response response = { 0 };
  247. TEST_VERIFY
  248. (clnt_call (clnt, PROC_ADD, NULL, NULL,
  249. xdr_test_response, (void *) &response,
  250. ((struct timeval) { 0, 0 })) == RPC_SUCCESS);
  251. double after_pings = get_ticks ();
  252. if (test_verbose)
  253. printf ("info: send/receive took %f seconds\n",
  254. after_pings - before_pings);
  255. /* Expected timeout is 0.75 seconds. */
  256. TEST_VERIFY (0.75 <= after_pings - before_pings);
  257. TEST_VERIFY (after_pings - before_pings < 1.2);
  258. uint32_t xid;
  259. memcpy (&xid, &cu->cu_inbuf, sizeof (xid));
  260. if (test_verbose)
  261. printf ("info: non-blocking response: xid=%x server_id=%u seq=%u sum=%u\n",
  262. xid, response.server_id, response.seq, response.sum);
  263. /* Check that the reply from the preferred server was used. */
  264. TEST_VERIFY (servers[1].xid == xid);
  265. TEST_VERIFY (response.server_id == 1);
  266. TEST_VERIFY (response.seq == 1);
  267. TEST_VERIFY (response.sum == 102);
  268. auth_destroy (clnt->cl_auth);
  269. clnt_destroy (clnt);
  270. for (int i = 0; i < SERVER_COUNT; ++i)
  271. {
  272. if (test_verbose)
  273. printf ("info: requesting server %d termination\n", i);
  274. client_socket = RPC_ANYSOCK;
  275. clnt = clntudp_create (&servers[i].address, PROGNUM, VERSNUM,
  276. ((struct timeval) { 5, 0 }),
  277. &client_socket);
  278. TEST_VERIFY_EXIT (clnt != NULL);
  279. TEST_VERIFY (clnt_call (clnt, PROC_EXIT,
  280. (xdrproc_t) xdr_void, NULL,
  281. (xdrproc_t) xdr_void, NULL,
  282. ((struct timeval) { 3, 0 })) == RPC_SUCCESS);
  283. clnt_destroy (clnt);
  284. int status;
  285. xwaitpid (servers[i].pid, &status, 0);
  286. TEST_VERIFY (WIFEXITED (status) && WEXITSTATUS (status) == EXIT_MARKER);
  287. }
  288. return 0;
  289. }
  290. #include <support/test-driver.c>