FD.io VPP  v20.05-21-gb1500e9ff
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, 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
226 {
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
289 {
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;
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
330 {
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
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
412 {
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];
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 {
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);
519  if (wrk->wrk_index)
520  vt_atomic_add (&vcm->active_workers, -1);
521  return 0;
522 }
523 
524 static void
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"
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"
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
564 {
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
588 {
589  vcl_test_session_t *ctrl = &vcm->ctrl_session;
590  vcl_test_cfg_t *cfg = &ctrl->cfg;
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
646 {
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
667 {
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
687 {
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
708 {
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
728 {
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
739 {
741  vcl_test_session_t *ctrl = &vcm->ctrl_session;
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)))
759 
760  else if (!strncmp (VCL_TEST_TOKEN_TXBUF_SIZE, ctrl->txbuf,
761  strlen (VCL_TEST_TOKEN_TXBUF_SIZE)))
763 
764  else if (!strncmp (VCL_TEST_TOKEN_NUM_TEST_SESS, ctrl->txbuf,
767 
768  else if (!strncmp (VCL_TEST_TOKEN_NUM_WRITES, ctrl->txbuf,
769  strlen (VCL_TEST_TOKEN_NUM_WRITES)))
771 
772  else if (!strncmp (VCL_TEST_TOKEN_RXBUF_SIZE, ctrl->txbuf,
773  strlen (VCL_TEST_TOKEN_RXBUF_SIZE)))
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
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);
838  }
839  if (!ctrl->cfg.num_test_sessions ||
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,
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);
856  }
857  if (!ctrl->cfg.num_test_sessions_perq ||
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,
865  }
866  break;
867 
868  case 'w':
869  if (sscanf (optarg, "%d", &v) != 1)
870  {
871  vtwrn ("Invalid value for option -%c!", c);
873  }
874  if (v > 1)
875  vcm->n_workers = v;
876  break;
877 
878  case 'X':
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);
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);
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);
909  }
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)",
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);
932  }
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)!",
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:
1003  }
1004 
1005  if (argc < (optind + 2))
1006  {
1007  vtwrn ("Insufficient number of arguments!");
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) /
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
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
1062 {
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 {
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);
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,
1105  vppcom_session_tls_add_key (ctrl->fd, vcl_test_key_rsa,
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 
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  */
#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()
uint32_t num_test_qsessions
Definition: vcl_test.h:90
vcl_test_session_t ctrl_session
static void dump_help(void)
Definition: vcl_test.h:522
vcl_test_session_t quic_session
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
u32 flags
Definition: vhost_user.h:248
svmdb_client_t * c
sll srl srl sll sra u16x4 i
Definition: vector_sse42.h:317
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