2 * Copyright (c) 2017-2018 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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <sys/types.h>
19 #include <sys/socket.h>
26 #include <vcl/vcl_test.h>
27 #include <sys/epoll.h>
28 #include <vppinfra/mem.h>
37 sock_test_stats_t stats;
46 uint32_t transport_udp;
49 #define SOCK_SERVER_MAX_TEST_CONN 10
50 #define SOCK_SERVER_MAX_EPOLL_EVENTS 10
54 sock_server_cfg_t cfg;
56 struct epoll_event listen_ev;
57 struct epoll_event wait_events[SOCK_SERVER_MAX_EPOLL_EVENTS];
59 size_t conn_pool_size;
60 sock_server_conn_t *conn_pool;
64 struct timeval timeout;
67 sock_server_main_t sock_server_main;
70 conn_pool_expand (size_t expand_size)
72 sock_server_main_t *ssm = &sock_server_main;
73 sock_server_conn_t *conn_pool;
74 size_t new_size = ssm->conn_pool_size + expand_size;
77 conn_pool = realloc (ssm->conn_pool, new_size * sizeof (*ssm->conn_pool));
80 for (i = ssm->conn_pool_size; i < new_size; i++)
82 sock_server_conn_t *conn = &conn_pool[i];
83 memset (conn, 0, sizeof (*conn));
84 sock_test_cfg_init (&conn->cfg);
85 sock_test_buf_alloc (&conn->cfg, 1 /* is_rxbuf */ ,
86 &conn->buf, &conn->buf_size);
87 conn->cfg.txbuf_size = conn->cfg.rxbuf_size;
90 ssm->conn_pool = conn_pool;
91 ssm->conn_pool_size = new_size;
95 int errno_val = errno;
96 perror ("ERROR in conn_pool_expand()");
97 fprintf (stderr, "SERVER: ERROR: Memory allocation "
98 "failed (errno = %d)!\n", errno_val);
102 static inline sock_server_conn_t *
103 conn_pool_alloc (void)
105 sock_server_main_t *ssm = &sock_server_main;
108 for (i = 0; i < ssm->conn_pool_size; i++)
110 if (!ssm->conn_pool[i].is_alloc)
112 ssm->conn_pool[i].endpt.ip = ssm->conn_pool[i].ip;
113 ssm->conn_pool[i].is_alloc = 1;
114 return (&ssm->conn_pool[i]);
122 conn_pool_free (sock_server_conn_t * conn)
129 sync_config_and_reply (sock_server_conn_t * conn, sock_test_cfg_t * rx_cfg)
132 sock_test_buf_alloc (&conn->cfg, 1 /* is_rxbuf */ ,
133 &conn->buf, &conn->buf_size);
134 conn->cfg.txbuf_size = conn->cfg.rxbuf_size;
136 if (conn->cfg.verbose)
138 printf ("\nSERVER (fd %d): Replying to cfg message!\n", conn->fd);
139 sock_test_cfg_dump (&conn->cfg, 0 /* is_client */ );
141 (void) vcl_test_write (conn->fd, (uint8_t *) & conn->cfg,
142 sizeof (conn->cfg), NULL, conn->cfg.verbose);
146 stream_test_server_start_stop (sock_server_conn_t * conn,
147 sock_test_cfg_t * rx_cfg)
149 sock_server_main_t *ssm = &sock_server_main;
150 int client_fd = conn->fd;
151 sock_test_t test = rx_cfg->test;
153 if (rx_cfg->ctrl_handle == conn->fd)
156 clock_gettime (CLOCK_REALTIME, &conn->stats.stop);
158 for (i = 0; i < ssm->conn_pool_size; i++)
160 sock_server_conn_t *tc = &ssm->conn_pool[i];
162 if (tc->cfg.ctrl_handle == conn->fd)
164 sock_test_stats_accumulate (&conn->stats, &tc->stats);
166 if (conn->cfg.verbose)
170 sprintf (buf, "SERVER (fd %d) RESULTS", tc->fd);
171 sock_test_stats_dump (buf, &tc->stats, 1 /* show_rx */ ,
172 test == SOCK_TEST_TYPE_BI
179 sock_test_stats_dump ("SERVER RESULTS", &conn->stats, 1 /* show_rx */ ,
180 (test == SOCK_TEST_TYPE_BI) /* show_tx */ ,
182 sock_test_cfg_dump (&conn->cfg, 0 /* is_client */ );
183 if (conn->cfg.verbose)
185 printf (" sock server main\n"
186 SOCK_TEST_SEPARATOR_STRING
188 " buf size: %u (0x%08x)\n"
189 SOCK_TEST_SEPARATOR_STRING,
190 conn->buf, conn->buf_size, conn->buf_size);
193 sync_config_and_reply (conn, rx_cfg);
194 printf ("\nSERVER (fd %d): %s-directional Stream Test Complete!\n"
195 SOCK_TEST_BANNER_STRING "\n", conn->fd,
196 test == SOCK_TEST_TYPE_BI ? "Bi" : "Uni");
200 printf ("\n" SOCK_TEST_BANNER_STRING
201 "SERVER (fd %d): %s-directional Stream Test!\n"
202 " Sending client the test cfg to start streaming data...\n",
203 client_fd, test == SOCK_TEST_TYPE_BI ? "Bi" : "Uni");
205 rx_cfg->ctrl_handle = (rx_cfg->ctrl_handle == ~0) ? conn->fd :
208 sync_config_and_reply (conn, rx_cfg);
210 /* read the 1st chunk, record start time */
211 memset (&conn->stats, 0, sizeof (conn->stats));
212 clock_gettime (CLOCK_REALTIME, &conn->stats.start);
218 stream_test_server (sock_server_conn_t * conn, int rx_bytes)
220 int client_fd = conn->fd;
221 sock_test_t test = conn->cfg.test;
223 if (test == SOCK_TEST_TYPE_BI)
224 (void) vcl_test_write (client_fd, conn->buf, rx_bytes, &conn->stats,
227 if (conn->stats.rx_bytes >= conn->cfg.total_bytes)
229 clock_gettime (CLOCK_REALTIME, &conn->stats.stop);
236 sock_server_main_t *ssm = &sock_server_main;
238 sock_server_conn_t *conn;
240 if (ssm->conn_pool_size < (ssm->num_conn + SOCK_SERVER_MAX_TEST_CONN + 1))
241 conn_pool_expand (SOCK_SERVER_MAX_TEST_CONN + 1);
243 conn = conn_pool_alloc ();
246 fprintf (stderr, "\nSERVER: ERROR: No free connections!\n");
250 client_fd = vppcom_session_accept (ssm->listen_fd, &conn->endpt, 0);
254 errno_val = errno = -client_fd;
255 perror ("ERROR in new_client()");
256 fprintf (stderr, "SERVER: ERROR: accept failed "
257 "(errno = %d)!\n", errno_val);
261 printf ("SERVER: Got a connection -- fd = %d (0x%08x)!\n",
262 client_fd, client_fd);
264 conn->fd = client_fd;
267 struct epoll_event ev;
271 ev.data.u64 = conn - ssm->conn_pool;
272 rv = vppcom_epoll_ctl (ssm->epfd, EPOLL_CTL_ADD, client_fd, &ev);
276 errno_val = errno = -rv;
277 perror ("ERROR in new_client()");
278 fprintf (stderr, "SERVER: ERROR: epoll_ctl failed (errno = %d)!\n",
287 print_usage_and_exit (void)
290 "sock_test_server [OPTIONS] <port>\n"
292 " -h Print this message and exit.\n"
294 " -u Use UDP transport layer\n");
299 main (int argc, char **argv)
301 sock_server_main_t *ssm = &sock_server_main;
302 int client_fd, rv, main_rv = 0;
303 int tx_bytes, rx_bytes, nbytes;
304 sock_server_conn_t *conn;
305 sock_test_cfg_t *rx_cfg;
307 uint64_t xtra_bytes = 0;
308 struct sockaddr_storage servaddr;
311 uint16_t port = SOCK_TEST_SERVER_PORT;
312 vppcom_endpt_t endpt;
314 clib_mem_init_thread_safe (0, 64 << 20);
317 while ((c = getopt (argc, argv, "6D")) != -1)
321 ssm->cfg.address_ip6 = 1;
325 ssm->cfg.transport_udp = 1;
332 if (isprint (optopt))
333 fprintf (stderr, "SERVER: ERROR: Unknown "
334 "option `-%c'.\n", optopt);
336 fprintf (stderr, "SERVER: ERROR: Unknown "
337 "option character `\\x%x'.\n", optopt);
342 print_usage_and_exit ();
345 if (argc < (optind + 1))
347 fprintf (stderr, "SERVER: ERROR: Insufficient number of arguments!\n");
348 print_usage_and_exit ();
351 if (sscanf (argv[optind], "%d", &v) == 1)
355 fprintf (stderr, "SERVER: ERROR: Invalid port (%s)!\n", argv[optind]);
356 print_usage_and_exit ();
359 conn_pool_expand (SOCK_SERVER_MAX_TEST_CONN + 1);
361 rv = vppcom_app_create ("vcl_test_server");
364 errno_val = errno = -rv;
365 perror ("ERROR in main()");
366 fprintf (stderr, "SERVER: ERROR: vppcom_app_create() failed "
367 "(errno = %d)!\n", errno_val);
372 ssm->listen_fd = vppcom_session_create (ssm->cfg.transport_udp ?
375 0 /* is_nonblocking */ );
377 if (ssm->listen_fd < 0)
379 errno_val = errno = -ssm->listen_fd;
380 perror ("ERROR in main()");
381 fprintf (stderr, "SERVER: ERROR: vppcom_session_create() failed "
382 "(errno = %d)!\n", errno_val);
386 memset (&servaddr, 0, sizeof (servaddr));
388 if (ssm->cfg.address_ip6)
390 struct sockaddr_in6 *server_addr = (struct sockaddr_in6 *) &servaddr;
391 server_addr->sin6_family = AF_INET6;
392 server_addr->sin6_addr = in6addr_any;
393 server_addr->sin6_port = htons (port);
397 struct sockaddr_in *server_addr = (struct sockaddr_in *) &servaddr;
398 server_addr->sin_family = AF_INET;
399 server_addr->sin_addr.s_addr = htonl (INADDR_ANY);
400 server_addr->sin_port = htons (port);
403 if (ssm->cfg.address_ip6)
405 struct sockaddr_in6 *server_addr = (struct sockaddr_in6 *) &servaddr;
407 endpt.ip = (uint8_t *) & server_addr->sin6_addr;
408 endpt.port = (uint16_t) server_addr->sin6_port;
412 struct sockaddr_in *server_addr = (struct sockaddr_in *) &servaddr;
414 endpt.ip = (uint8_t *) & server_addr->sin_addr;
415 endpt.port = (uint16_t) server_addr->sin_port;
418 rv = vppcom_session_bind (ssm->listen_fd, &endpt);
421 errno_val = errno = -rv;
422 perror ("ERROR in main()");
423 fprintf (stderr, "SERVER: ERROR: bind failed (errno = %d)!\n",
428 rv = vppcom_session_listen (ssm->listen_fd, 10);
431 errno_val = errno = -rv;
432 perror ("ERROR in main()");
433 fprintf (stderr, "SERVER: ERROR: listen failed "
434 "(errno = %d)!\n", errno_val);
438 ssm->epfd = vppcom_epoll_create ();
441 errno_val = errno = -ssm->epfd;
442 perror ("ERROR in main()");
443 fprintf (stderr, "SERVER: ERROR: epoll_create failed (errno = %d)!\n",
448 ssm->listen_ev.events = EPOLLIN;
449 ssm->listen_ev.data.u32 = ~0;
450 rv = vppcom_epoll_ctl (ssm->epfd, EPOLL_CTL_ADD, ssm->listen_fd,
454 errno_val = errno = -rv;
455 perror ("ERROR in main()");
456 fprintf (stderr, "SERVER: ERROR: epoll_ctl failed "
457 "(errno = %d)!\n", errno_val);
460 printf ("\nSERVER: Waiting for a client to connect on port %d...\n", port);
465 num_ev = vppcom_epoll_wait (ssm->epfd, ssm->wait_events,
466 SOCK_SERVER_MAX_EPOLL_EVENTS, 60.0);
470 perror ("epoll_wait()");
471 fprintf (stderr, "\nSERVER: ERROR: epoll_wait() "
472 "failed -- aborting!\n");
476 else if (num_ev == 0)
478 fprintf (stderr, "\nSERVER: epoll_wait() timeout!\n");
481 for (i = 0; i < num_ev; i++)
483 conn = &ssm->conn_pool[ssm->wait_events[i].data.u32];
484 if (ssm->wait_events[i].events & (EPOLLHUP | EPOLLRDHUP))
486 vppcom_session_close (conn->fd);
489 if (ssm->wait_events[i].data.u32 == ~0)
494 client_fd = conn->fd;
496 if (EPOLLIN & ssm->wait_events[i].events)
498 rx_bytes = vcl_test_read (client_fd, conn->buf,
499 conn->buf_size, &conn->stats);
502 rx_cfg = (sock_test_cfg_t *) conn->buf;
503 if (rx_cfg->magic == SOCK_TEST_CFG_CTRL_MAGIC)
507 printf ("SERVER (fd %d): Received a cfg message!\n",
509 sock_test_cfg_dump (rx_cfg, 0 /* is_client */ );
512 if (rx_bytes != sizeof (*rx_cfg))
514 printf ("SERVER (fd %d): Invalid cfg message "
515 "size (%d)!\n Should be %lu bytes.\n",
516 client_fd, rx_bytes, sizeof (*rx_cfg));
517 conn->cfg.rxbuf_size = 0;
518 conn->cfg.num_writes = 0;
519 if (conn->cfg.verbose)
521 printf ("SERVER (fd %d): Replying to "
522 "cfg message!\n", client_fd);
523 sock_test_cfg_dump (rx_cfg, 0 /* is_client */ );
525 vcl_test_write (client_fd, (uint8_t *) & conn->cfg,
526 sizeof (conn->cfg), NULL,
531 switch (rx_cfg->test)
533 case SOCK_TEST_TYPE_NONE:
534 case SOCK_TEST_TYPE_ECHO:
535 sync_config_and_reply (conn, rx_cfg);
538 case SOCK_TEST_TYPE_BI:
539 case SOCK_TEST_TYPE_UNI:
540 stream_test_server_start_stop (conn, rx_cfg);
543 case SOCK_TEST_TYPE_EXIT:
544 printf ("SERVER: Have a great day, "
545 "connection %d!\n", client_fd);
546 vppcom_session_close (client_fd);
547 conn_pool_free (conn);
548 printf ("SERVER: Closed client fd %d\n", client_fd);
552 printf ("SERVER: All client connections "
553 "closed.\n\nSERVER: "
554 "May the force be with you!\n\n");
561 "SERVER: ERROR: Unknown test type!\n");
562 sock_test_cfg_dump (rx_cfg, 0 /* is_client */ );
568 else if ((conn->cfg.test == SOCK_TEST_TYPE_UNI) ||
569 (conn->cfg.test == SOCK_TEST_TYPE_BI))
571 stream_test_server (conn, rx_bytes);
575 else if (isascii (conn->buf[0]))
577 /* If it looks vaguely like a string,
578 * make sure it's terminated.
580 ((char *) conn->buf)[rx_bytes <
581 conn->buf_size ? rx_bytes :
582 conn->buf_size - 1] = 0;
583 printf ("SERVER (fd %d): RX (%d bytes) - '%s'\n",
584 conn->fd, rx_bytes, conn->buf);
589 if (errno == ECONNRESET)
591 printf ("\nSERVER: Connection reset by remote peer.\n"
592 " Y'all have a great day now!\n\n");
599 if (isascii (conn->buf[0]))
601 /* If it looks vaguely like a string,
602 * make sure it's terminated
604 ((char *) conn->buf)[rx_bytes <
605 conn->buf_size ? rx_bytes :
606 conn->buf_size - 1] = 0;
608 fprintf (stderr, "SERVER: ERROR: "
609 "FIFO not drained in previous test!\n"
610 " extra chunks %u (0x%x)\n"
611 " extra bytes %lu (0x%lx)\n",
612 xtra, xtra, xtra_bytes, xtra_bytes);
617 if (conn->cfg.verbose)
618 printf ("SERVER (fd %d): Echoing back\n", client_fd);
620 nbytes = strlen ((const char *) conn->buf) + 1;
622 tx_bytes = vcl_test_write (client_fd, conn->buf,
623 nbytes, &conn->stats,
626 printf ("SERVER (fd %d): TX (%d bytes) - '%s'\n",
627 conn->fd, tx_bytes, conn->buf);
630 else // Extraneous read data from non-echo tests???
633 xtra_bytes += rx_bytes;
640 vppcom_session_close (ssm->listen_fd);
641 vppcom_app_destroy ();
644 free (ssm->conn_pool);
650 * fd.io coding-style-patch-verification: ON
653 * eval: (c-set-style "gnu")