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