vcl: add support for multi-worker apps
[vpp.git] / src / vcl / vcl_test_client.c
1 /*
2  * Copyright (c) 2017-2018 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 <vcl/vcl_test.h>
26
27 typedef struct
28 {
29   vppcom_endpt_t server_endpt;
30   struct sockaddr_storage server_addr;
31   uint32_t server_addr_size;
32   uint32_t cfg_seq_num;
33   sock_test_socket_t ctrl_socket;
34   sock_test_socket_t *test_socket;
35   uint32_t num_test_sockets;
36   uint8_t dump_cfg;
37 } sock_client_main_t;
38
39 sock_client_main_t sock_client_main;
40
41
42 static int
43 sock_test_cfg_sync (sock_test_socket_t * socket)
44 {
45   sock_client_main_t *scm = &sock_client_main;
46   sock_test_socket_t *ctrl = &scm->ctrl_socket;
47   sock_test_cfg_t *rl_cfg = (sock_test_cfg_t *) socket->rxbuf;
48   int rx_bytes, tx_bytes;
49
50   if (socket->cfg.verbose)
51     sock_test_cfg_dump (&socket->cfg, 1 /* is_client */ );
52
53   ctrl->cfg.seq_num = ++scm->cfg_seq_num;
54   if (socket->cfg.verbose)
55     {
56       printf ("CLIENT (fd %d): Sending config sent to server.\n", socket->fd);
57       sock_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
58     }
59   tx_bytes = vcl_test_write (socket->fd, (uint8_t *) & ctrl->cfg,
60                              sizeof (ctrl->cfg), NULL, ctrl->cfg.verbose);
61   if (tx_bytes < 0)
62     {
63       fprintf (stderr, "CLIENT (fd %d): ERROR: write test cfg failed (%d)!\n",
64                socket->fd, tx_bytes);
65       return tx_bytes;
66     }
67
68   rx_bytes = vcl_test_read (socket->fd, (uint8_t *) socket->rxbuf,
69                             sizeof (sock_test_cfg_t), NULL);
70   if (rx_bytes < 0)
71     return rx_bytes;
72
73   if (rl_cfg->magic != SOCK_TEST_CFG_CTRL_MAGIC)
74     {
75       fprintf (stderr, "CLIENT (fd %d): ERROR: Bad server reply cfg "
76                "-- aborting!\n", socket->fd);
77       return -1;
78     }
79   if ((rx_bytes != sizeof (sock_test_cfg_t))
80       || !sock_test_cfg_verify (rl_cfg, &ctrl->cfg))
81     {
82       fprintf (stderr, "CLIENT (fd %d): ERROR: Invalid config received "
83                "from server!\n", socket->fd);
84       if (rx_bytes != sizeof (sock_test_cfg_t))
85         {
86           fprintf (stderr, "\tRx bytes %d != cfg size %lu\n",
87                    rx_bytes, sizeof (sock_test_cfg_t));
88         }
89       else
90         {
91           sock_test_cfg_dump (rl_cfg, 1 /* is_client */ );
92           fprintf (stderr, "CLIENT (fd %d): Valid config sent to server.\n",
93                    socket->fd);
94           sock_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
95         }
96       return -1;
97     }
98   else if (socket->cfg.verbose)
99     {
100       printf ("CLIENT (fd %d): Got config back from server.\n", socket->fd);
101       sock_test_cfg_dump (rl_cfg, 1 /* is_client */ );
102     }
103   ctrl->cfg.ctrl_handle = ((ctrl->cfg.ctrl_handle == ~0) ?
104                            rl_cfg->ctrl_handle : ctrl->cfg.ctrl_handle);
105
106   return 0;
107 }
108
109 static void
110 echo_test_client ()
111 {
112   sock_client_main_t *scm = &sock_client_main;
113   sock_test_socket_t *ctrl = &scm->ctrl_socket;
114   sock_test_socket_t *tsock;
115   int rv, nfds = 0, rx_bytes, tx_bytes, nbytes;
116   uint32_t i, n, sidx;
117   fd_set wr_fdset, rd_fdset;
118   fd_set _wfdset, *wfdset = &_wfdset;
119   fd_set _rfdset, *rfdset = &_rfdset;
120
121   FD_ZERO (&wr_fdset);
122   FD_ZERO (&rd_fdset);
123   memset (&ctrl->stats, 0, sizeof (ctrl->stats));
124   ctrl->cfg.total_bytes = nbytes = strlen (ctrl->txbuf) + 1;
125   for (n = 0; n != ctrl->cfg.num_test_sockets; n++)
126     {
127       tsock = &scm->test_socket[n];
128       tsock->cfg = ctrl->cfg;
129       sock_test_socket_buf_alloc (tsock);
130       if (sock_test_cfg_sync (tsock))
131         return;
132
133       memcpy (tsock->txbuf, ctrl->txbuf, nbytes);
134       memset (&tsock->stats, 0, sizeof (tsock->stats));
135
136       FD_SET (tsock->fd, &wr_fdset);
137       FD_SET (tsock->fd, &rd_fdset);
138       sidx = vppcom_session_index (tsock->fd);
139       nfds = sidx > nfds ? sidx : nfds;
140 //      nfds = ((tsock->fd + 1) > nfds) ? (tsock->fd + 1) : nfds;
141     }
142
143   nfds++;
144   clock_gettime (CLOCK_REALTIME, &ctrl->stats.start);
145   while (n)
146     {
147       _wfdset = wr_fdset;
148       _rfdset = rd_fdset;
149
150       rv = vppcom_select (nfds, (uint64_t *) rfdset, (uint64_t *) wfdset,
151                           NULL, 0);
152       if (rv < 0)
153         {
154           perror ("select()");
155           fprintf (stderr, "\nCLIENT: ERROR: select() failed -- "
156                    "aborting test!\n");
157           return;
158         }
159       else if (rv == 0)
160         continue;
161
162       for (i = 0; i < ctrl->cfg.num_test_sockets; i++)
163         {
164           tsock = &scm->test_socket[i];
165           if (!((tsock->stats.stop.tv_sec == 0) &&
166                 (tsock->stats.stop.tv_nsec == 0)))
167             continue;
168
169           if (FD_ISSET (vppcom_session_index (tsock->fd), wfdset)
170               && (tsock->stats.tx_bytes < ctrl->cfg.total_bytes))
171
172             {
173               tx_bytes = vcl_test_write (tsock->fd, (uint8_t *) tsock->txbuf,
174                                          nbytes, &tsock->stats,
175                                          ctrl->cfg.verbose);
176               if (tx_bytes < 0)
177                 {
178                   fprintf (stderr, "\nCLIENT: ERROR: vcl_test_write(%d) "
179                            "failed -- aborting test!\n", tsock->fd);
180                   return;
181                 }
182
183             }
184
185           if ((FD_ISSET (vppcom_session_index (tsock->fd), rfdset)) &&
186               (tsock->stats.rx_bytes < ctrl->cfg.total_bytes))
187             {
188               rx_bytes = vcl_test_read (tsock->fd, (uint8_t *) tsock->rxbuf,
189                                         nbytes, &tsock->stats);
190               if (rx_bytes > 0)
191                 {
192                   printf ("CLIENT (fd %d): RX (%d bytes) - '%s'\n",
193                           tsock->fd, rx_bytes, tsock->rxbuf);
194
195                   if (tsock->stats.rx_bytes != tsock->stats.tx_bytes)
196                     printf ("CLIENT: WARNING: bytes read (%lu) "
197                             "!= bytes written (%lu)!\n",
198                             tsock->stats.rx_bytes, tsock->stats.tx_bytes);
199                 }
200             }
201
202           if (tsock->stats.rx_bytes >= ctrl->cfg.total_bytes)
203             {
204               clock_gettime (CLOCK_REALTIME, &tsock->stats.stop);
205               n--;
206             }
207         }
208     }
209   clock_gettime (CLOCK_REALTIME, &ctrl->stats.stop);
210
211   for (i = 0; i < ctrl->cfg.num_test_sockets; i++)
212     {
213       tsock = &scm->test_socket[i];
214       tsock->stats.start = ctrl->stats.start;
215
216       if (ctrl->cfg.verbose)
217         {
218           static char buf[64];
219
220           sprintf (buf, "CLIENT (fd %d) RESULTS", tsock->fd);
221           sock_test_stats_dump (buf, &tsock->stats,
222                                 1 /* show_rx */ , 1 /* show tx */ ,
223                                 ctrl->cfg.verbose);
224         }
225
226       sock_test_stats_accumulate (&ctrl->stats, &tsock->stats);
227     }
228
229   if (ctrl->cfg.verbose)
230     {
231       sock_test_stats_dump ("CLIENT RESULTS", &ctrl->stats,
232                             1 /* show_rx */ , 1 /* show tx */ ,
233                             ctrl->cfg.verbose);
234       sock_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
235
236       if (ctrl->cfg.verbose > 1)
237         {
238           printf ("  ctrl socket info\n"
239                   SOCK_TEST_SEPARATOR_STRING
240                   "          fd:  %d (0x%08x)\n"
241                   "       rxbuf:  %p\n"
242                   "  rxbuf size:  %u (0x%08x)\n"
243                   "       txbuf:  %p\n"
244                   "  txbuf size:  %u (0x%08x)\n"
245                   SOCK_TEST_SEPARATOR_STRING,
246                   ctrl->fd, (uint32_t) ctrl->fd,
247                   ctrl->rxbuf, ctrl->rxbuf_size, ctrl->rxbuf_size,
248                   ctrl->txbuf, ctrl->txbuf_size, ctrl->txbuf_size);
249         }
250     }
251 }
252
253 static void
254 stream_test_client (sock_test_t test)
255 {
256   sock_client_main_t *scm = &sock_client_main;
257   sock_test_socket_t *ctrl = &scm->ctrl_socket;
258   sock_test_socket_t *tsock;
259   int tx_bytes;
260   uint32_t i, n;
261   int rv;
262   int nfds = 0;
263   fd_set wr_fdset, rd_fdset;
264   fd_set _wfdset, *wfdset = &_wfdset;
265   fd_set _rfdset, *rfdset = (test == SOCK_TEST_TYPE_BI) ? &_rfdset : 0;
266
267   ctrl->cfg.total_bytes = ctrl->cfg.num_writes * ctrl->cfg.txbuf_size;
268   ctrl->cfg.ctrl_handle = ~0;
269
270   printf ("\n" SOCK_TEST_BANNER_STRING
271           "CLIENT (fd %d): %s-directional Stream Test!\n\n"
272           "CLIENT (fd %d): Sending config to server on ctrl socket...\n",
273           ctrl->fd, test == SOCK_TEST_TYPE_BI ? "Bi" : "Uni", ctrl->fd);
274
275   if (sock_test_cfg_sync (ctrl))
276     {
277       fprintf (stderr, "CLIENT: ERROR: test cfg sync failed -- aborting!");
278       return;
279     }
280
281   FD_ZERO (&wr_fdset);
282   FD_ZERO (&rd_fdset);
283   memset (&ctrl->stats, 0, sizeof (ctrl->stats));
284   for (n = 0; n != ctrl->cfg.num_test_sockets; n++)
285     {
286       tsock = &scm->test_socket[n];
287       tsock->cfg = ctrl->cfg;
288       sock_test_socket_buf_alloc (tsock);
289       printf ("CLIENT (fd %d): Sending config to server on "
290               "test socket %d...\n", tsock->fd, n);
291       sock_test_cfg_sync (tsock);
292
293       /* Fill payload with incrementing uint32's */
294       for (i = 0; i < tsock->txbuf_size; i++)
295         tsock->txbuf[i] = i & 0xff;
296
297       memset (&tsock->stats, 0, sizeof (tsock->stats));
298       FD_SET (tsock->fd, &wr_fdset);
299       FD_SET (tsock->fd, &rd_fdset);
300       nfds = ((tsock->fd + 1) > nfds) ? (tsock->fd + 1) : nfds;
301     }
302
303   nfds++;
304   clock_gettime (CLOCK_REALTIME, &ctrl->stats.start);
305   while (n)
306     {
307       _wfdset = wr_fdset;
308       _rfdset = rd_fdset;
309
310       rv = vppcom_select (nfds, (uint64_t *) rfdset, (uint64_t *) wfdset,
311                           NULL, 0);
312       if (rv < 0)
313         {
314           perror ("select()");
315           fprintf (stderr, "\nCLIENT: ERROR: select() failed -- "
316                    "aborting test!\n");
317           return;
318         }
319       else if (rv == 0)
320         continue;
321
322       for (i = 0; i < ctrl->cfg.num_test_sockets; i++)
323         {
324           tsock = &scm->test_socket[i];
325           if (!((tsock->stats.stop.tv_sec == 0) &&
326                 (tsock->stats.stop.tv_nsec == 0)))
327             continue;
328
329           if ((test == SOCK_TEST_TYPE_BI) &&
330               FD_ISSET (tsock->fd, rfdset) &&
331               (tsock->stats.rx_bytes < ctrl->cfg.total_bytes))
332             {
333               (void) vcl_test_read (tsock->fd,
334                                     (uint8_t *) tsock->rxbuf,
335                                     tsock->rxbuf_size, &tsock->stats);
336             }
337
338           if (FD_ISSET (tsock->fd, wfdset)
339               && (tsock->stats.tx_bytes < ctrl->cfg.total_bytes))
340             {
341               tx_bytes = vcl_test_write (tsock->fd, (uint8_t *) tsock->txbuf,
342                                          ctrl->cfg.txbuf_size, &tsock->stats,
343                                          ctrl->cfg.verbose);
344               if (tx_bytes < 0)
345                 {
346                   fprintf (stderr, "\nCLIENT: ERROR: vcl_test_write(%d) "
347                            "failed -- aborting test!\n", tsock->fd);
348                   return;
349                 }
350             }
351
352           if (((test == SOCK_TEST_TYPE_UNI) &&
353                (tsock->stats.tx_bytes >= ctrl->cfg.total_bytes)) ||
354               ((test == SOCK_TEST_TYPE_BI) &&
355                (tsock->stats.rx_bytes >= ctrl->cfg.total_bytes)))
356             {
357               clock_gettime (CLOCK_REALTIME, &tsock->stats.stop);
358               n--;
359             }
360         }
361     }
362   clock_gettime (CLOCK_REALTIME, &ctrl->stats.stop);
363
364   printf ("CLIENT (fd %d): Sending config to server on ctrl socket...\n",
365           ctrl->fd);
366
367   if (sock_test_cfg_sync (ctrl))
368     {
369       fprintf (stderr, "CLIENT: ERROR: test cfg sync failed -- aborting!");
370       return;
371     }
372
373   for (i = 0; i < ctrl->cfg.num_test_sockets; i++)
374     {
375       tsock = &scm->test_socket[i];
376
377       if (ctrl->cfg.verbose)
378         {
379           static char buf[64];
380
381           sprintf (buf, "CLIENT (fd %d) RESULTS", tsock->fd);
382           sock_test_stats_dump (buf, &tsock->stats,
383                                 test == SOCK_TEST_TYPE_BI /* show_rx */ ,
384                                 1 /* show tx */ , ctrl->cfg.verbose);
385         }
386
387       sock_test_stats_accumulate (&ctrl->stats, &tsock->stats);
388     }
389
390   sock_test_stats_dump ("CLIENT RESULTS", &ctrl->stats,
391                         test == SOCK_TEST_TYPE_BI /* show_rx */ ,
392                         1 /* show tx */ , ctrl->cfg.verbose);
393   sock_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
394
395   if (ctrl->cfg.verbose)
396     {
397       printf ("  ctrl socket info\n"
398               SOCK_TEST_SEPARATOR_STRING
399               "          fd:  %d (0x%08x)\n"
400               "       rxbuf:  %p\n"
401               "  rxbuf size:  %u (0x%08x)\n"
402               "       txbuf:  %p\n"
403               "  txbuf size:  %u (0x%08x)\n"
404               SOCK_TEST_SEPARATOR_STRING,
405               ctrl->fd, (uint32_t) ctrl->fd,
406               ctrl->rxbuf, ctrl->rxbuf_size, ctrl->rxbuf_size,
407               ctrl->txbuf, ctrl->txbuf_size, ctrl->txbuf_size);
408     }
409
410   ctrl->cfg.test = SOCK_TEST_TYPE_ECHO;
411   if (sock_test_cfg_sync (ctrl))
412     fprintf (stderr, "CLIENT: ERROR: post-test cfg sync failed!");
413
414   printf ("CLIENT (fd %d): %s-directional Stream Test Complete!\n"
415           SOCK_TEST_BANNER_STRING "\n", ctrl->fd,
416           test == SOCK_TEST_TYPE_BI ? "Bi" : "Uni");
417 }
418
419 static void
420 exit_client (void)
421 {
422   sock_client_main_t *scm = &sock_client_main;
423   sock_test_socket_t *ctrl = &scm->ctrl_socket;
424   sock_test_socket_t *tsock;
425   int i;
426
427   for (i = 0; i < ctrl->cfg.num_test_sockets; i++)
428     {
429       tsock = &scm->test_socket[i];
430       tsock->cfg.test = SOCK_TEST_TYPE_EXIT;
431
432       /* coverity[COPY_PASTE_ERROR] */
433       if (ctrl->cfg.verbose)
434         {
435           printf ("\nCLIENT (fd %d): Sending exit cfg to server...\n",
436                   tsock->fd);
437           sock_test_cfg_dump (&tsock->cfg, 1 /* is_client */ );
438         }
439       (void) vcl_test_write (tsock->fd, (uint8_t *) & tsock->cfg,
440                              sizeof (tsock->cfg), &tsock->stats,
441                              ctrl->cfg.verbose);
442     }
443
444   ctrl->cfg.test = SOCK_TEST_TYPE_EXIT;
445   if (ctrl->cfg.verbose)
446     {
447       printf ("\nCLIENT (fd %d): Sending exit cfg to server...\n", ctrl->fd);
448       sock_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
449     }
450   (void) vcl_test_write (ctrl->fd, (uint8_t *) & ctrl->cfg,
451                          sizeof (ctrl->cfg), &ctrl->stats, ctrl->cfg.verbose);
452   printf ("\nCLIENT: So long and thanks for all the fish!\n\n");
453   sleep (1);
454 }
455
456 static int
457 sock_test_connect_test_sockets (uint32_t num_test_sockets)
458 {
459   sock_client_main_t *scm = &sock_client_main;
460   sock_test_socket_t *ctrl = &scm->ctrl_socket;
461   sock_test_socket_t *tsock;
462   int i, rv, errno_val;
463
464   if (num_test_sockets < 1)
465     {
466       errno = EINVAL;
467       return -1;
468     }
469
470   if (num_test_sockets < scm->num_test_sockets)
471     {
472       for (i = scm->num_test_sockets - 1; i >= num_test_sockets; i--)
473         {
474           tsock = &scm->test_socket[i];
475           vppcom_session_close (tsock->fd);
476           free (tsock->txbuf);
477           free (tsock->rxbuf);
478         }
479     }
480
481   else if (num_test_sockets > scm->num_test_sockets)
482     {
483       tsock = realloc (scm->test_socket,
484                        sizeof (sock_test_socket_t) * num_test_sockets);
485       if (!tsock)
486         {
487           errno_val = errno;
488           perror ("ERROR in sock_test_connect_test_sockets()");
489           fprintf (stderr, "CLIENT: ERROR: socket failed (errno = %d)!\n",
490                    errno_val);
491           return -1;
492         }
493
494       if (!scm->test_socket)
495         memset (tsock, 0, sizeof (*tsock));
496
497       scm->test_socket = tsock;
498       for (i = scm->num_test_sockets; i < num_test_sockets; i++)
499         {
500           tsock = &scm->test_socket[i];
501           tsock->fd = vppcom_session_create (ctrl->cfg.transport_udp ?
502                                              VPPCOM_PROTO_UDP :
503                                              VPPCOM_PROTO_TCP,
504                                              1 /* is_nonblocking */ );
505           if (tsock->fd < 0)
506             {
507               errno = -tsock->fd;
508               tsock->fd = -1;
509             }
510           if (tsock->fd < 0)
511             {
512               errno_val = errno;
513               perror ("ERROR in sock_test_connect_test_sockets()");
514               fprintf (stderr, "CLIENT: ERROR: socket failed (errno = %d)!\n",
515                        errno_val);
516               return tsock->fd;
517             }
518
519           rv = vppcom_session_connect (tsock->fd, &scm->server_endpt);
520           if (rv)
521             {
522               errno = -rv;
523               rv = -1;
524             }
525           if (rv < 0)
526             {
527               errno_val = errno;
528               perror ("ERROR in sock_test_connect_test_sockets()");
529               fprintf (stderr, "CLIENT: ERROR: connect failed "
530                        "(errno = %d)!\n", errno_val);
531               return -1;
532             }
533           tsock->cfg = ctrl->cfg;
534           sock_test_socket_buf_alloc (tsock);
535           sock_test_cfg_sync (tsock);
536
537           printf ("CLIENT (fd %d): Test socket %d connected.\n",
538                   tsock->fd, i);
539         }
540     }
541
542   scm->num_test_sockets = num_test_sockets;
543   printf ("CLIENT: All sockets (%d) connected!\n", scm->num_test_sockets + 1);
544   return 0;
545 }
546
547 static void
548 dump_help (void)
549 {
550 #define INDENT "\n  "
551
552   printf ("CLIENT: Test configuration commands:"
553           INDENT SOCK_TEST_TOKEN_HELP
554           "\t\t\tDisplay help."
555           INDENT SOCK_TEST_TOKEN_EXIT
556           "\t\t\tExit test client & server."
557           INDENT SOCK_TEST_TOKEN_SHOW_CFG
558           "\t\t\tShow the current test cfg."
559           INDENT SOCK_TEST_TOKEN_RUN_UNI
560           "\t\t\tRun the Uni-directional test."
561           INDENT SOCK_TEST_TOKEN_RUN_BI
562           "\t\t\tRun the Bi-directional test."
563           INDENT SOCK_TEST_TOKEN_VERBOSE
564           "\t\t\tToggle verbose setting."
565           INDENT SOCK_TEST_TOKEN_RXBUF_SIZE
566           "<rxbuf size>\tRx buffer size (bytes)."
567           INDENT SOCK_TEST_TOKEN_TXBUF_SIZE
568           "<txbuf size>\tTx buffer size (bytes)."
569           INDENT SOCK_TEST_TOKEN_NUM_WRITES
570           "<# of writes>\tNumber of txbuf writes to server." "\n");
571 }
572
573 static void
574 cfg_txbuf_size_set (void)
575 {
576   sock_client_main_t *scm = &sock_client_main;
577   sock_test_socket_t *ctrl = &scm->ctrl_socket;
578   char *p = ctrl->txbuf + strlen (SOCK_TEST_TOKEN_TXBUF_SIZE);
579   uint64_t txbuf_size = strtoull ((const char *) p, NULL, 10);
580
581   if (txbuf_size >= SOCK_TEST_CFG_BUF_SIZE_MIN)
582     {
583       ctrl->cfg.txbuf_size = txbuf_size;
584       ctrl->cfg.total_bytes = ctrl->cfg.num_writes * ctrl->cfg.txbuf_size;
585       sock_test_buf_alloc (&ctrl->cfg, 0 /* is_rxbuf */ ,
586                            (uint8_t **) & ctrl->txbuf, &ctrl->txbuf_size);
587       sock_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
588     }
589   else
590     fprintf (stderr, "CLIENT: ERROR: Invalid txbuf size (%lu) < "
591              "minimum buf size (%u)!\n",
592              txbuf_size, SOCK_TEST_CFG_BUF_SIZE_MIN);
593 }
594
595 static void
596 cfg_num_writes_set (void)
597 {
598   sock_client_main_t *scm = &sock_client_main;
599   sock_test_socket_t *ctrl = &scm->ctrl_socket;
600   char *p = ctrl->txbuf + strlen (SOCK_TEST_TOKEN_NUM_WRITES);
601   uint32_t num_writes = strtoul ((const char *) p, NULL, 10);
602
603   if (num_writes > 0)
604     {
605       ctrl->cfg.num_writes = num_writes;
606       ctrl->cfg.total_bytes = ctrl->cfg.num_writes * ctrl->cfg.txbuf_size;
607       sock_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
608     }
609   else
610     {
611       fprintf (stderr, "CLIENT: ERROR: invalid num writes: %u\n", num_writes);
612     }
613 }
614
615 static void
616 cfg_num_test_sockets_set (void)
617 {
618   sock_client_main_t *scm = &sock_client_main;
619   sock_test_socket_t *ctrl = &scm->ctrl_socket;
620   char *p = ctrl->txbuf + strlen (SOCK_TEST_TOKEN_NUM_TEST_SCKTS);
621   uint32_t num_test_sockets = strtoul ((const char *) p, NULL, 10);
622
623   if ((num_test_sockets > 0) &&
624       (num_test_sockets <= SOCK_TEST_CFG_MAX_TEST_SCKTS))
625     {
626       ctrl->cfg.num_test_sockets = num_test_sockets;
627       sock_test_connect_test_sockets (num_test_sockets);
628
629       sock_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
630     }
631   else
632     {
633       fprintf (stderr, "CLIENT: ERROR: invalid num test sockets: "
634                "%u, (%d max)\n",
635                num_test_sockets, SOCK_TEST_CFG_MAX_TEST_SCKTS);
636     }
637 }
638
639 static void
640 cfg_rxbuf_size_set (void)
641 {
642   sock_client_main_t *scm = &sock_client_main;
643   sock_test_socket_t *ctrl = &scm->ctrl_socket;
644   char *p = ctrl->txbuf + strlen (SOCK_TEST_TOKEN_RXBUF_SIZE);
645   uint64_t rxbuf_size = strtoull ((const char *) p, NULL, 10);
646
647   if (rxbuf_size >= SOCK_TEST_CFG_BUF_SIZE_MIN)
648     {
649       ctrl->cfg.rxbuf_size = rxbuf_size;
650       sock_test_buf_alloc (&ctrl->cfg, 1 /* is_rxbuf */ ,
651                            (uint8_t **) & ctrl->rxbuf, &ctrl->rxbuf_size);
652       sock_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
653     }
654   else
655     fprintf (stderr, "CLIENT: ERROR: Invalid rxbuf size (%lu) < "
656              "minimum buf size (%u)!\n",
657              rxbuf_size, SOCK_TEST_CFG_BUF_SIZE_MIN);
658 }
659
660 static void
661 cfg_verbose_toggle (void)
662 {
663   sock_client_main_t *scm = &sock_client_main;
664   sock_test_socket_t *ctrl = &scm->ctrl_socket;
665
666   ctrl->cfg.verbose = ctrl->cfg.verbose ? 0 : 1;
667   sock_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
668
669 }
670
671 static sock_test_t
672 parse_input ()
673 {
674   sock_client_main_t *scm = &sock_client_main;
675   sock_test_socket_t *ctrl = &scm->ctrl_socket;
676   sock_test_t rv = SOCK_TEST_TYPE_NONE;
677
678   if (!strncmp (SOCK_TEST_TOKEN_EXIT, ctrl->txbuf,
679                 strlen (SOCK_TEST_TOKEN_EXIT)))
680     rv = SOCK_TEST_TYPE_EXIT;
681
682   else if (!strncmp (SOCK_TEST_TOKEN_HELP, ctrl->txbuf,
683                      strlen (SOCK_TEST_TOKEN_HELP)))
684     dump_help ();
685
686   else if (!strncmp (SOCK_TEST_TOKEN_SHOW_CFG, ctrl->txbuf,
687                      strlen (SOCK_TEST_TOKEN_SHOW_CFG)))
688     scm->dump_cfg = 1;
689
690   else if (!strncmp (SOCK_TEST_TOKEN_VERBOSE, ctrl->txbuf,
691                      strlen (SOCK_TEST_TOKEN_VERBOSE)))
692     cfg_verbose_toggle ();
693
694   else if (!strncmp (SOCK_TEST_TOKEN_TXBUF_SIZE, ctrl->txbuf,
695                      strlen (SOCK_TEST_TOKEN_TXBUF_SIZE)))
696     cfg_txbuf_size_set ();
697
698   else if (!strncmp (SOCK_TEST_TOKEN_NUM_TEST_SCKTS, ctrl->txbuf,
699                      strlen (SOCK_TEST_TOKEN_NUM_TEST_SCKTS)))
700     cfg_num_test_sockets_set ();
701
702   else if (!strncmp (SOCK_TEST_TOKEN_NUM_WRITES, ctrl->txbuf,
703                      strlen (SOCK_TEST_TOKEN_NUM_WRITES)))
704     cfg_num_writes_set ();
705
706   else if (!strncmp (SOCK_TEST_TOKEN_RXBUF_SIZE, ctrl->txbuf,
707                      strlen (SOCK_TEST_TOKEN_RXBUF_SIZE)))
708     cfg_rxbuf_size_set ();
709
710   else if (!strncmp (SOCK_TEST_TOKEN_RUN_UNI, ctrl->txbuf,
711                      strlen (SOCK_TEST_TOKEN_RUN_UNI)))
712     rv = ctrl->cfg.test = SOCK_TEST_TYPE_UNI;
713
714   else if (!strncmp (SOCK_TEST_TOKEN_RUN_BI, ctrl->txbuf,
715                      strlen (SOCK_TEST_TOKEN_RUN_BI)))
716     rv = ctrl->cfg.test = SOCK_TEST_TYPE_BI;
717
718   else
719     rv = SOCK_TEST_TYPE_ECHO;
720
721   return rv;
722 }
723
724 void
725 print_usage_and_exit (void)
726 {
727   fprintf (stderr,
728            "sock_test_client [OPTIONS] <ipaddr> <port>\n"
729            "  OPTIONS\n"
730            "  -h               Print this message and exit.\n"
731            "  -6               Use IPv6\n"
732            "  -u               Use UDP transport layer\n"
733            "  -c               Print test config before test.\n"
734            "  -w <dir>         Write test results to <dir>.\n"
735            "  -X               Exit after running test.\n"
736            "  -E               Run Echo test.\n"
737            "  -N <num-writes>  Test Cfg: number of writes.\n"
738            "  -R <rxbuf-size>  Test Cfg: rx buffer size.\n"
739            "  -T <txbuf-size>  Test Cfg: tx buffer size.\n"
740            "  -U               Run Uni-directional test.\n"
741            "  -B               Run Bi-directional test.\n"
742            "  -V               Verbose mode.\n");
743   exit (1);
744 }
745
746 int
747 main (int argc, char **argv)
748 {
749   sock_client_main_t *scm = &sock_client_main;
750   sock_test_socket_t *ctrl = &scm->ctrl_socket;
751   int c, rv, errno_val;
752   sock_test_t post_test = SOCK_TEST_TYPE_NONE;
753
754   sock_test_cfg_init (&ctrl->cfg);
755   sock_test_socket_buf_alloc (ctrl);
756
757   opterr = 0;
758   while ((c = getopt (argc, argv, "chn:w:XE:I:N:R:T:UBV6D")) != -1)
759     switch (c)
760       {
761       case 'c':
762         scm->dump_cfg = 1;
763         break;
764
765       case 's':
766         if (sscanf (optarg, "0x%x", &ctrl->cfg.num_test_sockets) != 1)
767           if (sscanf (optarg, "%u", &ctrl->cfg.num_test_sockets) != 1)
768             {
769               fprintf (stderr, "CLIENT: ERROR: Invalid value for "
770                        "option -%c!\n", c);
771               print_usage_and_exit ();
772             }
773         if (!ctrl->cfg.num_test_sockets ||
774             (ctrl->cfg.num_test_sockets > FD_SETSIZE))
775           {
776             fprintf (stderr, "CLIENT: ERROR: Invalid number of "
777                      "sockets (%d) specified for option -%c!\n"
778                      "       Valid range is 1 - %d\n",
779                      ctrl->cfg.num_test_sockets, c, FD_SETSIZE);
780             print_usage_and_exit ();
781           }
782         break;
783
784       case 'w':
785         fprintf (stderr, "CLIENT: Writing test results to files is TBD.\n");
786         break;
787
788       case 'X':
789         post_test = SOCK_TEST_TYPE_EXIT;
790         break;
791
792       case 'E':
793         if (strlen (optarg) > ctrl->txbuf_size)
794           {
795             fprintf (stderr, "CLIENT: ERROR: Option -%c value "
796                      "larger than txbuf size (%d)!\n",
797                      optopt, ctrl->txbuf_size);
798             print_usage_and_exit ();
799           }
800         strcpy (ctrl->txbuf, optarg);
801         ctrl->cfg.test = SOCK_TEST_TYPE_ECHO;
802         break;
803
804       case 'I':
805         if (sscanf (optarg, "0x%x", &ctrl->cfg.num_test_sockets) != 1)
806           if (sscanf (optarg, "%d", &ctrl->cfg.num_test_sockets) != 1)
807             {
808               fprintf (stderr, "CLIENT: ERROR: Invalid value for "
809                        "option -%c!\n", c);
810               print_usage_and_exit ();
811             }
812         if (ctrl->cfg.num_test_sockets > SOCK_TEST_CFG_MAX_TEST_SCKTS)
813           {
814             fprintf (stderr, "CLIENT: ERROR: value greater than max "
815                      "number test sockets (%d)!",
816                      SOCK_TEST_CFG_MAX_TEST_SCKTS);
817             print_usage_and_exit ();
818           }
819         break;
820
821       case 'N':
822         if (sscanf (optarg, "0x%lx", &ctrl->cfg.num_writes) != 1)
823           if (sscanf (optarg, "%ld", &ctrl->cfg.num_writes) != 1)
824             {
825               fprintf (stderr, "CLIENT: ERROR: Invalid value for "
826                        "option -%c!\n", c);
827               print_usage_and_exit ();
828             }
829         ctrl->cfg.total_bytes = ctrl->cfg.num_writes * ctrl->cfg.txbuf_size;
830         break;
831
832       case 'R':
833         if (sscanf (optarg, "0x%lx", &ctrl->cfg.rxbuf_size) != 1)
834           if (sscanf (optarg, "%ld", &ctrl->cfg.rxbuf_size) != 1)
835             {
836               fprintf (stderr, "CLIENT: ERROR: Invalid value for "
837                        "option -%c!\n", c);
838               print_usage_and_exit ();
839             }
840         if (ctrl->cfg.rxbuf_size >= SOCK_TEST_CFG_BUF_SIZE_MIN)
841           {
842             ctrl->rxbuf_size = ctrl->cfg.rxbuf_size;
843             sock_test_buf_alloc (&ctrl->cfg, 1 /* is_rxbuf */ ,
844                                  (uint8_t **) & ctrl->rxbuf,
845                                  &ctrl->rxbuf_size);
846           }
847         else
848           {
849             fprintf (stderr, "CLIENT: ERROR: rxbuf size (%lu) "
850                      "less than minumum (%u)\n",
851                      ctrl->cfg.rxbuf_size, SOCK_TEST_CFG_BUF_SIZE_MIN);
852             print_usage_and_exit ();
853           }
854
855         break;
856
857       case 'T':
858         if (sscanf (optarg, "0x%lx", &ctrl->cfg.txbuf_size) != 1)
859           if (sscanf (optarg, "%ld", &ctrl->cfg.txbuf_size) != 1)
860             {
861               fprintf (stderr, "CLIENT: ERROR: Invalid value "
862                        "for option -%c!\n", c);
863               print_usage_and_exit ();
864             }
865         if (ctrl->cfg.txbuf_size >= SOCK_TEST_CFG_BUF_SIZE_MIN)
866           {
867             ctrl->txbuf_size = ctrl->cfg.txbuf_size;
868             sock_test_buf_alloc (&ctrl->cfg, 0 /* is_rxbuf */ ,
869                                  (uint8_t **) & ctrl->txbuf,
870                                  &ctrl->txbuf_size);
871             ctrl->cfg.total_bytes =
872               ctrl->cfg.num_writes * ctrl->cfg.txbuf_size;
873           }
874         else
875           {
876             fprintf (stderr, "CLIENT: ERROR: txbuf size (%lu) "
877                      "less than minumum (%u)!\n",
878                      ctrl->cfg.txbuf_size, SOCK_TEST_CFG_BUF_SIZE_MIN);
879             print_usage_and_exit ();
880           }
881         break;
882
883       case 'U':
884         ctrl->cfg.test = SOCK_TEST_TYPE_UNI;
885         break;
886
887       case 'B':
888         ctrl->cfg.test = SOCK_TEST_TYPE_BI;
889         break;
890
891       case 'V':
892         ctrl->cfg.verbose = 1;
893         break;
894
895       case '6':
896         ctrl->cfg.address_ip6 = 1;
897         break;
898
899       case 'D':
900         ctrl->cfg.transport_udp = 1;
901         break;
902
903       case '?':
904         switch (optopt)
905           {
906           case 'E':
907           case 'I':
908           case 'N':
909           case 'R':
910           case 'T':
911           case 'w':
912             fprintf (stderr, "CLIENT: ERROR: Option -%c "
913                      "requires an argument.\n", optopt);
914             break;
915
916           default:
917             if (isprint (optopt))
918               fprintf (stderr, "CLIENT: ERROR: Unknown "
919                        "option `-%c'.\n", optopt);
920             else
921               fprintf (stderr, "CLIENT: ERROR: Unknown "
922                        "option character `\\x%x'.\n", optopt);
923           }
924         /* fall thru */
925       case 'h':
926       default:
927         print_usage_and_exit ();
928       }
929
930   if (argc < (optind + 2))
931     {
932       fprintf (stderr, "CLIENT: ERROR: Insufficient number of arguments!\n");
933       print_usage_and_exit ();
934     }
935
936   ctrl->fd = vppcom_app_create ("vcl_test_client");
937   if (ctrl->fd < 0)
938     {
939       errno = -ctrl->fd;
940       ctrl->fd = -1;
941     }
942   else
943     {
944       ctrl->fd = vppcom_session_create (ctrl->cfg.transport_udp ?
945                                         VPPCOM_PROTO_UDP :
946                                         VPPCOM_PROTO_TCP,
947                                         0 /* is_nonblocking */ );
948       if (ctrl->fd < 0)
949         {
950           errno = -ctrl->fd;
951           ctrl->fd = -1;
952         }
953     }
954
955   if (ctrl->fd < 0)
956     {
957       errno_val = errno;
958       perror ("ERROR in main()");
959       fprintf (stderr, "CLIENT: ERROR: socket "
960                "failed (errno = %d)!\n", errno_val);
961       return ctrl->fd;
962     }
963
964   memset (&scm->server_addr, 0, sizeof (scm->server_addr));
965   if (ctrl->cfg.address_ip6)
966     {
967       struct sockaddr_in6 *server_addr =
968         (struct sockaddr_in6 *) &scm->server_addr;
969       scm->server_addr_size = sizeof (*server_addr);
970       server_addr->sin6_family = AF_INET6;
971       inet_pton (AF_INET6, argv[optind++], &(server_addr->sin6_addr));
972       server_addr->sin6_port = htons (atoi (argv[optind]));
973     }
974   else
975     {
976       struct sockaddr_in *server_addr =
977         (struct sockaddr_in *) &scm->server_addr;
978       scm->server_addr_size = sizeof (*server_addr);
979       server_addr->sin_family = AF_INET;
980       inet_pton (AF_INET, argv[optind++], &(server_addr->sin_addr));
981       server_addr->sin_port = htons (atoi (argv[optind]));
982     }
983
984   if (ctrl->cfg.address_ip6)
985     {
986       struct sockaddr_in6 *server_addr =
987         (struct sockaddr_in6 *) &scm->server_addr;
988       scm->server_endpt.is_ip4 = 0;
989       scm->server_endpt.ip = (uint8_t *) & server_addr->sin6_addr;
990       scm->server_endpt.port = (uint16_t) server_addr->sin6_port;
991     }
992   else
993     {
994       struct sockaddr_in *server_addr =
995         (struct sockaddr_in *) &scm->server_addr;
996       scm->server_endpt.is_ip4 = 1;
997       scm->server_endpt.ip = (uint8_t *) & server_addr->sin_addr;
998       scm->server_endpt.port = (uint16_t) server_addr->sin_port;
999     }
1000
1001   do
1002     {
1003       printf ("\nCLIENT: Connecting to server...\n");
1004
1005       rv = vppcom_session_connect (ctrl->fd, &scm->server_endpt);
1006       if (rv)
1007         {
1008           errno = -rv;
1009           rv = -1;
1010         }
1011       if (rv < 0)
1012         {
1013           errno_val = errno;
1014           perror ("ERROR in main()");
1015           fprintf (stderr, "CLIENT: ERROR: connect failed (errno = %d)!\n",
1016                    errno_val);
1017           return -1;
1018         }
1019
1020       sock_test_cfg_sync (ctrl);
1021       printf ("CLIENT (fd %d): Control socket connected.\n", ctrl->fd);
1022     }
1023   while (rv < 0);
1024
1025   sock_test_connect_test_sockets (ctrl->cfg.num_test_sockets);
1026
1027   while (ctrl->cfg.test != SOCK_TEST_TYPE_EXIT)
1028     {
1029       if (scm->dump_cfg)
1030         {
1031           sock_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
1032           scm->dump_cfg = 0;
1033         }
1034
1035       switch (ctrl->cfg.test)
1036         {
1037         case SOCK_TEST_TYPE_ECHO:
1038           echo_test_client ();
1039           break;
1040
1041         case SOCK_TEST_TYPE_UNI:
1042         case SOCK_TEST_TYPE_BI:
1043           stream_test_client (ctrl->cfg.test);
1044           break;
1045
1046         case SOCK_TEST_TYPE_EXIT:
1047           continue;
1048
1049         case SOCK_TEST_TYPE_NONE:
1050         default:
1051           break;
1052         }
1053       switch (post_test)
1054         {
1055         case SOCK_TEST_TYPE_EXIT:
1056           switch (ctrl->cfg.test)
1057             {
1058             case SOCK_TEST_TYPE_EXIT:
1059             case SOCK_TEST_TYPE_UNI:
1060             case SOCK_TEST_TYPE_BI:
1061             case SOCK_TEST_TYPE_ECHO:
1062               ctrl->cfg.test = SOCK_TEST_TYPE_EXIT;
1063               continue;
1064
1065             case SOCK_TEST_TYPE_NONE:
1066             default:
1067               break;
1068             }
1069           break;
1070
1071         case SOCK_TEST_TYPE_NONE:
1072         case SOCK_TEST_TYPE_ECHO:
1073         case SOCK_TEST_TYPE_UNI:
1074         case SOCK_TEST_TYPE_BI:
1075         default:
1076           break;
1077         }
1078
1079       memset (ctrl->txbuf, 0, ctrl->txbuf_size);
1080       memset (ctrl->rxbuf, 0, ctrl->rxbuf_size);
1081
1082       printf ("\nCLIENT: Type some characters and hit <return>\n"
1083               "('" SOCK_TEST_TOKEN_HELP "' for help): ");
1084
1085       if (fgets (ctrl->txbuf, ctrl->txbuf_size, stdin) != NULL)
1086         {
1087           if (strlen (ctrl->txbuf) == 1)
1088             {
1089               printf ("\nCLIENT: Nothing to send!  Please try again...\n");
1090               continue;
1091             }
1092           ctrl->txbuf[strlen (ctrl->txbuf) - 1] = 0;    // chomp the newline.
1093
1094           /* Parse input for keywords */
1095           ctrl->cfg.test = parse_input ();
1096         }
1097     }
1098
1099   exit_client ();
1100   vppcom_session_close (ctrl->fd);
1101   vppcom_app_destroy ();
1102   return 0;
1103 }
1104
1105 /*
1106  * fd.io coding-style-patch-verification: ON
1107  *
1108  * Local Variables:
1109  * eval: (c-set-style "gnu")
1110  * End:
1111  */