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