35252da21bc45129dff7391ee2cdd56bb9765424
[vpp.git] / src / plugins / hs_apps / vcl / sock_test_client.c
1 /*
2  * Copyright (c) 2017-2019 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <unistd.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <ctype.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <stdio.h>
23 #include <time.h>
24 #include <arpa/inet.h>
25 #include <hs_apps/vcl/sock_test.h>
26 #include <fcntl.h>
27 #include <sys/un.h>
28
29 typedef struct
30 {
31   int af_unix_echo_tx;
32   int af_unix_echo_rx;
33   struct sockaddr_storage server_addr;
34   uint32_t server_addr_size;
35   uint32_t cfg_seq_num;
36   vcl_test_session_t ctrl_socket;
37   vcl_test_session_t *test_socket;
38   uint32_t num_test_sockets;
39   uint8_t dump_cfg;
40 } sock_client_main_t;
41
42 sock_client_main_t sock_client_main;
43
44 static int
45 sock_test_cfg_sync (vcl_test_session_t * socket)
46 {
47   sock_client_main_t *scm = &sock_client_main;
48   vcl_test_session_t *ctrl = &scm->ctrl_socket;
49   vcl_test_cfg_t *rl_cfg = (vcl_test_cfg_t *) socket->rxbuf;
50   int rx_bytes, tx_bytes;
51
52   if (socket->cfg.verbose)
53     vcl_test_cfg_dump (&socket->cfg, 1 /* is_client */ );
54
55   ctrl->cfg.seq_num = ++scm->cfg_seq_num;
56   if (socket->cfg.verbose)
57     {
58       stinf ("(fd %d): Sending config sent to server.\n", socket->fd);
59       vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
60     }
61   tx_bytes = sock_test_write (socket->fd, (uint8_t *) & ctrl->cfg,
62                               sizeof (ctrl->cfg), NULL, ctrl->cfg.verbose);
63   if (tx_bytes < 0)
64     stabrt ("(fd %d): write test cfg failed (%d)!", socket->fd, tx_bytes);
65
66   rx_bytes = sock_test_read (socket->fd, (uint8_t *) socket->rxbuf,
67                              sizeof (vcl_test_cfg_t), NULL);
68   if (rx_bytes < 0)
69     return rx_bytes;
70
71   if (rl_cfg->magic != VCL_TEST_CFG_CTRL_MAGIC)
72     stabrt ("(fd %d): Bad server reply cfg -- aborting!\n", socket->fd);
73
74   if ((rx_bytes != sizeof (vcl_test_cfg_t))
75       || !vcl_test_cfg_verify (rl_cfg, &ctrl->cfg))
76     stabrt ("(fd %d): Invalid config received from server!\n", socket->fd);
77
78   if (socket->cfg.verbose)
79     {
80       stinf ("(fd %d): Got config back from server.", socket->fd);
81       vcl_test_cfg_dump (rl_cfg, 1 /* is_client */ );
82     }
83   ctrl->cfg.ctrl_handle = ((ctrl->cfg.ctrl_handle == ~0) ?
84                            rl_cfg->ctrl_handle : ctrl->cfg.ctrl_handle);
85
86   return 0;
87 }
88
89 static void
90 sock_client_echo_af_unix (sock_client_main_t * scm)
91 {
92   int fd, rv;
93   struct sockaddr_un serveraddr;
94   uint8_t buffer[256];
95   size_t nbytes = strlen (SOCK_TEST_MIXED_EPOLL_DATA) + 1;
96   struct timeval timeout;
97
98   /* Open AF_UNIX socket and send an echo to test mixed epoll on server.
99    */
100   fd = socket (AF_UNIX, SOCK_STREAM, 0);
101   if (fd < 0)
102     stfail ("socket()");
103
104   memset (&serveraddr, 0, sizeof (serveraddr));
105   serveraddr.sun_family = AF_UNIX;
106   strncpy (serveraddr.sun_path, SOCK_TEST_AF_UNIX_FILENAME,
107            sizeof (serveraddr.sun_path));
108   rv = connect (fd, (struct sockaddr *) &serveraddr, SUN_LEN (&serveraddr));
109   if (rv < 0)
110     stfail ("connect()");
111
112   scm->af_unix_echo_tx++;
113   strncpy ((char *) buffer, SOCK_TEST_MIXED_EPOLL_DATA, sizeof (buffer));
114   timeout.tv_sec = 0;
115   timeout.tv_usec = 250000;
116   select (0, NULL, NULL, NULL, &timeout);       /* delay .25 secs */
117   rv = write (fd, buffer, nbytes);
118   if (rv < 0)
119     stfail ("write()");
120
121   if (rv < nbytes)
122     stabrt ("write(fd %d, \"%s\", %lu) returned %d!", fd, buffer, nbytes, rv);
123
124   stinf ("(AF_UNIX): TX (%d bytes) - '%s'\n", rv, buffer);
125   memset (buffer, 0, sizeof (buffer));
126   rv = read (fd, buffer, nbytes);
127   if (rv < 0)
128     stfail ("read()");
129
130   if (rv < nbytes)
131     stabrt ("read(fd %d, %p, %lu) returned %d!\n", fd, buffer, nbytes, rv);
132
133   if (!strncmp (SOCK_TEST_MIXED_EPOLL_DATA, (const char *) buffer, nbytes))
134     {
135       stinf ("(AF_UNIX): RX (%d bytes) - '%s'\n", rv, buffer);
136       scm->af_unix_echo_rx++;
137     }
138   else
139     stabrt ("(AF_UNIX): RX (%d bytes) - '%s'\n", rv, buffer);
140
141   close (fd);
142 }
143
144 static void
145 echo_test_client (void)
146 {
147   sock_client_main_t *scm = &sock_client_main;
148   vcl_test_session_t *ctrl = &scm->ctrl_socket;
149   vcl_test_session_t *tsock;
150   int rx_bytes, tx_bytes, nbytes;
151   uint32_t i, n;
152   int rv;
153   int nfds = 0;
154   fd_set wr_fdset, rd_fdset;
155   fd_set _wfdset, *wfdset = &_wfdset;
156   fd_set _rfdset, *rfdset = &_rfdset;
157
158   FD_ZERO (&wr_fdset);
159   FD_ZERO (&rd_fdset);
160   memset (&ctrl->stats, 0, sizeof (ctrl->stats));
161   ctrl->cfg.total_bytes = nbytes = strlen (ctrl->txbuf) + 1;
162   for (n = 0; n != ctrl->cfg.num_test_sessions; n++)
163     {
164       tsock = &scm->test_socket[n];
165       tsock->cfg = ctrl->cfg;
166       vcl_test_session_buf_alloc (tsock);
167       if (sock_test_cfg_sync (tsock))
168         return;
169
170       memcpy (tsock->txbuf, ctrl->txbuf, nbytes);
171       memset (&tsock->stats, 0, sizeof (tsock->stats));
172
173       FD_SET (tsock->fd, &wr_fdset);
174       FD_SET (tsock->fd, &rd_fdset);
175       nfds = ((tsock->fd + 1) > nfds) ? (tsock->fd + 1) : nfds;
176     }
177
178   nfds++;
179   clock_gettime (CLOCK_REALTIME, &ctrl->stats.start);
180   while (n)
181     {
182       _wfdset = wr_fdset;
183       _rfdset = rd_fdset;
184
185       struct timeval timeout;
186       timeout.tv_sec = 0;
187       timeout.tv_usec = 0;
188       rv = select (nfds, rfdset, wfdset, NULL, &timeout);
189
190       if (rv < 0)
191         stfail ("select()");
192
193       if (rv == 0)
194         continue;
195
196       for (i = 0; i < ctrl->cfg.num_test_sessions; i++)
197         {
198           tsock = &scm->test_socket[i];
199           if (!((tsock->stats.stop.tv_sec == 0) &&
200                 (tsock->stats.stop.tv_nsec == 0)))
201             continue;
202
203           if (FD_ISSET (tsock->fd, wfdset) &&
204               (tsock->stats.tx_bytes < ctrl->cfg.total_bytes))
205             {
206               tx_bytes = sock_test_write (tsock->fd, (uint8_t *) tsock->txbuf,
207                                           nbytes, &tsock->stats,
208                                           ctrl->cfg.verbose);
209               if (tx_bytes < 0)
210                 stabrt ("sock_test_write(%d) failed -- aborting test!",
211                         tsock->fd);
212
213               stinf ("(fd %d): TX (%d bytes) - '%s'", tsock->fd, tx_bytes,
214                      tsock->txbuf);
215             }
216
217           if ((FD_ISSET (tsock->fd, rfdset)) &&
218               (tsock->stats.rx_bytes < ctrl->cfg.total_bytes))
219             {
220               rx_bytes = sock_test_read (tsock->fd, (uint8_t *) tsock->rxbuf,
221                                          nbytes, &tsock->stats);
222               if (rx_bytes > 0)
223                 {
224                   stinf ("(fd %d): RX (%d bytes)\n", tsock->fd, rx_bytes);
225
226                   if (tsock->stats.rx_bytes != tsock->stats.tx_bytes)
227                     stinf ("bytes read (%lu) != bytes written (%lu)!\n",
228                            tsock->stats.rx_bytes, tsock->stats.tx_bytes);
229                 }
230             }
231
232           if (tsock->stats.rx_bytes >= ctrl->cfg.total_bytes)
233             {
234               clock_gettime (CLOCK_REALTIME, &tsock->stats.stop);
235               n--;
236             }
237         }
238     }
239   clock_gettime (CLOCK_REALTIME, &ctrl->stats.stop);
240
241   sock_client_echo_af_unix (scm);
242
243   for (i = 0; i < ctrl->cfg.num_test_sessions; i++)
244     {
245       tsock = &scm->test_socket[i];
246       tsock->stats.start = ctrl->stats.start;
247
248       if (ctrl->cfg.verbose)
249         {
250           static char buf[64];
251
252           snprintf (buf, sizeof (buf), "CLIENT (fd %d) RESULTS", tsock->fd);
253           vcl_test_stats_dump (buf, &tsock->stats,
254                                1 /* show_rx */ , 1 /* show tx */ ,
255                                ctrl->cfg.verbose);
256         }
257
258       vcl_test_stats_accumulate (&ctrl->stats, &tsock->stats);
259     }
260
261   if (ctrl->cfg.verbose)
262     {
263       vcl_test_stats_dump ("CLIENT RESULTS", &ctrl->stats,
264                            1 /* show_rx */ , 1 /* show tx */ ,
265                            ctrl->cfg.verbose);
266       vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
267
268       if (ctrl->cfg.verbose > 1)
269         {
270           stinf ("  ctrl socket info\n"
271                  VCL_TEST_SEPARATOR_STRING
272                  "          fd:  %d (0x%08x)\n"
273                  "       rxbuf:  %p\n"
274                  "  rxbuf size:  %u (0x%08x)\n"
275                  "       txbuf:  %p\n"
276                  "  txbuf size:  %u (0x%08x)\n"
277                  VCL_TEST_SEPARATOR_STRING,
278                  ctrl->fd, (uint32_t) ctrl->fd,
279                  ctrl->rxbuf, ctrl->rxbuf_size, ctrl->rxbuf_size,
280                  ctrl->txbuf, ctrl->txbuf_size, ctrl->txbuf_size);
281         }
282     }
283 }
284
285 static void
286 stream_test_client (vcl_test_t test)
287 {
288   sock_client_main_t *scm = &sock_client_main;
289   vcl_test_session_t *ctrl = &scm->ctrl_socket;
290   vcl_test_session_t *tsock;
291   int tx_bytes, rv, nfds = 0;;
292   uint32_t i, n;
293   fd_set wr_fdset, rd_fdset;
294   fd_set _wfdset, *wfdset = &_wfdset;
295   fd_set _rfdset, *rfdset = (test == VCL_TEST_TYPE_BI) ? &_rfdset : 0;
296
297   ctrl->cfg.total_bytes = ctrl->cfg.num_writes * ctrl->cfg.txbuf_size;
298   ctrl->cfg.ctrl_handle = ~0;
299
300   stinf ("\n" SOCK_TEST_BANNER_STRING
301          "CLIENT (fd %d): %s-directional Stream Test!\n\n"
302          "CLIENT (fd %d): Sending config to server on ctrl socket...\n",
303          ctrl->fd, test == VCL_TEST_TYPE_BI ? "Bi" : "Uni", ctrl->fd);
304
305   if (sock_test_cfg_sync (ctrl))
306     stabrt ("test cfg sync failed -- aborting!");
307
308   FD_ZERO (&wr_fdset);
309   FD_ZERO (&rd_fdset);
310   memset (&ctrl->stats, 0, sizeof (ctrl->stats));
311   for (n = 0; n != ctrl->cfg.num_test_sessions; n++)
312     {
313       tsock = &scm->test_socket[n];
314       tsock->cfg = ctrl->cfg;
315       vcl_test_session_buf_alloc (tsock);
316       stinf ("(fd %d): Sending config to server on test socket %d...\n",
317              tsock->fd, n);
318       sock_test_cfg_sync (tsock);
319
320       /* Fill payload with incrementing uint32's */
321       for (i = 0; i < tsock->txbuf_size; i++)
322         tsock->txbuf[i] = i & 0xff;
323
324       memset (&tsock->stats, 0, sizeof (tsock->stats));
325       FD_SET (tsock->fd, &wr_fdset);
326       FD_SET (tsock->fd, &rd_fdset);
327       nfds = ((tsock->fd + 1) > nfds) ? (tsock->fd + 1) : nfds;
328     }
329
330   nfds++;
331   clock_gettime (CLOCK_REALTIME, &ctrl->stats.start);
332   while (n)
333     {
334       _wfdset = wr_fdset;
335       _rfdset = rd_fdset;
336
337       struct timeval timeout;
338       timeout.tv_sec = 0;
339       timeout.tv_usec = 0;
340       rv = select (nfds, rfdset, wfdset, NULL, &timeout);
341
342       if (rv < 0)
343         stfail ("select()");
344
345       if (rv == 0)
346         continue;
347
348       for (i = 0; i < ctrl->cfg.num_test_sessions; i++)
349         {
350           tsock = &scm->test_socket[i];
351           if (!((tsock->stats.stop.tv_sec == 0) &&
352                 (tsock->stats.stop.tv_nsec == 0)))
353             continue;
354
355           if ((test == VCL_TEST_TYPE_BI) &&
356               FD_ISSET (tsock->fd, rfdset) &&
357               (tsock->stats.rx_bytes < ctrl->cfg.total_bytes))
358             {
359               (void) sock_test_read (tsock->fd,
360                                      (uint8_t *) tsock->rxbuf,
361                                      tsock->rxbuf_size, &tsock->stats);
362             }
363
364           if (FD_ISSET (tsock->fd, wfdset) &&
365               (tsock->stats.tx_bytes < ctrl->cfg.total_bytes))
366             {
367               tx_bytes = sock_test_write (tsock->fd, (uint8_t *) tsock->txbuf,
368                                           ctrl->cfg.txbuf_size, &tsock->stats,
369                                           ctrl->cfg.verbose);
370               if (tx_bytes < 0)
371                 stabrt ("sock_test_write(%d) failed -- aborting test!",
372                         tsock->fd);
373             }
374
375           if (((test == VCL_TEST_TYPE_UNI) &&
376                (tsock->stats.tx_bytes >= ctrl->cfg.total_bytes)) ||
377               ((test == VCL_TEST_TYPE_BI) &&
378                (tsock->stats.rx_bytes >= ctrl->cfg.total_bytes)))
379             {
380               clock_gettime (CLOCK_REALTIME, &tsock->stats.stop);
381               n--;
382             }
383         }
384     }
385   clock_gettime (CLOCK_REALTIME, &ctrl->stats.stop);
386
387   stinf ("(fd %d): Sending config to server on ctrl socket...\n", ctrl->fd);
388
389   if (sock_test_cfg_sync (ctrl))
390     stabrt ("test cfg sync failed -- aborting!");
391
392   for (i = 0; i < ctrl->cfg.num_test_sessions; i++)
393     {
394       tsock = &scm->test_socket[i];
395
396       if (ctrl->cfg.verbose)
397         {
398           static char buf[64];
399
400           snprintf (buf, sizeof (buf), "CLIENT (fd %d) RESULTS", tsock->fd);
401           vcl_test_stats_dump (buf, &tsock->stats,
402                                test == VCL_TEST_TYPE_BI /* show_rx */ ,
403                                1 /* show tx */ , ctrl->cfg.verbose);
404         }
405
406       vcl_test_stats_accumulate (&ctrl->stats, &tsock->stats);
407     }
408
409   vcl_test_stats_dump ("CLIENT RESULTS", &ctrl->stats,
410                        test == VCL_TEST_TYPE_BI /* show_rx */ ,
411                        1 /* show tx */ , ctrl->cfg.verbose);
412   vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
413
414   if (ctrl->cfg.verbose)
415     {
416       stinf ("  ctrl socket info\n"
417              VCL_TEST_SEPARATOR_STRING
418              "          fd:  %d (0x%08x)\n"
419              "       rxbuf:  %p\n"
420              "  rxbuf size:  %u (0x%08x)\n"
421              "       txbuf:  %p\n"
422              "  txbuf size:  %u (0x%08x)\n"
423              VCL_TEST_SEPARATOR_STRING,
424              ctrl->fd, (uint32_t) ctrl->fd,
425              ctrl->rxbuf, ctrl->rxbuf_size, ctrl->rxbuf_size,
426              ctrl->txbuf, ctrl->txbuf_size, ctrl->txbuf_size);
427     }
428
429   ctrl->cfg.test = VCL_TEST_TYPE_ECHO;
430   if (sock_test_cfg_sync (ctrl))
431     stabrt ("post-test cfg sync failed!");
432
433   stinf ("(fd %d): %s-directional Stream Test Complete!\n"
434          SOCK_TEST_BANNER_STRING "\n", ctrl->fd,
435          test == VCL_TEST_TYPE_BI ? "Bi" : "Uni");
436 }
437
438 static void
439 exit_client (void)
440 {
441   sock_client_main_t *scm = &sock_client_main;
442   vcl_test_session_t *ctrl = &scm->ctrl_socket;
443   vcl_test_session_t *tsock;
444   int i;
445
446   stinf ("af_unix_echo_tx %d, af_unix_echo_rx %d\n",
447          scm->af_unix_echo_tx, scm->af_unix_echo_rx);
448   for (i = 0; i < ctrl->cfg.num_test_sessions; i++)
449     {
450       tsock = &scm->test_socket[i];
451       tsock->cfg.test = VCL_TEST_TYPE_EXIT;
452
453       /* coverity[COPY_PASTE_ERROR] */
454       if (ctrl->cfg.verbose)
455         {
456           stinf ("\(fd %d): Sending exit cfg to server...\n", tsock->fd);
457           vcl_test_cfg_dump (&tsock->cfg, 1 /* is_client */ );
458         }
459       (void) sock_test_write (tsock->fd, (uint8_t *) & tsock->cfg,
460                               sizeof (tsock->cfg), &tsock->stats,
461                               ctrl->cfg.verbose);
462     }
463
464   ctrl->cfg.test = VCL_TEST_TYPE_EXIT;
465   if (ctrl->cfg.verbose)
466     {
467       stinf ("\n(fd %d): Sending exit cfg to server...\n", ctrl->fd);
468       vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
469     }
470   (void) sock_test_write (ctrl->fd, (uint8_t *) & ctrl->cfg,
471                           sizeof (ctrl->cfg), &ctrl->stats,
472                           ctrl->cfg.verbose);
473   stinf ("\nCLIENT: So long and thanks for all the fish!\n\n");
474   sleep (1);
475 }
476
477 static int
478 sock_test_connect_test_sockets (uint32_t num_test_sockets)
479 {
480   sock_client_main_t *scm = &sock_client_main;
481   vcl_test_session_t *ctrl = &scm->ctrl_socket;
482   vcl_test_session_t *tsock;
483   int i, rv;
484
485   if (num_test_sockets < 1)
486     {
487       errno = EINVAL;
488       return -1;
489     }
490
491   if (num_test_sockets < scm->num_test_sockets)
492     {
493       for (i = scm->num_test_sockets - 1; i >= num_test_sockets; i--)
494         {
495           tsock = &scm->test_socket[i];
496           close (tsock->fd);
497           free (tsock->txbuf);
498           free (tsock->rxbuf);
499         }
500     }
501
502   else if (num_test_sockets > scm->num_test_sockets)
503     {
504       tsock = realloc (scm->test_socket,
505                        sizeof (vcl_test_session_t) * num_test_sockets);
506       if (!tsock)
507         stfail ("realloc()");
508
509       memset (&tsock[scm->num_test_sockets], 0,
510               sizeof (vcl_test_session_t) * (num_test_sockets -
511                                              scm->num_test_sockets));
512
513       scm->test_socket = tsock;
514       for (i = scm->num_test_sockets; i < num_test_sockets; i++)
515         {
516           tsock = &scm->test_socket[i];
517           tsock->fd = socket (ctrl->cfg.address_ip6 ? AF_INET6 : AF_INET,
518                               ctrl->cfg.transport_udp ?
519                               SOCK_DGRAM : SOCK_STREAM, 0);
520
521           if (tsock->fd < 0)
522             stfail ("socket()");
523
524           rv = connect (tsock->fd, (struct sockaddr *) &scm->server_addr,
525                         scm->server_addr_size);
526
527           if (rv < 0)
528             stfail ("connect()");
529
530           if (fcntl (tsock->fd, F_SETFL, O_NONBLOCK) < 0)
531             stfail ("fcntl");
532
533           tsock->cfg = ctrl->cfg;
534           vcl_test_session_buf_alloc (tsock);
535           sock_test_cfg_sync (tsock);
536
537           stinf ("(fd %d): Test socket %d connected", tsock->fd, i);
538         }
539     }
540
541   scm->num_test_sockets = num_test_sockets;
542   stinf ("All sockets (%d) connected!\n", scm->num_test_sockets + 1);
543   return 0;
544 }
545
546 static void
547 cfg_txbuf_size_set (void)
548 {
549   sock_client_main_t *scm = &sock_client_main;
550   vcl_test_session_t *ctrl = &scm->ctrl_socket;
551   char *p = ctrl->txbuf + strlen (VCL_TEST_TOKEN_TXBUF_SIZE);
552   uint64_t txbuf_size = strtoull ((const char *) p, NULL, 10);
553
554   if (txbuf_size >= VCL_TEST_CFG_BUF_SIZE_MIN)
555     {
556       ctrl->cfg.txbuf_size = txbuf_size;
557       ctrl->cfg.total_bytes = ctrl->cfg.num_writes * ctrl->cfg.txbuf_size;
558       vcl_test_buf_alloc (&ctrl->cfg, 0 /* is_rxbuf */ ,
559                           (uint8_t **) & ctrl->txbuf, &ctrl->txbuf_size);
560       vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
561     }
562   else
563     stabrt ("Invalid txbuf size (%lu) < minimum buf size (%u)!",
564             txbuf_size, VCL_TEST_CFG_BUF_SIZE_MIN);
565 }
566
567 static void
568 cfg_num_writes_set (void)
569 {
570   sock_client_main_t *scm = &sock_client_main;
571   vcl_test_session_t *ctrl = &scm->ctrl_socket;
572   char *p = ctrl->txbuf + strlen (VCL_TEST_TOKEN_NUM_WRITES);
573   uint32_t num_writes = strtoul ((const char *) p, NULL, 10);
574
575   if (num_writes > 0)
576     {
577       ctrl->cfg.num_writes = num_writes;
578       ctrl->cfg.total_bytes = ctrl->cfg.num_writes * ctrl->cfg.txbuf_size;
579       vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
580     }
581   else
582     stabrt ("Invalid num writes: %u", num_writes);
583 }
584
585 static void
586 cfg_num_test_sockets_set (void)
587 {
588   sock_client_main_t *scm = &sock_client_main;
589   vcl_test_session_t *ctrl = &scm->ctrl_socket;
590   char *p = ctrl->txbuf + strlen (VCL_TEST_TOKEN_NUM_TEST_SESS);
591   uint32_t num_test_sockets = strtoul ((const char *) p, NULL, 10);
592
593   if ((num_test_sockets > 0) &&
594       (num_test_sockets <= VCL_TEST_CFG_MAX_TEST_SESS))
595     {
596       ctrl->cfg.num_test_sessions = num_test_sockets;
597       sock_test_connect_test_sockets (num_test_sockets);
598
599       vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
600     }
601   else
602     stabrt ("Invalid num test sockets: %u, (%d max)\n", num_test_sockets,
603             VCL_TEST_CFG_MAX_TEST_SESS);
604 }
605
606 static void
607 cfg_rxbuf_size_set (void)
608 {
609   sock_client_main_t *scm = &sock_client_main;
610   vcl_test_session_t *ctrl = &scm->ctrl_socket;
611   char *p = ctrl->txbuf + strlen (VCL_TEST_TOKEN_RXBUF_SIZE);
612   uint64_t rxbuf_size = strtoull ((const char *) p, NULL, 10);
613
614   if (rxbuf_size >= VCL_TEST_CFG_BUF_SIZE_MIN)
615     {
616       ctrl->cfg.rxbuf_size = rxbuf_size;
617       vcl_test_buf_alloc (&ctrl->cfg, 1 /* is_rxbuf */ ,
618                           (uint8_t **) & ctrl->rxbuf, &ctrl->rxbuf_size);
619       vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
620     }
621   else
622     stabrt ("Invalid rxbuf size (%lu) < minimum buf size (%u)!",
623             rxbuf_size, VCL_TEST_CFG_BUF_SIZE_MIN);
624 }
625
626 static void
627 cfg_verbose_toggle (void)
628 {
629   sock_client_main_t *scm = &sock_client_main;
630   vcl_test_session_t *ctrl = &scm->ctrl_socket;
631
632   ctrl->cfg.verbose = ctrl->cfg.verbose ? 0 : 1;
633   vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
634 }
635
636 static vcl_test_t
637 parse_input ()
638 {
639   sock_client_main_t *scm = &sock_client_main;
640   vcl_test_session_t *ctrl = &scm->ctrl_socket;
641   vcl_test_t rv = VCL_TEST_TYPE_NONE;
642
643   if (!strncmp (VCL_TEST_TOKEN_EXIT, ctrl->txbuf,
644                 strlen (VCL_TEST_TOKEN_EXIT)))
645     rv = VCL_TEST_TYPE_EXIT;
646
647   else if (!strncmp (VCL_TEST_TOKEN_HELP, ctrl->txbuf,
648                      strlen (VCL_TEST_TOKEN_HELP)))
649     dump_help ();
650
651   else if (!strncmp (VCL_TEST_TOKEN_SHOW_CFG, ctrl->txbuf,
652                      strlen (VCL_TEST_TOKEN_SHOW_CFG)))
653     scm->dump_cfg = 1;
654
655   else if (!strncmp (VCL_TEST_TOKEN_VERBOSE, ctrl->txbuf,
656                      strlen (VCL_TEST_TOKEN_VERBOSE)))
657     cfg_verbose_toggle ();
658
659   else if (!strncmp (VCL_TEST_TOKEN_TXBUF_SIZE, ctrl->txbuf,
660                      strlen (VCL_TEST_TOKEN_TXBUF_SIZE)))
661     cfg_txbuf_size_set ();
662
663   else if (!strncmp (VCL_TEST_TOKEN_NUM_TEST_SESS, ctrl->txbuf,
664                      strlen (VCL_TEST_TOKEN_NUM_TEST_SESS)))
665     cfg_num_test_sockets_set ();
666
667   else if (!strncmp (VCL_TEST_TOKEN_NUM_WRITES, ctrl->txbuf,
668                      strlen (VCL_TEST_TOKEN_NUM_WRITES)))
669     cfg_num_writes_set ();
670
671   else if (!strncmp (VCL_TEST_TOKEN_RXBUF_SIZE, ctrl->txbuf,
672                      strlen (VCL_TEST_TOKEN_RXBUF_SIZE)))
673     cfg_rxbuf_size_set ();
674
675   else if (!strncmp (VCL_TEST_TOKEN_RUN_UNI, ctrl->txbuf,
676                      strlen (VCL_TEST_TOKEN_RUN_UNI)))
677     rv = ctrl->cfg.test = VCL_TEST_TYPE_UNI;
678
679   else if (!strncmp (VCL_TEST_TOKEN_RUN_BI, ctrl->txbuf,
680                      strlen (VCL_TEST_TOKEN_RUN_BI)))
681     rv = ctrl->cfg.test = VCL_TEST_TYPE_BI;
682
683   else
684     rv = VCL_TEST_TYPE_ECHO;
685
686   return rv;
687 }
688
689 void
690 print_usage_and_exit (void)
691 {
692   stinf ("sock_test_client [OPTIONS] <ipaddr> <port>\n"
693          "  OPTIONS\n"
694          "  -h               Print this message and exit.\n"
695          "  -6               Use IPv6\n"
696          "  -u               Use UDP transport layer\n"
697          "  -c               Print test config before test.\n"
698          "  -w <dir>         Write test results to <dir>.\n"
699          "  -X               Exit after running test.\n"
700          "  -E               Run Echo test.\n"
701          "  -N <num-writes>  Test Cfg: number of writes.\n"
702          "  -R <rxbuf-size>  Test Cfg: rx buffer size.\n"
703          "  -T <txbuf-size>  Test Cfg: tx buffer size.\n"
704          "  -U               Run Uni-directional test.\n"
705          "  -B               Run Bi-directional test.\n"
706          "  -V               Verbose mode.\n");
707   exit (1);
708 }
709
710 int
711 main (int argc, char **argv)
712 {
713   sock_client_main_t *scm = &sock_client_main;
714   vcl_test_session_t *ctrl = &scm->ctrl_socket;
715   int c, rv;
716   vcl_test_t post_test = VCL_TEST_TYPE_NONE;
717
718   vcl_test_cfg_init (&ctrl->cfg);
719   vcl_test_session_buf_alloc (ctrl);
720
721   opterr = 0;
722   while ((c = getopt (argc, argv, "chn:w:XE:I:N:R:T:UBV6D")) != -1)
723     switch (c)
724       {
725       case 'c':
726         scm->dump_cfg = 1;
727         break;
728
729       case 's':
730         if (sscanf (optarg, "0x%x", &ctrl->cfg.num_test_sessions) != 1)
731           if (sscanf (optarg, "%u", &ctrl->cfg.num_test_sessions) != 1)
732             {
733               stinf ("ERROR: Invalid value for option -%c!", c);
734               print_usage_and_exit ();
735             }
736         if (!ctrl->cfg.num_test_sessions ||
737             (ctrl->cfg.num_test_sessions > FD_SETSIZE))
738           {
739             stinf ("ERROR: Invalid number of "
740                    "sockets (%d) specified for option -%c!\n"
741                    "       Valid range is 1 - %d\n",
742                    ctrl->cfg.num_test_sessions, c, FD_SETSIZE);
743             print_usage_and_exit ();
744           }
745         break;
746
747       case 'w':
748         stinf ("Writing test results to files is TBD.\n");
749         break;
750
751       case 'X':
752         post_test = VCL_TEST_TYPE_EXIT;
753         break;
754
755       case 'E':
756         if (strlen (optarg) > ctrl->txbuf_size)
757           {
758             stinf ("ERROR: Option -%c value larger than txbuf size (%d)!",
759                    optopt, ctrl->txbuf_size);
760             print_usage_and_exit ();
761           }
762         strncpy (ctrl->txbuf, optarg, ctrl->txbuf_size);
763         ctrl->cfg.test = VCL_TEST_TYPE_ECHO;
764         break;
765
766       case 'I':
767         if (sscanf (optarg, "0x%x", &ctrl->cfg.num_test_sessions) != 1)
768           if (sscanf (optarg, "%d", &ctrl->cfg.num_test_sessions) != 1)
769             {
770               stinf ("ERROR: Invalid value for option -%c!\n", c);
771               print_usage_and_exit ();
772             }
773         if (ctrl->cfg.num_test_sessions > VCL_TEST_CFG_MAX_TEST_SESS)
774           {
775             stinf ("ERROR: value greater than max number test sockets (%d)!",
776                    VCL_TEST_CFG_MAX_TEST_SESS);
777             print_usage_and_exit ();
778           }
779         break;
780
781       case 'N':
782         if (sscanf (optarg, "0x%lx", &ctrl->cfg.num_writes) != 1)
783           if (sscanf (optarg, "%ld", &ctrl->cfg.num_writes) != 1)
784             {
785               stinf ("ERROR: Invalid value for option -%c!", c);
786               print_usage_and_exit ();
787             }
788         ctrl->cfg.total_bytes = ctrl->cfg.num_writes * ctrl->cfg.txbuf_size;
789         break;
790
791       case 'R':
792         if (sscanf (optarg, "0x%lx", &ctrl->cfg.rxbuf_size) != 1)
793           if (sscanf (optarg, "%ld", &ctrl->cfg.rxbuf_size) != 1)
794             {
795               stinf ("ERROR: Invalid value for option -%c!", c);
796               print_usage_and_exit ();
797             }
798         if (ctrl->cfg.rxbuf_size >= VCL_TEST_CFG_BUF_SIZE_MIN)
799           {
800             ctrl->rxbuf_size = ctrl->cfg.rxbuf_size;
801             vcl_test_buf_alloc (&ctrl->cfg, 1 /* is_rxbuf */ ,
802                                 (uint8_t **) & ctrl->rxbuf,
803                                 &ctrl->rxbuf_size);
804           }
805         else
806           {
807             stinf ("ERROR: rxbuf size (%lu) less than minumum (%u)\n",
808                    ctrl->cfg.rxbuf_size, VCL_TEST_CFG_BUF_SIZE_MIN);
809             print_usage_and_exit ();
810           }
811
812         break;
813
814       case 'T':
815         if (sscanf (optarg, "0x%lx", &ctrl->cfg.txbuf_size) != 1)
816           if (sscanf (optarg, "%ld", &ctrl->cfg.txbuf_size) != 1)
817             {
818               stinf ("ERROR: Invalid value for option -%c!", c);
819               print_usage_and_exit ();
820             }
821         if (ctrl->cfg.txbuf_size >= VCL_TEST_CFG_BUF_SIZE_MIN)
822           {
823             ctrl->txbuf_size = ctrl->cfg.txbuf_size;
824             vcl_test_buf_alloc (&ctrl->cfg, 0 /* is_rxbuf */ ,
825                                 (uint8_t **) & ctrl->txbuf,
826                                 &ctrl->txbuf_size);
827             ctrl->cfg.total_bytes =
828               ctrl->cfg.num_writes * ctrl->cfg.txbuf_size;
829           }
830         else
831           {
832             stinf ("ERROR: txbuf size (%lu) less than minumum (%u)!",
833                    ctrl->cfg.txbuf_size, VCL_TEST_CFG_BUF_SIZE_MIN);
834             print_usage_and_exit ();
835           }
836         break;
837
838       case 'U':
839         ctrl->cfg.test = VCL_TEST_TYPE_UNI;
840         break;
841
842       case 'B':
843         ctrl->cfg.test = VCL_TEST_TYPE_BI;
844         break;
845
846       case 'V':
847         ctrl->cfg.verbose = 1;
848         break;
849
850       case '6':
851         ctrl->cfg.address_ip6 = 1;
852         break;
853
854       case 'D':
855         ctrl->cfg.transport_udp = 1;
856         break;
857
858       case '?':
859         switch (optopt)
860           {
861           case 'E':
862           case 'I':
863           case 'N':
864           case 'R':
865           case 'T':
866           case 'w':
867             stinf ("ERROR: Option -%c requires an argument.\n", optopt);
868             break;
869
870           default:
871             if (isprint (optopt))
872               stinf ("ERROR: Unknown option `-%c'.\n", optopt);
873             else
874               stinf ("ERROR: Unknown option character `\\x%x'.\n", optopt);
875           }
876         /* fall thru */
877       case 'h':
878       default:
879         print_usage_and_exit ();
880       }
881
882   if (argc < (optind + 2))
883     {
884       stinf ("ERROR: Insufficient number of arguments!\n");
885       print_usage_and_exit ();
886     }
887
888   ctrl->fd = socket (ctrl->cfg.address_ip6 ? AF_INET6 : AF_INET,
889                      ctrl->cfg.transport_udp ? SOCK_DGRAM : SOCK_STREAM, 0);
890
891   if (ctrl->fd < 0)
892     stfail ("socket()");
893
894   memset (&scm->server_addr, 0, sizeof (scm->server_addr));
895   if (ctrl->cfg.address_ip6)
896     {
897       struct sockaddr_in6 *server_addr =
898         (struct sockaddr_in6 *) &scm->server_addr;
899       scm->server_addr_size = sizeof (*server_addr);
900       server_addr->sin6_family = AF_INET6;
901       inet_pton (AF_INET6, argv[optind++], &(server_addr->sin6_addr));
902       server_addr->sin6_port = htons (atoi (argv[optind]));
903     }
904   else
905     {
906       struct sockaddr_in *server_addr =
907         (struct sockaddr_in *) &scm->server_addr;
908       scm->server_addr_size = sizeof (*server_addr);
909       server_addr->sin_family = AF_INET;
910       inet_pton (AF_INET, argv[optind++], &(server_addr->sin_addr));
911       server_addr->sin_port = htons (atoi (argv[optind]));
912     }
913
914   do
915     {
916       stinf ("\nConnecting to server...\n");
917
918       rv = connect (ctrl->fd, (struct sockaddr *) &scm->server_addr,
919                     scm->server_addr_size);
920
921       if (rv < 0)
922         stfail ("connect()");
923
924       sock_test_cfg_sync (ctrl);
925       stinf ("(fd %d): Control socket connected.\n", ctrl->fd);
926     }
927   while (rv < 0);
928
929   sock_test_connect_test_sockets (ctrl->cfg.num_test_sessions);
930
931   while (ctrl->cfg.test != VCL_TEST_TYPE_EXIT)
932     {
933       if (scm->dump_cfg)
934         {
935           vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
936           scm->dump_cfg = 0;
937         }
938
939       switch (ctrl->cfg.test)
940         {
941         case VCL_TEST_TYPE_ECHO:
942           echo_test_client ();
943           break;
944
945         case VCL_TEST_TYPE_UNI:
946         case VCL_TEST_TYPE_BI:
947           stream_test_client (ctrl->cfg.test);
948           break;
949
950         case VCL_TEST_TYPE_EXIT:
951           continue;
952
953         case VCL_TEST_TYPE_NONE:
954         default:
955           break;
956         }
957       switch (post_test)
958         {
959         case VCL_TEST_TYPE_EXIT:
960           switch (ctrl->cfg.test)
961             {
962             case VCL_TEST_TYPE_EXIT:
963             case VCL_TEST_TYPE_UNI:
964             case VCL_TEST_TYPE_BI:
965             case VCL_TEST_TYPE_ECHO:
966               ctrl->cfg.test = VCL_TEST_TYPE_EXIT;
967               continue;
968
969             case VCL_TEST_TYPE_NONE:
970             default:
971               break;
972             }
973           break;
974
975         case VCL_TEST_TYPE_NONE:
976         case VCL_TEST_TYPE_ECHO:
977         case VCL_TEST_TYPE_UNI:
978         case VCL_TEST_TYPE_BI:
979         default:
980           break;
981         }
982
983       memset (ctrl->txbuf, 0, ctrl->txbuf_size);
984       memset (ctrl->rxbuf, 0, ctrl->rxbuf_size);
985
986       stinf ("\nType some characters and hit <return>\n"
987              "('" VCL_TEST_TOKEN_HELP "' for help): ");
988
989       if (fgets (ctrl->txbuf, ctrl->txbuf_size, stdin) != NULL)
990         {
991           if (strlen (ctrl->txbuf) == 1)
992             {
993               stinf ("\nNothing to send!  Please try again...\n");
994               continue;
995             }
996           ctrl->txbuf[strlen (ctrl->txbuf) - 1] = 0;    // chomp the newline.
997
998           /* Parse input for keywords */
999           ctrl->cfg.test = parse_input ();
1000         }
1001     }
1002
1003   exit_client ();
1004   close (ctrl->fd);
1005   return (scm->af_unix_echo_tx == scm->af_unix_echo_rx) ? 0 : -1;
1006 }
1007
1008 /*
1009  * fd.io coding-style-patch-verification: ON
1010  *
1011  * Local Variables:
1012  * eval: (c-set-style "gnu")
1013  * End:
1014  */