1 /* Test timeout handling in the UDP client.
2    Copyright (C) 2017-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
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.
9 
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.
14 
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/>.  */
18 
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>
32 #include <stdlib.h>
33 
34 static pid_t server_pid;
35 
36 /* Test data serialization and deserialization.   */
37 
38 struct test_query
39 {
40   uint32_t a;
41   uint32_t b;
42   uint32_t timeout_ms;
43   uint32_t wait_for_seq;
44   uint32_t garbage_packets;
45 };
46 
47 static bool_t
xdr_test_query(XDR * xdrs,void * data,...)48 xdr_test_query (XDR *xdrs, void *data, ...)
49 {
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);
56 }
57 
58 struct test_response
59 {
60   uint32_t seq;
61   uint32_t sum;
62 };
63 
64 static bool_t
xdr_test_response(XDR * xdrs,void * data,...)65 xdr_test_response (XDR *xdrs, void *data, ...)
66 {
67   struct test_response *p = data;
68   return xdr_uint32_t (xdrs, &p->seq)
69     && xdr_uint32_t (xdrs, &p->sum);
70 }
71 
72 /* Implementation of the test server.  */
73 
74 enum
75   {
76     /* RPC parameters, chosen at random.  */
77     PROGNUM = 15717,
78     VERSNUM = 13689,
79 
80     /* Main RPC operation.  */
81     PROC_ADD = 1,
82 
83     /* Reset the sequence number.  */
84     PROC_RESET_SEQ,
85 
86     /* Request process termination.  */
87     PROC_EXIT,
88 
89     /* Special exit status to mark successful processing.  */
90     EXIT_MARKER = 55,
91   };
92 
93 static void
server_dispatch(struct svc_req * request,SVCXPRT * transport)94 server_dispatch (struct svc_req *request, SVCXPRT *transport)
95 {
96   /* Query sequence number.  */
97   static uint32_t seq = 0;
98   ++seq;
99 
100   if (test_verbose)
101     printf ("info: server_dispatch seq=%u rq_proc=%lu\n",
102             seq, request->rq_proc);
103 
104   switch (request->rq_proc)
105     {
106     case PROC_ADD:
107       {
108         struct test_query query;
109         memset (&query, 0xc0, sizeof (query));
110         TEST_VERIFY_EXIT
111           (svc_getargs (transport, xdr_test_query,
112                         (void *) &query));
113 
114         if (test_verbose)
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);
119 
120         if (seq < query.wait_for_seq)
121           {
122             /* No response at this point.  */
123             if (test_verbose)
124               printf ("  skipped response\n");
125             break;
126           }
127 
128         if (query.garbage_packets > 0)
129           {
130             int per_packet_timeout;
131             if (query.timeout_ms > 0)
132               per_packet_timeout
133                 = query.timeout_ms * 1000 / query.garbage_packets;
134             else
135               per_packet_timeout = 0;
136 
137             char buf[20];
138             memset (&buf, 0xc0, sizeof (buf));
139             for (int i = 0; i < query.garbage_packets; ++i)
140               {
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
144                    large.  */
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);
152               }
153           }
154         else if (query.timeout_ms > 0)
155           usleep (query.timeout_ms * 1000);
156 
157         struct test_response response =
158           {
159             .seq = seq,
160             .sum = query.a + query.b,
161           };
162         TEST_VERIFY (svc_sendreply (transport, xdr_test_response,
163                                     (void *) &response));
164       }
165       break;
166 
167     case PROC_RESET_SEQ:
168       seq = 0;
169       TEST_VERIFY (svc_sendreply (transport, (xdrproc_t) xdr_void, NULL));
170       break;
171 
172     case PROC_EXIT:
173       TEST_VERIFY (svc_sendreply (transport, (xdrproc_t) xdr_void, NULL));
174       _exit (EXIT_MARKER);
175       break;
176 
177     default:
178       FAIL_EXIT1 ("invalid rq_proc value: %lu", request->rq_proc);
179       break;
180     }
181 }
182 
183 /* Function to be called before exit to make sure the
184    server process is properly killed.  */
185 static void
kill_server(void)186 kill_server (void)
187 {
188   kill (server_pid, SIGTERM);
189 }
190 
191 /* Implementation of the test client.  */
192 
193 static struct test_response
test_call(CLIENT * clnt,int proc,struct test_query query,struct timeval timeout)194 test_call (CLIENT *clnt, int proc, struct test_query query,
195            struct timeval timeout)
196 {
197   if (test_verbose)
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,
205                                timeout)
206                     == RPC_SUCCESS);
207   return response;
208 }
209 
210 static void
test_call_timeout(CLIENT * clnt,int proc,struct test_query query,struct timeval timeout)211 test_call_timeout (CLIENT *clnt, int proc, struct test_query query,
212                    struct timeval timeout)
213 {
214   struct test_response response;
215   TEST_VERIFY (clnt_call (clnt, proc,
216                           xdr_test_query, (void *) &query,
217                           xdr_test_response, (void *) &response,
218                           timeout)
219                == RPC_TIMEDOUT);
220 }
221 
222 /* Complete one regular RPC call to drain the server socket
223    buffer.  Resets the sequence number.  */
224 static void
test_call_flush(CLIENT * clnt)225 test_call_flush (CLIENT *clnt)
226 {
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.  */
230   if (test_verbose)
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 }))
236                     == RPC_SUCCESS);
237 }
238 
239 /* Return the number seconds since an arbitrary point in time.  */
240 static double
get_ticks(void)241 get_ticks (void)
242 {
243   {
244     struct timespec ts;
245     if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0)
246       return ts.tv_sec + ts.tv_nsec * 1e-9;
247   }
248   {
249     struct timeval tv;
250     TEST_VERIFY_EXIT (gettimeofday (&tv, NULL) == 0);
251     return tv.tv_sec + tv.tv_usec * 1e-6;
252   }
253 }
254 
255 static void
test_udp_server(int port)256 test_udp_server (int port)
257 {
258   struct sockaddr_in sin =
259     {
260       .sin_family = AF_INET,
261       .sin_addr.s_addr = htonl (INADDR_LOOPBACK),
262       .sin_port = htons (port)
263     };
264   int sock = RPC_ANYSOCK;
265 
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
269      large.  */
270   CLIENT *clnt = clntudp_create (&sin, PROGNUM, VERSNUM,
271                                  (struct timeval) { 1, 500 * 1000 },
272                                  &sock);
273   TEST_VERIFY_EXIT (clnt != NULL);
274 
275   /* Basic call/response test.  */
276   struct test_response response = test_call
277     (clnt, PROC_ADD,
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);
282 
283   /* Check that garbage packets do not interfere with timeout
284      processing.  */
285   double before = get_ticks ();
286   response = test_call
287     (clnt, PROC_ADD,
288      (struct test_query) {
289        .a = 19, .b = 4, .timeout_ms = 500, .garbage_packets = 21,
290      },
291      (struct timeval) { 3, 0 });
292   TEST_VERIFY (response.sum == 23);
293   TEST_VERIFY (response.seq == 2);
294   double after = get_ticks ();
295   if (test_verbose)
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);
303 
304   /* Check that missing a response introduces a 1.5 second timeout, as
305      requested when calling clntudp_create.  */
306   before = get_ticks ();
307   response = test_call
308     (clnt, PROC_ADD,
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 ();
314   if (test_verbose)
315     printf ("info: skipping one response took %f seconds\n",
316             after - before);
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);
322 
323   /* Check that the overall timeout wins against the per-query
324      timeout.  */
325   before = get_ticks ();
326   test_call_timeout
327     (clnt, PROC_ADD,
328      (struct test_query) { .a = 170, .b = 41, .wait_for_seq = 2 },
329      (struct timeval) { 0, 750 * 1000 });
330   after = get_ticks ();
331   if (test_verbose)
332     printf ("info: 0.75 second timeout took %f seconds\n",
333             after - before);
334   TEST_VERIFY (0.70 <= after - before);
335   TEST_VERIFY (after - before < 1.4);
336   test_call_flush (clnt);
337 
338   for (int with_garbage = 0; with_garbage < 2; ++with_garbage)
339     {
340       /* Check that no response at all causes the client to bail out.  */
341       before = get_ticks ();
342       test_call_timeout
343         (clnt, PROC_ADD,
344          (struct test_query) {
345            .a = 170, .b = 40, .timeout_ms = 1200,
346            .garbage_packets = with_garbage * 21
347          },
348          (struct timeval) { 0, 750 * 1000 });
349       after = get_ticks ();
350       if (test_verbose)
351         printf ("info: test_udp_server: 0.75 second timeout took %f seconds"
352                 " (garbage %d)\n",
353                 after - before, with_garbage);
354       TEST_VERIFY (0.70 <= after - before);
355       TEST_VERIFY (after - before < 1.4);
356       test_call_flush (clnt);
357 
358       /* As above, but check the total timeout.  */
359       before = get_ticks ();
360       test_call_timeout
361         (clnt, PROC_ADD,
362          (struct test_query) {
363            .a = 170, .b = 40, .timeout_ms = 3000,
364            .garbage_packets = with_garbage * 30
365          },
366          (struct timeval) { 2, 500 * 1000 });
367       after = get_ticks ();
368       if (test_verbose)
369         printf ("info: test_udp_server: 2.5 second timeout took %f seconds"
370                 " (garbage %d)\n",
371                 after - before, with_garbage);
372       TEST_VERIFY (2.45 <= after - before);
373       TEST_VERIFY (after - before < 3.0);
374       test_call_flush (clnt);
375     }
376 
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 }))
381                     == RPC_SUCCESS);
382   clnt_destroy (clnt);
383 }
384 
385 static int
do_test(void)386 do_test (void)
387 {
388   support_become_root ();
389   support_enter_network_namespace ();
390 
391   SVCXPRT *transport = svcudp_create (RPC_ANYSOCK);
392   TEST_VERIFY_EXIT (transport != NULL);
393   TEST_VERIFY (svc_register (transport, PROGNUM, VERSNUM, server_dispatch, 0));
394 
395   server_pid = xfork ();
396   if (server_pid == 0)
397     {
398       svc_run ();
399       FAIL_EXIT1 ("supposed to be unreachable");
400     }
401   atexit (kill_server);
402   test_udp_server (transport->xp_port);
403 
404   int status;
405   xwaitpid (server_pid, &status, 0);
406   TEST_VERIFY (WIFEXITED (status) && WEXITSTATUS (status) == EXIT_MARKER);
407 
408   SVC_DESTROY (transport);
409   return 0;
410 }
411 
412 /* The minimum run time is around 17 seconds.  */
413 #define TIMEOUT 25
414 #include <support/test-driver.c>
415