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