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