hsa: cleanup sock server test
[vpp.git] / src / plugins / hs_apps / vcl / sock_test_server.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 <sys/types.h>
19 #include <sys/socket.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <time.h>
23 #include <ctype.h>
24 #include <hs_apps/vcl/sock_test.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <sys/ioctl.h>
28 #include <sys/epoll.h>
29 #include <sys/un.h>
30
31 #define SOCK_SERVER_MAX_TEST_CONN  10
32 #define SOCK_SERVER_MAX_EPOLL_EVENTS 10
33
34 typedef struct
35 {
36   uint8_t is_alloc;
37   int fd;
38   uint8_t *buf;
39   uint32_t buf_size;
40   vcl_test_cfg_t cfg;
41   vcl_test_stats_t stats;
42 } sock_server_conn_t;
43
44 typedef struct
45 {
46   uint32_t port;
47   uint32_t address_ip6;
48   uint32_t transport_udp;
49 } sock_server_cfg_t;
50
51 typedef struct
52 {
53   int listen_fd;
54   sock_server_cfg_t cfg;
55   int epfd;
56   struct epoll_event listen_ev;
57   struct epoll_event wait_events[SOCK_SERVER_MAX_EPOLL_EVENTS];
58   int af_unix_listen_fd;
59   int af_unix_fd;
60   struct epoll_event af_unix_listen_ev;
61   struct sockaddr_un serveraddr;
62   uint32_t af_unix_xacts;
63   size_t num_conn;
64   size_t conn_pool_size;
65   sock_server_conn_t *conn_pool;
66   int nfds;
67   fd_set rd_fdset;
68   fd_set wr_fdset;
69   struct timeval timeout;
70 } sock_server_main_t;
71
72 sock_server_main_t sock_server_main;
73
74 static inline void
75 conn_pool_expand (size_t expand_size)
76 {
77   sock_server_main_t *ssm = &sock_server_main;
78   sock_server_conn_t *conn_pool;
79   size_t new_size = ssm->conn_pool_size + expand_size;
80   int i;
81
82   conn_pool = realloc (ssm->conn_pool, new_size * sizeof (*ssm->conn_pool));
83   if (!conn_pool)
84     stfail ("conn_pool_expand()", -errno);
85
86   for (i = ssm->conn_pool_size; i < new_size; i++)
87     {
88       sock_server_conn_t *conn = &conn_pool[i];
89       memset (conn, 0, sizeof (*conn));
90       vcl_test_cfg_init (&conn->cfg);
91       vcl_test_buf_alloc (&conn->cfg, 1 /* is_rxbuf */ , &conn->buf,
92                           &conn->buf_size);
93       conn->cfg.txbuf_size = conn->cfg.rxbuf_size;
94     }
95
96   ssm->conn_pool = conn_pool;
97   ssm->conn_pool_size = new_size;
98 }
99
100 static inline sock_server_conn_t *
101 conn_pool_alloc (void)
102 {
103   sock_server_main_t *ssm = &sock_server_main;
104   int i;
105
106   for (i = 0; i < ssm->conn_pool_size; i++)
107     {
108       if (!ssm->conn_pool[i].is_alloc)
109         {
110           ssm->conn_pool[i].is_alloc = 1;
111           return (&ssm->conn_pool[i]);
112         }
113     }
114
115   return 0;
116 }
117
118 static inline void
119 conn_pool_free (sock_server_conn_t * conn)
120 {
121   conn->fd = 0;
122   conn->is_alloc = 0;
123 }
124
125 static inline void
126 sync_config_and_reply (sock_server_conn_t * conn, vcl_test_cfg_t * rx_cfg)
127 {
128   conn->cfg = *rx_cfg;
129   vcl_test_buf_alloc (&conn->cfg, 1 /* is_rxbuf */ ,
130                       &conn->buf, &conn->buf_size);
131   conn->cfg.txbuf_size = conn->cfg.rxbuf_size;
132
133   if (conn->cfg.verbose)
134     {
135       stinf ("(fd %d): Replying to cfg message!\n", conn->fd);
136       vcl_test_cfg_dump (&conn->cfg, 0 /* is_client */ );
137     }
138   (void) sock_test_write (conn->fd, (uint8_t *) & conn->cfg,
139                           sizeof (conn->cfg), NULL, conn->cfg.verbose);
140 }
141
142 static void
143 stream_test_server_start_stop (sock_server_conn_t * conn,
144                                vcl_test_cfg_t * rx_cfg)
145 {
146   sock_server_main_t *ssm = &sock_server_main;
147   int client_fd = conn->fd;
148   vcl_test_t test = rx_cfg->test;
149
150   if (rx_cfg->ctrl_handle == conn->fd)
151     {
152       int i;
153       clock_gettime (CLOCK_REALTIME, &conn->stats.stop);
154
155       for (i = 0; i < ssm->conn_pool_size; i++)
156         {
157           sock_server_conn_t *tc = &ssm->conn_pool[i];
158
159           if (tc->cfg.ctrl_handle == conn->fd)
160             {
161               vcl_test_stats_accumulate (&conn->stats, &tc->stats);
162
163               if (conn->cfg.verbose)
164                 {
165                   static char buf[64];
166
167                   snprintf (buf, sizeof (buf), "SERVER (fd %d) RESULTS",
168                             tc->fd);
169                   vcl_test_stats_dump (buf, &tc->stats, 1 /* show_rx */ ,
170                                        test == VCL_TEST_TYPE_BI
171                                        /* show tx */ ,
172                                        conn->cfg.verbose);
173                 }
174             }
175         }
176
177       vcl_test_stats_dump ("SERVER RESULTS", &conn->stats, 1 /* show_rx */ ,
178                            (test == VCL_TEST_TYPE_BI) /* show_tx */ ,
179                            conn->cfg.verbose);
180       vcl_test_cfg_dump (&conn->cfg, 0 /* is_client */ );
181       if (conn->cfg.verbose)
182         {
183           stinf ("  sock server main\n"
184                  VCL_TEST_SEPARATOR_STRING
185                  "       buf:  %p\n"
186                  "  buf size:  %u (0x%08x)\n"
187                  VCL_TEST_SEPARATOR_STRING,
188                  conn->buf, conn->buf_size, conn->buf_size);
189         }
190
191       sync_config_and_reply (conn, rx_cfg);
192       stinf ("SERVER (fd %d): %s-directional Stream Test Complete!\n"
193              SOCK_TEST_BANNER_STRING "\n", conn->fd,
194              test == VCL_TEST_TYPE_BI ? "Bi" : "Uni");
195     }
196   else
197     {
198       stinf (SOCK_TEST_BANNER_STRING
199              "SERVER (fd %d): %s-directional Stream Test!\n"
200              "  Sending client the test cfg to start streaming data...\n",
201              client_fd, test == VCL_TEST_TYPE_BI ? "Bi" : "Uni");
202
203       rx_cfg->ctrl_handle = (rx_cfg->ctrl_handle == ~0) ? conn->fd :
204         rx_cfg->ctrl_handle;
205
206       sync_config_and_reply (conn, rx_cfg);
207
208       /* read the 1st chunk, record start time */
209       memset (&conn->stats, 0, sizeof (conn->stats));
210       clock_gettime (CLOCK_REALTIME, &conn->stats.start);
211     }
212 }
213
214
215 static inline void
216 stream_test_server (sock_server_conn_t * conn, int rx_bytes)
217 {
218   int client_fd = conn->fd;
219   vcl_test_t test = conn->cfg.test;
220
221   if (test == VCL_TEST_TYPE_BI)
222     (void) sock_test_write (client_fd, conn->buf, rx_bytes, &conn->stats,
223                             conn->cfg.verbose);
224
225   if (conn->stats.rx_bytes >= conn->cfg.total_bytes)
226     {
227       clock_gettime (CLOCK_REALTIME, &conn->stats.stop);
228     }
229 }
230
231 static inline void
232 af_unix_echo (void)
233 {
234   sock_server_main_t *ssm = &sock_server_main;
235   int af_unix_client_fd, rv;
236   uint8_t buffer[256];
237   size_t nbytes = strlen (SOCK_TEST_MIXED_EPOLL_DATA) + 1;
238
239   af_unix_client_fd = accept (ssm->af_unix_listen_fd,
240                               (struct sockaddr *) NULL, NULL);
241   if (af_unix_client_fd < 0)
242     stfail ("af_unix_echo accept()", af_unix_client_fd);
243
244   stinf ("Got an AF_UNIX connection -- fd = %d (0x%08x)!",
245          af_unix_client_fd, af_unix_client_fd);
246
247   memset (buffer, 0, sizeof (buffer));
248
249   rv = read (af_unix_client_fd, buffer, nbytes);
250   if (rv < 0)
251     stfail ("af_unix_echo read()", rv);
252
253   /* Make the buffer is NULL-terminated. */
254   buffer[sizeof (buffer) - 1] = 0;
255   stinf ("(AF_UNIX): RX (%d bytes) - '%s'", rv, buffer);
256
257   if (!strncmp (SOCK_TEST_MIXED_EPOLL_DATA, (const char *) buffer, nbytes))
258     {
259       rv = write (af_unix_client_fd, buffer, nbytes);
260       if (rv < 0)
261         stfail ("af_unix_echo write()", rv);
262       stinf ("(AF_UNIX): TX (%d bytes) - '%s'\n", rv, buffer);
263       ssm->af_unix_xacts++;
264     }
265   close (af_unix_client_fd);
266 }
267
268 static inline void
269 new_client (void)
270 {
271   sock_server_main_t *ssm = &sock_server_main;
272   int client_fd;
273   sock_server_conn_t *conn;
274
275   if (ssm->conn_pool_size < (ssm->num_conn + SOCK_SERVER_MAX_TEST_CONN + 1))
276     conn_pool_expand (SOCK_SERVER_MAX_TEST_CONN + 1);
277
278   conn = conn_pool_alloc ();
279   if (!conn)
280     stfail ("No free connections!", 1);
281
282   client_fd = accept (ssm->listen_fd, (struct sockaddr *) NULL, NULL);
283   if (client_fd < 0)
284     stfail ("new_client accept()", client_fd);
285
286   stinf ("Got a connection -- fd = %d (0x%08x)!\n", client_fd, client_fd);
287
288   conn->fd = client_fd;
289
290   struct epoll_event ev;
291   int rv;
292
293   ev.events = EPOLLIN;
294   ev.data.u64 = conn - ssm->conn_pool;
295   rv = epoll_ctl (ssm->epfd, EPOLL_CTL_ADD, client_fd, &ev);
296
297   if (rv < 0)
298     stfail ("new_client epoll_ctl()", rv);
299
300   ssm->nfds++;
301 }
302
303 static int
304 socket_server_echo_af_unix_init (sock_server_main_t * ssm)
305 {
306   int rv;
307
308   if (ssm->af_unix_listen_fd > 0)
309     return 0;
310
311   unlink ((const char *) SOCK_TEST_AF_UNIX_FILENAME);
312   ssm->af_unix_listen_fd = socket (AF_UNIX, SOCK_STREAM, 0);
313   if (ssm->af_unix_listen_fd < 0)
314     stfail ("echo_af_unix_init socket()", ssm->af_unix_listen_fd);
315
316   memset (&ssm->serveraddr, 0, sizeof (ssm->serveraddr));
317   ssm->serveraddr.sun_family = AF_UNIX;
318   strncpy (ssm->serveraddr.sun_path, SOCK_TEST_AF_UNIX_FILENAME,
319            sizeof (ssm->serveraddr.sun_path));
320
321   rv = bind (ssm->af_unix_listen_fd, (struct sockaddr *) &ssm->serveraddr,
322              SUN_LEN (&ssm->serveraddr));
323   if (rv < 0)
324     stfail ("echo_af_unix_init bind()", rv);
325
326   rv = listen (ssm->af_unix_listen_fd, 10);
327   if (rv < 0)
328     stfail ("echo_af_unix_init listen()", rv);
329
330   ssm->af_unix_listen_ev.events = EPOLLIN;
331   ssm->af_unix_listen_ev.data.u32 = SOCK_TEST_AF_UNIX_ACCEPT_DATA;
332   rv = epoll_ctl (ssm->epfd, EPOLL_CTL_ADD, ssm->af_unix_listen_fd,
333                   &ssm->af_unix_listen_ev);
334   if (rv < 0)
335     stfail ("echo_af_unix_init epoll_ctl()", rv);
336
337   return 0;
338 }
339
340 void
341 print_usage_and_exit (void)
342 {
343   fprintf (stderr,
344            "sock_test_server [OPTIONS] <port>\n"
345            "  OPTIONS\n"
346            "  -h               Print this message and exit.\n"
347            "  -6               Use IPv6\n"
348            "  -u               Use UDP transport layer\n");
349   exit (1);
350 }
351
352
353 static void
354 sts_server_echo (sock_server_conn_t * conn, int rx_bytes)
355 {
356   int tx_bytes, nbytes, pos;
357
358   /* If it looks vaguely like a string make sure it's terminated */
359   pos = rx_bytes < conn->buf_size ? rx_bytes : conn->buf_size - 1;
360   ((char *) conn->buf)[pos] = 0;
361
362   if (conn->cfg.verbose)
363     stinf ("(fd %d): Echoing back\n", conn->fd);
364
365   nbytes = strlen ((const char *) conn->buf) + 1;
366
367   tx_bytes = sock_test_write (conn->fd, conn->buf, nbytes, &conn->stats,
368                               conn->cfg.verbose);
369   if (tx_bytes >= 0)
370     stinf ("(fd %d): TX (%d bytes) - '%s'\n", conn->fd, tx_bytes, conn->buf);
371 }
372
373 static int
374 sts_handle_cfg (vcl_test_cfg_t * rx_cfg, sock_server_conn_t * conn,
375                 int rx_bytes)
376 {
377   sock_server_main_t *ssm = &sock_server_main;
378
379   if (rx_cfg->verbose)
380     {
381       stinf ("(fd %d): Received a cfg message!\n", conn->fd);
382       vcl_test_cfg_dump (rx_cfg, 0 /* is_client */ );
383     }
384
385   if (rx_bytes != sizeof (*rx_cfg))
386     {
387       stinf ("(fd %d): Invalid cfg message size (%d) expected %lu!", conn->fd,
388              rx_bytes, sizeof (*rx_cfg));
389       conn->cfg.rxbuf_size = 0;
390       conn->cfg.num_writes = 0;
391       if (conn->cfg.verbose)
392         {
393           stinf ("(fd %d): Replying to cfg message!\n", conn->fd);
394           vcl_test_cfg_dump (rx_cfg, 0 /* is_client */ );
395         }
396       sock_test_write (conn->fd, (uint8_t *) & conn->cfg, sizeof (conn->cfg),
397                        NULL, conn->cfg.verbose);
398       return -1;
399     }
400
401   switch (rx_cfg->test)
402     {
403     case VCL_TEST_TYPE_NONE:
404       sync_config_and_reply (conn, rx_cfg);
405       break;
406
407     case VCL_TEST_TYPE_ECHO:
408       if (socket_server_echo_af_unix_init (ssm))
409         goto done;
410
411       sync_config_and_reply (conn, rx_cfg);
412       break;
413
414     case VCL_TEST_TYPE_BI:
415     case VCL_TEST_TYPE_UNI:
416       stream_test_server_start_stop (conn, rx_cfg);
417       break;
418
419     case VCL_TEST_TYPE_EXIT:
420       stinf ("Have a great day connection %d!", conn->fd);
421       close (conn->fd);
422       conn_pool_free (conn);
423       stinf ("Closed client fd %d", conn->fd);
424       ssm->nfds--;
425       break;
426
427     default:
428       stinf ("ERROR: Unknown test type!\n");
429       vcl_test_cfg_dump (rx_cfg, 0 /* is_client */ );
430       break;
431     }
432
433 done:
434   return 0;
435 }
436
437 static int
438 sts_conn_expect_config (sock_server_conn_t * conn)
439 {
440   if (conn->cfg.test == VCL_TEST_TYPE_ECHO)
441     return 1;
442
443   return (conn->stats.rx_bytes < 128
444           || conn->stats.rx_bytes > conn->cfg.total_bytes);
445 }
446
447 int
448 main (int argc, char **argv)
449 {
450   int client_fd, rv, main_rv = 0, rx_bytes, c, v, i;
451   sock_server_main_t *ssm = &sock_server_main;
452   sock_server_conn_t *conn;
453   vcl_test_cfg_t *rx_cfg;
454   struct sockaddr_storage servaddr;
455   uint16_t port = VCL_TEST_SERVER_PORT;
456   uint32_t servaddr_size;
457
458   opterr = 0;
459   while ((c = getopt (argc, argv, "6D")) != -1)
460     switch (c)
461       {
462       case '6':
463         ssm->cfg.address_ip6 = 1;
464         break;
465
466       case 'D':
467         ssm->cfg.transport_udp = 1;
468         break;
469
470       case '?':
471         switch (optopt)
472           {
473           default:
474             if (isprint (optopt))
475               stinf ("ERROR: Unknown option `-%c'", optopt);
476             else
477               stinf ("ERROR: Unknown option character `\\x%x'.\n", optopt);
478           }
479         /* fall thru */
480       case 'h':
481       default:
482         print_usage_and_exit ();
483       }
484
485   if (argc < (optind + 1))
486     {
487       stinf ("ERROR: Insufficient number of arguments!\n");
488       print_usage_and_exit ();
489     }
490
491   if (sscanf (argv[optind], "%d", &v) == 1)
492     port = (uint16_t) v;
493   else
494     {
495       stinf ("ERROR: Invalid port (%s)!\n", argv[optind]);
496       print_usage_and_exit ();
497     }
498
499   conn_pool_expand (SOCK_SERVER_MAX_TEST_CONN + 1);
500
501   ssm->listen_fd = socket (ssm->cfg.address_ip6 ? AF_INET6 : AF_INET,
502                            ssm->cfg.transport_udp ? SOCK_DGRAM : SOCK_STREAM,
503                            0);
504
505   if (ssm->listen_fd < 0)
506     stfail ("main listen()", ssm->listen_fd);
507
508   memset (&servaddr, 0, sizeof (servaddr));
509
510   if (ssm->cfg.address_ip6)
511     {
512       struct sockaddr_in6 *server_addr = (struct sockaddr_in6 *) &servaddr;
513       servaddr_size = sizeof (*server_addr);
514       server_addr->sin6_family = AF_INET6;
515       server_addr->sin6_addr = in6addr_any;
516       server_addr->sin6_port = htons (port);
517     }
518   else
519     {
520       struct sockaddr_in *server_addr = (struct sockaddr_in *) &servaddr;
521       servaddr_size = sizeof (*server_addr);
522       server_addr->sin_family = AF_INET;
523       server_addr->sin_addr.s_addr = htonl (INADDR_ANY);
524       server_addr->sin_port = htons (port);
525     }
526
527   rv = bind (ssm->listen_fd, (struct sockaddr *) &servaddr, servaddr_size);
528   if (rv < 0)
529     stfail ("main bind()", rv);
530
531   rv = fcntl (ssm->listen_fd, F_SETFL, O_NONBLOCK);
532   if (rv < 0)
533     stfail ("main fcntl()", rv);
534
535   rv = listen (ssm->listen_fd, 10);
536   if (rv < 0)
537     stfail ("main listen()", rv);
538
539   ssm->epfd = epoll_create (1);
540   if (ssm->epfd < 0)
541     stfail ("main epoll_create()", ssm->epfd);
542
543   ssm->listen_ev.events = EPOLLIN;
544   ssm->listen_ev.data.u32 = ~0;
545
546   rv = epoll_ctl (ssm->epfd, EPOLL_CTL_ADD, ssm->listen_fd, &ssm->listen_ev);
547   if (rv < 0)
548     stfail ("main epoll_ctl()", rv);
549
550   stinf ("Waiting for a client to connect on port %d...\n", port);
551
552   while (1)
553     {
554       int num_ev;
555       num_ev = epoll_wait (ssm->epfd, ssm->wait_events,
556                            SOCK_SERVER_MAX_EPOLL_EVENTS, 60000);
557       if (num_ev < 0)
558         stfail ("main epoll_wait()", num_ev);
559
560       if (num_ev == 0)
561         {
562           stinf ("epoll_wait() timeout!\n");
563           continue;
564         }
565       for (i = 0; i < num_ev; i++)
566         {
567           conn = &ssm->conn_pool[ssm->wait_events[i].data.u32];
568           if (ssm->wait_events[i].events & (EPOLLHUP | EPOLLRDHUP))
569             {
570               close (conn->fd);
571               continue;
572             }
573           if (ssm->wait_events[i].data.u32 == ~0)
574             {
575               new_client ();
576               continue;
577             }
578           else if (ssm->wait_events[i].data.u32 ==
579                    SOCK_TEST_AF_UNIX_ACCEPT_DATA)
580             {
581               af_unix_echo ();
582               continue;
583             }
584           client_fd = conn->fd;
585
586           if (EPOLLIN & ssm->wait_events[i].events)
587             {
588             read_again:
589               rx_bytes = sock_test_read (client_fd, conn->buf,
590                                          conn->buf_size, &conn->stats);
591
592               if (rx_bytes <= 0)
593                 {
594                   if (errno == ECONNRESET)
595                     {
596                       stinf ("Connection reset by peer\n");
597                       main_rv = -1;
598                       goto done;
599                     }
600                   else
601                     continue;
602                 }
603
604               if (sts_conn_expect_config (conn))
605                 {
606                   rx_cfg = (vcl_test_cfg_t *) conn->buf;
607                   if (rx_cfg->magic == VCL_TEST_CFG_CTRL_MAGIC)
608                     {
609                       sts_handle_cfg (rx_cfg, conn, rx_bytes);
610                       if (!ssm->nfds)
611                         {
612                           stinf ("All client connections closed.\n\nSERVER: "
613                                  "May the force be with you!\n\n");
614                           goto done;
615                         }
616                       continue;
617                     }
618                 }
619
620               if ((conn->cfg.test == VCL_TEST_TYPE_UNI)
621                   || (conn->cfg.test == VCL_TEST_TYPE_BI))
622                 {
623                   stream_test_server (conn, rx_bytes);
624                   if (ioctl (conn->fd, FIONREAD))
625                     goto read_again;
626                   continue;
627                 }
628               else if (isascii (conn->buf[0]))
629                 {
630                   sts_server_echo (conn, rx_bytes);
631                 }
632               else
633                 {
634                   stwrn ("FIFO not drained! extra bytes %d", rx_bytes);
635                 }
636             }
637         }
638     }
639
640 done:
641   close (ssm->listen_fd);
642   close (ssm->af_unix_listen_fd);
643   unlink ((const char *) SOCK_TEST_AF_UNIX_FILENAME);
644
645   if (ssm->conn_pool)
646     free (ssm->conn_pool);
647
648   return main_rv;
649 }
650
651 /*
652  * fd.io coding-style-patch-verification: ON
653  *
654  * Local Variables:
655  * eval: (c-set-style "gnu")
656  * End:
657  */