Fix testing with read-only source directory.
[glibc.git] / sunrpc / tst-udp-timeout.c
blob7ceba984320f497f652a42c3e6b617da7862ee8f
1 /* Test timeout handling in the UDP client.
2 Copyright (C) 2017 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
19 #include <netinet/in.h>
20 #include <rpc/clnt.h>
21 #include <rpc/svc.h>
22 #include <stdbool.h>
23 #include <string.h>
24 #include <support/check.h>
25 #include <support/namespace.h>
26 #include <support/test-driver.h>
27 #include <support/xsocket.h>
28 #include <support/xunistd.h>
29 #include <sys/socket.h>
30 #include <time.h>
31 #include <unistd.h>
33 /* Test data serialization and deserialization. */
35 struct test_query
37 uint32_t a;
38 uint32_t b;
39 uint32_t timeout_ms;
40 uint32_t wait_for_seq;
41 uint32_t garbage_packets;
44 static bool_t
45 xdr_test_query (XDR *xdrs, void *data, ...)
47 struct test_query *p = data;
48 return xdr_uint32_t (xdrs, &p->a)
49 && xdr_uint32_t (xdrs, &p->b)
50 && xdr_uint32_t (xdrs, &p->timeout_ms)
51 && xdr_uint32_t (xdrs, &p->wait_for_seq)
52 && xdr_uint32_t (xdrs, &p->garbage_packets);
55 struct test_response
57 uint32_t seq;
58 uint32_t sum;
61 static bool_t
62 xdr_test_response (XDR *xdrs, void *data, ...)
64 struct test_response *p = data;
65 return xdr_uint32_t (xdrs, &p->seq)
66 && xdr_uint32_t (xdrs, &p->sum);
69 /* Implementation of the test server. */
71 enum
73 /* RPC parameters, chosen at random. */
74 PROGNUM = 15717,
75 VERSNUM = 13689,
77 /* Main RPC operation. */
78 PROC_ADD = 1,
80 /* Reset the sequence number. */
81 PROC_RESET_SEQ,
83 /* Request process termination. */
84 PROC_EXIT,
86 /* Special exit status to mark successful processing. */
87 EXIT_MARKER = 55,
90 static void
91 server_dispatch (struct svc_req *request, SVCXPRT *transport)
93 /* Query sequence number. */
94 static uint32_t seq = 0;
95 ++seq;
97 if (test_verbose)
98 printf ("info: server_dispatch seq=%u rq_proc=%lu\n",
99 seq, request->rq_proc);
101 switch (request->rq_proc)
103 case PROC_ADD:
105 struct test_query query;
106 memset (&query, 0xc0, sizeof (query));
107 TEST_VERIFY_EXIT
108 (svc_getargs (transport, xdr_test_query,
109 (void *) &query));
111 if (test_verbose)
112 printf (" a=%u b=%u timeout_ms=%u wait_for_seq=%u"
113 " garbage_packets=%u\n",
114 query.a, query.b, query.timeout_ms, query.wait_for_seq,
115 query.garbage_packets);
117 if (seq < query.wait_for_seq)
119 /* No response at this point. */
120 if (test_verbose)
121 printf (" skipped response\n");
122 break;
125 if (query.garbage_packets > 0)
127 int per_packet_timeout;
128 if (query.timeout_ms > 0)
129 per_packet_timeout
130 = query.timeout_ms * 1000 / query.garbage_packets;
131 else
132 per_packet_timeout = 0;
134 char buf[20];
135 memset (&buf, 0xc0, sizeof (buf));
136 for (int i = 0; i < query.garbage_packets; ++i)
138 /* 13 is relatively prime to 20 = sizeof (buf) + 1, so
139 the len variable will cover the entire interval
140 [0, 20] if query.garbage_packets is sufficiently
141 large. */
142 size_t len = (i * 13 + 1) % (sizeof (buf) + 1);
143 TEST_VERIFY (sendto (transport->xp_sock,
144 buf, len, MSG_NOSIGNAL,
145 (struct sockaddr *) &transport->xp_raddr,
146 transport->xp_addrlen) == len);
147 if (per_packet_timeout > 0)
148 usleep (per_packet_timeout);
151 else if (query.timeout_ms > 0)
152 usleep (query.timeout_ms * 1000);
154 struct test_response response =
156 .seq = seq,
157 .sum = query.a + query.b,
159 TEST_VERIFY (svc_sendreply (transport, xdr_test_response,
160 (void *) &response));
162 break;
164 case PROC_RESET_SEQ:
165 seq = 0;
166 TEST_VERIFY (svc_sendreply (transport, (xdrproc_t) xdr_void, NULL));
167 break;
169 case PROC_EXIT:
170 TEST_VERIFY (svc_sendreply (transport, (xdrproc_t) xdr_void, NULL));
171 _exit (EXIT_MARKER);
172 break;
174 default:
175 FAIL_EXIT1 ("invalid rq_proc value: %lu", request->rq_proc);
176 break;
180 /* Implementation of the test client. */
182 static struct test_response
183 test_call (CLIENT *clnt, int proc, struct test_query query,
184 struct timeval timeout)
186 if (test_verbose)
187 printf ("info: test_call proc=%d timeout=%lu.%06lu\n",
188 proc, (unsigned long) timeout.tv_sec,
189 (unsigned long) timeout.tv_usec);
190 struct test_response response;
191 TEST_VERIFY_EXIT (clnt_call (clnt, proc,
192 xdr_test_query, (void *) &query,
193 xdr_test_response, (void *) &response,
194 timeout)
195 == RPC_SUCCESS);
196 return response;
199 static void
200 test_call_timeout (CLIENT *clnt, int proc, struct test_query query,
201 struct timeval timeout)
203 struct test_response response;
204 TEST_VERIFY (clnt_call (clnt, proc,
205 xdr_test_query, (void *) &query,
206 xdr_test_response, (void *) &response,
207 timeout)
208 == RPC_TIMEDOUT);
211 /* Complete one regular RPC call to drain the server socket
212 buffer. Resets the sequence number. */
213 static void
214 test_call_flush (CLIENT *clnt)
216 /* This needs a longer timeout to flush out all pending requests.
217 The choice of 5 seconds is larger than the per-response timeouts
218 requested via the timeout_ms field. */
219 if (test_verbose)
220 printf ("info: flushing pending queries\n");
221 TEST_VERIFY_EXIT (clnt_call (clnt, PROC_RESET_SEQ,
222 (xdrproc_t) xdr_void, NULL,
223 (xdrproc_t) xdr_void, NULL,
224 ((struct timeval) { 5, 0 }))
225 == RPC_SUCCESS);
228 /* Return the number seconds since an arbitrary point in time. */
229 static double
230 get_ticks (void)
233 struct timespec ts;
234 if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0)
235 return ts.tv_sec + ts.tv_nsec * 1e-9;
238 struct timeval tv;
239 TEST_VERIFY_EXIT (gettimeofday (&tv, NULL) == 0);
240 return tv.tv_sec + tv.tv_usec * 1e-6;
244 static void
245 test_udp_server (int port)
247 struct sockaddr_in sin =
249 .sin_family = AF_INET,
250 .sin_addr.s_addr = htonl (INADDR_LOOPBACK),
251 .sin_port = htons (port)
253 int sock = RPC_ANYSOCK;
255 /* The client uses a 1.5 second timeout for retries. The timeouts
256 are arbitrary, but chosen so that there is a substantial gap
257 between them, but the total time spent waiting is not too
258 large. */
259 CLIENT *clnt = clntudp_create (&sin, PROGNUM, VERSNUM,
260 (struct timeval) { 1, 500 * 1000 },
261 &sock);
262 TEST_VERIFY_EXIT (clnt != NULL);
264 /* Basic call/response test. */
265 struct test_response response = test_call
266 (clnt, PROC_ADD,
267 (struct test_query) { .a = 17, .b = 4 },
268 (struct timeval) { 3, 0 });
269 TEST_VERIFY (response.sum == 21);
270 TEST_VERIFY (response.seq == 1);
272 /* Check that garbage packets do not interfere with timeout
273 processing. */
274 double before = get_ticks ();
275 response = test_call
276 (clnt, PROC_ADD,
277 (struct test_query) {
278 .a = 19, .b = 4, .timeout_ms = 500, .garbage_packets = 21,
280 (struct timeval) { 3, 0 });
281 TEST_VERIFY (response.sum == 23);
282 TEST_VERIFY (response.seq == 2);
283 double after = get_ticks ();
284 if (test_verbose)
285 printf ("info: 21 garbage packets took %f seconds\n", after - before);
286 /* Expected timeout is 0.5 seconds. Add some slack in case process
287 scheduling delays processing the query or response, but do not
288 accept a retry (which would happen at 1.5 seconds). */
289 TEST_VERIFY (0.5 <= after - before);
290 TEST_VERIFY (after - before < 1.2);
291 test_call_flush (clnt);
293 /* Check that missing a response introduces a 1.5 second timeout, as
294 requested when calling clntudp_create. */
295 before = get_ticks ();
296 response = test_call
297 (clnt, PROC_ADD,
298 (struct test_query) { .a = 170, .b = 40, .wait_for_seq = 2 },
299 (struct timeval) { 3, 0 });
300 TEST_VERIFY (response.sum == 210);
301 TEST_VERIFY (response.seq == 2);
302 after = get_ticks ();
303 if (test_verbose)
304 printf ("info: skipping one response took %f seconds\n",
305 after - before);
306 /* Expected timeout is 1.5 seconds. Do not accept a second retry
307 (which would happen at 3 seconds). */
308 TEST_VERIFY (1.5 <= after - before);
309 TEST_VERIFY (after - before < 2.9);
310 test_call_flush (clnt);
312 /* Check that the overall timeout wins against the per-query
313 timeout. */
314 before = get_ticks ();
315 test_call_timeout
316 (clnt, PROC_ADD,
317 (struct test_query) { .a = 170, .b = 41, .wait_for_seq = 2 },
318 (struct timeval) { 0, 750 * 1000 });
319 after = get_ticks ();
320 if (test_verbose)
321 printf ("info: 0.75 second timeout took %f seconds\n",
322 after - before);
323 TEST_VERIFY (0.75 <= after - before);
324 TEST_VERIFY (after - before < 1.4);
325 test_call_flush (clnt);
327 for (int with_garbage = 0; with_garbage < 2; ++with_garbage)
329 /* Check that no response at all causes the client to bail out. */
330 before = get_ticks ();
331 test_call_timeout
332 (clnt, PROC_ADD,
333 (struct test_query) {
334 .a = 170, .b = 40, .timeout_ms = 1200,
335 .garbage_packets = with_garbage * 21
337 (struct timeval) { 0, 750 * 1000 });
338 after = get_ticks ();
339 if (test_verbose)
340 printf ("info: test_udp_server: 0.75 second timeout took %f seconds"
341 " (garbage %d)\n",
342 after - before, with_garbage);
343 TEST_VERIFY (0.75 <= after - before);
344 TEST_VERIFY (after - before < 1.4);
345 test_call_flush (clnt);
347 /* As above, but check the total timeout. */
348 before = get_ticks ();
349 test_call_timeout
350 (clnt, PROC_ADD,
351 (struct test_query) {
352 .a = 170, .b = 40, .timeout_ms = 3000,
353 .garbage_packets = with_garbage * 30
355 (struct timeval) { 2, 500 * 1000 });
356 after = get_ticks ();
357 if (test_verbose)
358 printf ("info: test_udp_server: 2.5 second timeout took %f seconds"
359 " (garbage %d)\n",
360 after - before, with_garbage);
361 TEST_VERIFY (2.5 <= after - before);
362 TEST_VERIFY (after - before < 3.0);
363 test_call_flush (clnt);
366 TEST_VERIFY_EXIT (clnt_call (clnt, PROC_EXIT,
367 (xdrproc_t) xdr_void, NULL,
368 (xdrproc_t) xdr_void, NULL,
369 ((struct timeval) { 5, 0 }))
370 == RPC_SUCCESS);
371 clnt_destroy (clnt);
374 static int
375 do_test (void)
377 support_become_root ();
378 support_enter_network_namespace ();
380 SVCXPRT *transport = svcudp_create (RPC_ANYSOCK);
381 TEST_VERIFY_EXIT (transport != NULL);
382 TEST_VERIFY (svc_register (transport, PROGNUM, VERSNUM, server_dispatch, 0));
384 pid_t pid = xfork ();
385 if (pid == 0)
387 svc_run ();
388 FAIL_EXIT1 ("supposed to be unreachable");
390 test_udp_server (transport->xp_port);
392 int status;
393 xwaitpid (pid, &status, 0);
394 TEST_VERIFY (WIFEXITED (status) && WEXITSTATUS (status) == EXIT_MARKER);
396 SVC_DESTROY (transport);
397 return 0;
400 /* The minimum run time is around 17 seconds. */
401 #define TIMEOUT 25
402 #include <support/test-driver.c>