1 /* Test timeout handling in the UDP client.
2 Copyright (C) 2017-2024 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 <https://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>
34 static pid_t server_pid
;
36 /* Test data serialization and deserialization. */
43 uint32_t wait_for_seq
;
44 uint32_t garbage_packets
;
48 xdr_test_query (XDR
*xdrs
, void *data
, ...)
50 struct test_query
*p
= data
;
51 return xdr_uint32_t (xdrs
, &p
->a
)
52 && xdr_uint32_t (xdrs
, &p
->b
)
53 && xdr_uint32_t (xdrs
, &p
->timeout_ms
)
54 && xdr_uint32_t (xdrs
, &p
->wait_for_seq
)
55 && xdr_uint32_t (xdrs
, &p
->garbage_packets
);
65 xdr_test_response (XDR
*xdrs
, void *data
, ...)
67 struct test_response
*p
= data
;
68 return xdr_uint32_t (xdrs
, &p
->seq
)
69 && xdr_uint32_t (xdrs
, &p
->sum
);
72 /* Implementation of the test server. */
76 /* RPC parameters, chosen at random. */
80 /* Main RPC operation. */
83 /* Reset the sequence number. */
86 /* Request process termination. */
89 /* Special exit status to mark successful processing. */
94 server_dispatch (struct svc_req
*request
, SVCXPRT
*transport
)
96 /* Query sequence number. */
97 static uint32_t seq
= 0;
101 printf ("info: server_dispatch seq=%u rq_proc=%lu\n",
102 seq
, request
->rq_proc
);
104 switch (request
->rq_proc
)
108 struct test_query query
;
109 memset (&query
, 0xc0, sizeof (query
));
111 (svc_getargs (transport
, xdr_test_query
,
115 printf (" a=%u b=%u timeout_ms=%u wait_for_seq=%u"
116 " garbage_packets=%u\n",
117 query
.a
, query
.b
, query
.timeout_ms
, query
.wait_for_seq
,
118 query
.garbage_packets
);
120 if (seq
< query
.wait_for_seq
)
122 /* No response at this point. */
124 printf (" skipped response\n");
128 if (query
.garbage_packets
> 0)
130 int per_packet_timeout
;
131 if (query
.timeout_ms
> 0)
133 = query
.timeout_ms
* 1000 / query
.garbage_packets
;
135 per_packet_timeout
= 0;
138 memset (&buf
, 0xc0, sizeof (buf
));
139 for (int i
= 0; i
< query
.garbage_packets
; ++i
)
141 /* 13 is relatively prime to 20 = sizeof (buf) + 1, so
142 the len variable will cover the entire interval
143 [0, 20] if query.garbage_packets is sufficiently
145 size_t len
= (i
* 13 + 1) % (sizeof (buf
) + 1);
146 TEST_VERIFY (sendto (transport
->xp_sock
,
147 buf
, len
, MSG_NOSIGNAL
,
148 (struct sockaddr
*) &transport
->xp_raddr
,
149 transport
->xp_addrlen
) == len
);
150 if (per_packet_timeout
> 0)
151 usleep (per_packet_timeout
);
154 else if (query
.timeout_ms
> 0)
155 usleep (query
.timeout_ms
* 1000);
157 struct test_response response
=
160 .sum
= query
.a
+ query
.b
,
162 TEST_VERIFY (svc_sendreply (transport
, xdr_test_response
,
163 (void *) &response
));
169 TEST_VERIFY (svc_sendreply (transport
, (xdrproc_t
) xdr_void
, NULL
));
173 TEST_VERIFY (svc_sendreply (transport
, (xdrproc_t
) xdr_void
, NULL
));
178 FAIL_EXIT1 ("invalid rq_proc value: %lu", request
->rq_proc
);
183 /* Function to be called before exit to make sure the
184 server process is properly killed. */
188 kill (server_pid
, SIGTERM
);
191 /* Implementation of the test client. */
193 static struct test_response
194 test_call (CLIENT
*clnt
, int proc
, struct test_query query
,
195 struct timeval timeout
)
198 printf ("info: test_call proc=%d timeout=%lu.%06lu\n",
199 proc
, (unsigned long) timeout
.tv_sec
,
200 (unsigned long) timeout
.tv_usec
);
201 struct test_response response
;
202 TEST_VERIFY_EXIT (clnt_call (clnt
, proc
,
203 xdr_test_query
, (void *) &query
,
204 xdr_test_response
, (void *) &response
,
211 test_call_timeout (CLIENT
*clnt
, int proc
, struct test_query query
,
212 struct timeval timeout
)
214 struct test_response response
;
215 TEST_VERIFY (clnt_call (clnt
, proc
,
216 xdr_test_query
, (void *) &query
,
217 xdr_test_response
, (void *) &response
,
222 /* Complete one regular RPC call to drain the server socket
223 buffer. Resets the sequence number. */
225 test_call_flush (CLIENT
*clnt
)
227 /* This needs a longer timeout to flush out all pending requests.
228 The choice of 5 seconds is larger than the per-response timeouts
229 requested via the timeout_ms field. */
231 printf ("info: flushing pending queries\n");
232 TEST_VERIFY_EXIT (clnt_call (clnt
, PROC_RESET_SEQ
,
233 (xdrproc_t
) xdr_void
, NULL
,
234 (xdrproc_t
) xdr_void
, NULL
,
235 ((struct timeval
) { 5, 0 }))
239 /* Return the number seconds since an arbitrary point in time. */
245 if (clock_gettime (CLOCK_MONOTONIC
, &ts
) == 0)
246 return ts
.tv_sec
+ ts
.tv_nsec
* 1e-9;
250 TEST_VERIFY_EXIT (gettimeofday (&tv
, NULL
) == 0);
251 return tv
.tv_sec
+ tv
.tv_usec
* 1e-6;
256 test_udp_server (int port
)
258 struct sockaddr_in sin
=
260 .sin_family
= AF_INET
,
261 .sin_addr
.s_addr
= htonl (INADDR_LOOPBACK
),
262 .sin_port
= htons (port
)
264 int sock
= RPC_ANYSOCK
;
266 /* The client uses a 1.5 second timeout for retries. The timeouts
267 are arbitrary, but chosen so that there is a substantial gap
268 between them, but the total time spent waiting is not too
270 CLIENT
*clnt
= clntudp_create (&sin
, PROGNUM
, VERSNUM
,
271 (struct timeval
) { 1, 500 * 1000 },
273 TEST_VERIFY_EXIT (clnt
!= NULL
);
275 /* Basic call/response test. */
276 struct test_response response
= test_call
278 (struct test_query
) { .a
= 17, .b
= 4 },
279 (struct timeval
) { 3, 0 });
280 TEST_VERIFY (response
.sum
== 21);
281 TEST_VERIFY (response
.seq
== 1);
283 /* Check that garbage packets do not interfere with timeout
285 double before
= get_ticks ();
288 (struct test_query
) {
289 .a
= 19, .b
= 4, .timeout_ms
= 500, .garbage_packets
= 21,
291 (struct timeval
) { 3, 0 });
292 TEST_VERIFY (response
.sum
== 23);
293 TEST_VERIFY (response
.seq
== 2);
294 double after
= get_ticks ();
296 printf ("info: 21 garbage packets took %f seconds\n", after
- before
);
297 /* Expected timeout is 0.5 seconds. Add some slack for rounding errors and
298 in case process scheduling delays processing the query or response, but
299 do not accept a retry (which would happen at 1.5 seconds). */
300 TEST_VERIFY (0.45 <= after
- before
);
301 TEST_VERIFY (after
- before
< 1.2);
302 test_call_flush (clnt
);
304 /* Check that missing a response introduces a 1.5 second timeout, as
305 requested when calling clntudp_create. */
306 before
= get_ticks ();
309 (struct test_query
) { .a
= 170, .b
= 40, .wait_for_seq
= 2 },
310 (struct timeval
) { 3, 0 });
311 TEST_VERIFY (response
.sum
== 210);
312 TEST_VERIFY (response
.seq
== 2);
313 after
= get_ticks ();
315 printf ("info: skipping one response took %f seconds\n",
317 /* Expected timeout is 1.5 seconds. Do not accept a second retry
318 (which would happen at 3 seconds). */
319 TEST_VERIFY (1.45 <= after
- before
);
320 TEST_VERIFY (after
- before
< 2.9);
321 test_call_flush (clnt
);
323 /* Check that the overall timeout wins against the per-query
325 before
= get_ticks ();
328 (struct test_query
) { .a
= 170, .b
= 41, .wait_for_seq
= 2 },
329 (struct timeval
) { 0, 750 * 1000 });
330 after
= get_ticks ();
332 printf ("info: 0.75 second timeout took %f seconds\n",
334 TEST_VERIFY (0.70 <= after
- before
);
335 TEST_VERIFY (after
- before
< 1.4);
336 test_call_flush (clnt
);
338 for (int with_garbage
= 0; with_garbage
< 2; ++with_garbage
)
340 /* Check that no response at all causes the client to bail out. */
341 before
= get_ticks ();
344 (struct test_query
) {
345 .a
= 170, .b
= 40, .timeout_ms
= 1200,
346 .garbage_packets
= with_garbage
* 21
348 (struct timeval
) { 0, 750 * 1000 });
349 after
= get_ticks ();
351 printf ("info: test_udp_server: 0.75 second timeout took %f seconds"
353 after
- before
, with_garbage
);
354 TEST_VERIFY (0.70 <= after
- before
);
355 TEST_VERIFY (after
- before
< 1.4);
356 test_call_flush (clnt
);
358 /* As above, but check the total timeout. */
359 before
= get_ticks ();
362 (struct test_query
) {
363 .a
= 170, .b
= 40, .timeout_ms
= 3000,
364 .garbage_packets
= with_garbage
* 30
366 (struct timeval
) { 2, 500 * 1000 });
367 after
= get_ticks ();
369 printf ("info: test_udp_server: 2.5 second timeout took %f seconds"
371 after
- before
, with_garbage
);
372 TEST_VERIFY (2.45 <= after
- before
);
373 TEST_VERIFY (after
- before
< 3.0);
374 test_call_flush (clnt
);
377 TEST_VERIFY_EXIT (clnt_call (clnt
, PROC_EXIT
,
378 (xdrproc_t
) xdr_void
, NULL
,
379 (xdrproc_t
) xdr_void
, NULL
,
380 ((struct timeval
) { 5, 0 }))
388 support_become_root ();
389 support_enter_network_namespace ();
391 SVCXPRT
*transport
= svcudp_create (RPC_ANYSOCK
);
392 TEST_VERIFY_EXIT (transport
!= NULL
);
393 TEST_VERIFY (svc_register (transport
, PROGNUM
, VERSNUM
, server_dispatch
, 0));
395 server_pid
= xfork ();
399 FAIL_EXIT1 ("supposed to be unreachable");
401 atexit (kill_server
);
402 test_udp_server (transport
->xp_port
);
405 xwaitpid (server_pid
, &status
, 0);
406 TEST_VERIFY (WIFEXITED (status
) && WEXITSTATUS (status
) == EXIT_MARKER
);
408 SVC_DESTROY (transport
);
412 /* The minimum run time is around 17 seconds. */
414 #include <support/test-driver.c>