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>
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>
33 /* Test data serialization and deserialization. */
40 uint32_t wait_for_seq
;
41 uint32_t garbage_packets
;
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
);
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. */
73 /* RPC parameters, chosen at random. */
77 /* Main RPC operation. */
80 /* Reset the sequence number. */
83 /* Request process termination. */
86 /* Special exit status to mark successful processing. */
91 server_dispatch (struct svc_req
*request
, SVCXPRT
*transport
)
93 /* Query sequence number. */
94 static uint32_t seq
= 0;
98 printf ("info: server_dispatch seq=%u rq_proc=%lu\n",
99 seq
, request
->rq_proc
);
101 switch (request
->rq_proc
)
105 struct test_query query
;
106 memset (&query
, 0xc0, sizeof (query
));
108 (svc_getargs (transport
, xdr_test_query
,
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. */
121 printf (" skipped response\n");
125 if (query
.garbage_packets
> 0)
127 int per_packet_timeout
;
128 if (query
.timeout_ms
> 0)
130 = query
.timeout_ms
* 1000 / query
.garbage_packets
;
132 per_packet_timeout
= 0;
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
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
=
157 .sum
= query
.a
+ query
.b
,
159 TEST_VERIFY (svc_sendreply (transport
, xdr_test_response
,
160 (void *) &response
));
166 TEST_VERIFY (svc_sendreply (transport
, (xdrproc_t
) xdr_void
, NULL
));
170 TEST_VERIFY (svc_sendreply (transport
, (xdrproc_t
) xdr_void
, NULL
));
175 FAIL_EXIT1 ("invalid rq_proc value: %lu", request
->rq_proc
);
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
)
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
,
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
,
211 /* Complete one regular RPC call to drain the server socket
212 buffer. Resets the sequence number. */
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. */
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 }))
228 /* Return the number seconds since an arbitrary point in time. */
234 if (clock_gettime (CLOCK_MONOTONIC
, &ts
) == 0)
235 return ts
.tv_sec
+ ts
.tv_nsec
* 1e-9;
239 TEST_VERIFY_EXIT (gettimeofday (&tv
, NULL
) == 0);
240 return tv
.tv_sec
+ tv
.tv_usec
* 1e-6;
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
259 CLIENT
*clnt
= clntudp_create (&sin
, PROGNUM
, VERSNUM
,
260 (struct timeval
) { 1, 500 * 1000 },
262 TEST_VERIFY_EXIT (clnt
!= NULL
);
264 /* Basic call/response test. */
265 struct test_response response
= test_call
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
274 double before
= get_ticks ();
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 ();
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 ();
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 ();
304 printf ("info: skipping one response took %f seconds\n",
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
314 before
= get_ticks ();
317 (struct test_query
) { .a
= 170, .b
= 41, .wait_for_seq
= 2 },
318 (struct timeval
) { 0, 750 * 1000 });
319 after
= get_ticks ();
321 printf ("info: 0.75 second timeout took %f seconds\n",
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 ();
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 ();
340 printf ("info: test_udp_server: 0.75 second timeout took %f seconds"
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 ();
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 ();
358 printf ("info: test_udp_server: 2.5 second timeout took %f seconds"
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 }))
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 ();
388 FAIL_EXIT1 ("supposed to be unreachable");
390 test_udp_server (transport
->xp_port
);
393 xwaitpid (pid
, &status
, 0);
394 TEST_VERIFY (WIFEXITED (status
) && WEXITSTATUS (status
) == EXIT_MARKER
);
396 SVC_DESTROY (transport
);
400 /* The minimum run time is around 17 seconds. */
402 #include <support/test-driver.c>