blob: 7b56d497153b1b941a1ba12d5ac47eb7e9ff944f [file] [log] [blame]
Dave Wallace543852a2017-08-03 02:11:34 -04001/*
Dave Wallacee4d5a652018-06-24 21:21:21 -04002 * Copyright (c) 2017-2018 Cisco and/or its affiliates.
Dave Wallace543852a2017-08-03 02:11:34 -04003 * 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
Dave Wallacee4d5a652018-06-24 21:21:21 -040016#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 <sys/stat.h>
25#include <fcntl.h>
26#include <vcl/vcl_test.h>
27#include <sys/epoll.h>
Dave Barach6a5adc32018-07-04 10:56:23 -040028#include <vppinfra/mem.h>
Dave Wallace543852a2017-08-03 02:11:34 -040029
Dave Wallacee4d5a652018-06-24 21:21:21 -040030typedef struct
31{
32 uint8_t is_alloc;
33 int fd;
34 uint8_t *buf;
35 uint32_t buf_size;
36 sock_test_cfg_t cfg;
37 sock_test_stats_t stats;
38 vppcom_endpt_t endpt;
39 uint8_t ip[16];
40} sock_server_conn_t;
41
42typedef struct
43{
44 uint32_t port;
45 uint32_t address_ip6;
46 uint32_t transport_udp;
47} sock_server_cfg_t;
48
49#define SOCK_SERVER_MAX_TEST_CONN 10
50#define SOCK_SERVER_MAX_EPOLL_EVENTS 10
51typedef 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 size_t num_conn;
59 size_t conn_pool_size;
60 sock_server_conn_t *conn_pool;
61 int nfds;
62 fd_set rd_fdset;
63 fd_set wr_fdset;
64 struct timeval timeout;
65} sock_server_main_t;
66
67sock_server_main_t sock_server_main;
68
69static inline void
70conn_pool_expand (size_t expand_size)
71{
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;
75 int i;
76
77 conn_pool = realloc (ssm->conn_pool, new_size * sizeof (*ssm->conn_pool));
78 if (conn_pool)
79 {
80 for (i = ssm->conn_pool_size; i < new_size; i++)
81 {
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;
88 }
89
90 ssm->conn_pool = conn_pool;
91 ssm->conn_pool_size = new_size;
92 }
93 else
94 {
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);
99 }
100}
101
102static inline sock_server_conn_t *
103conn_pool_alloc (void)
104{
105 sock_server_main_t *ssm = &sock_server_main;
106 int i;
107
108 for (i = 0; i < ssm->conn_pool_size; i++)
109 {
110 if (!ssm->conn_pool[i].is_alloc)
111 {
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]);
115 }
116 }
117
118 return 0;
119}
120
121static inline void
122conn_pool_free (sock_server_conn_t * conn)
123{
124 conn->fd = 0;
125 conn->is_alloc = 0;
126}
127
128static inline void
129sync_config_and_reply (sock_server_conn_t * conn, sock_test_cfg_t * rx_cfg)
130{
131 conn->cfg = *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;
135
136 if (conn->cfg.verbose)
137 {
138 printf ("\nSERVER (fd %d): Replying to cfg message!\n", conn->fd);
139 sock_test_cfg_dump (&conn->cfg, 0 /* is_client */ );
140 }
141 (void) vcl_test_write (conn->fd, (uint8_t *) & conn->cfg,
142 sizeof (conn->cfg), NULL, conn->cfg.verbose);
143}
144
145static void
146stream_test_server_start_stop (sock_server_conn_t * conn,
147 sock_test_cfg_t * rx_cfg)
148{
149 sock_server_main_t *ssm = &sock_server_main;
150 int client_fd = conn->fd;
151 sock_test_t test = rx_cfg->test;
152
153 if (rx_cfg->ctrl_handle == conn->fd)
154 {
155 int i;
156 clock_gettime (CLOCK_REALTIME, &conn->stats.stop);
157
158 for (i = 0; i < ssm->conn_pool_size; i++)
159 {
160 sock_server_conn_t *tc = &ssm->conn_pool[i];
161
162 if (tc->cfg.ctrl_handle == conn->fd)
163 {
164 sock_test_stats_accumulate (&conn->stats, &tc->stats);
165
166 if (conn->cfg.verbose)
167 {
168 static char buf[64];
169
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
173 /* show tx */ ,
174 conn->cfg.verbose);
175 }
176 }
177 }
178
179 sock_test_stats_dump ("SERVER RESULTS", &conn->stats, 1 /* show_rx */ ,
180 (test == SOCK_TEST_TYPE_BI) /* show_tx */ ,
181 conn->cfg.verbose);
182 sock_test_cfg_dump (&conn->cfg, 0 /* is_client */ );
183 if (conn->cfg.verbose)
184 {
185 printf (" sock server main\n"
186 SOCK_TEST_SEPARATOR_STRING
187 " buf: %p\n"
188 " buf size: %u (0x%08x)\n"
189 SOCK_TEST_SEPARATOR_STRING,
190 conn->buf, conn->buf_size, conn->buf_size);
191 }
192
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");
197 }
198 else
199 {
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");
204
205 rx_cfg->ctrl_handle = (rx_cfg->ctrl_handle == ~0) ? conn->fd :
206 rx_cfg->ctrl_handle;
207
208 sync_config_and_reply (conn, rx_cfg);
209
210 /* read the 1st chunk, record start time */
211 memset (&conn->stats, 0, sizeof (conn->stats));
212 clock_gettime (CLOCK_REALTIME, &conn->stats.start);
213 }
214}
215
216
217static inline void
218stream_test_server (sock_server_conn_t * conn, int rx_bytes)
219{
220 int client_fd = conn->fd;
221 sock_test_t test = conn->cfg.test;
222
223 if (test == SOCK_TEST_TYPE_BI)
224 (void) vcl_test_write (client_fd, conn->buf, rx_bytes, &conn->stats,
225 conn->cfg.verbose);
226
227 if (conn->stats.rx_bytes >= conn->cfg.total_bytes)
228 {
229 clock_gettime (CLOCK_REALTIME, &conn->stats.stop);
230 }
231}
232
233static inline void
234new_client (void)
235{
236 sock_server_main_t *ssm = &sock_server_main;
237 int client_fd;
238 sock_server_conn_t *conn;
239
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);
242
243 conn = conn_pool_alloc ();
244 if (!conn)
245 {
246 fprintf (stderr, "\nSERVER: ERROR: No free connections!\n");
247 return;
248 }
249
250 client_fd = vppcom_session_accept (ssm->listen_fd, &conn->endpt, 0);
251 if (client_fd < 0)
252 {
253 int errno_val;
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);
258 return;
259 }
260
261 printf ("SERVER: Got a connection -- fd = %d (0x%08x)!\n",
262 client_fd, client_fd);
263
264 conn->fd = client_fd;
265
266 {
267 struct epoll_event ev;
268 int rv;
269
270 ev.events = EPOLLIN;
271 ev.data.u64 = conn - ssm->conn_pool;
272 rv = vppcom_epoll_ctl (ssm->epfd, EPOLL_CTL_ADD, client_fd, &ev);
273 if (rv < 0)
274 {
275 int errno_val;
276 errno_val = errno = -rv;
277 perror ("ERROR in new_client()");
278 fprintf (stderr, "SERVER: ERROR: epoll_ctl failed (errno = %d)!\n",
279 errno_val);
280 }
281 else
282 ssm->nfds++;
283 }
284}
285
286void
287print_usage_and_exit (void)
288{
289 fprintf (stderr,
290 "sock_test_server [OPTIONS] <port>\n"
291 " OPTIONS\n"
292 " -h Print this message and exit.\n"
293 " -6 Use IPv6\n"
294 " -u Use UDP transport layer\n");
295 exit (1);
296}
297
298int
299main (int argc, char **argv)
300{
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;
306 uint32_t xtra = 0;
307 uint64_t xtra_bytes = 0;
308 struct sockaddr_storage servaddr;
309 int errno_val;
310 int c, v, i;
311 uint16_t port = SOCK_TEST_SERVER_PORT;
312 vppcom_endpt_t endpt;
313
Dave Barach6a5adc32018-07-04 10:56:23 -0400314 clib_mem_init_thread_safe (0, 64 << 20);
315
Dave Wallacee4d5a652018-06-24 21:21:21 -0400316 opterr = 0;
317 while ((c = getopt (argc, argv, "6D")) != -1)
318 switch (c)
319 {
320 case '6':
321 ssm->cfg.address_ip6 = 1;
322 break;
323
324 case 'D':
325 ssm->cfg.transport_udp = 1;
326 break;
327
328 case '?':
329 switch (optopt)
330 {
331 default:
332 if (isprint (optopt))
333 fprintf (stderr, "SERVER: ERROR: Unknown "
334 "option `-%c'.\n", optopt);
335 else
336 fprintf (stderr, "SERVER: ERROR: Unknown "
337 "option character `\\x%x'.\n", optopt);
338 }
339 /* fall thru */
340 case 'h':
341 default:
342 print_usage_and_exit ();
343 }
344
345 if (argc < (optind + 1))
346 {
347 fprintf (stderr, "SERVER: ERROR: Insufficient number of arguments!\n");
348 print_usage_and_exit ();
349 }
350
351 if (sscanf (argv[optind], "%d", &v) == 1)
352 port = (uint16_t) v;
353 else
354 {
355 fprintf (stderr, "SERVER: ERROR: Invalid port (%s)!\n", argv[optind]);
356 print_usage_and_exit ();
357 }
358
359 conn_pool_expand (SOCK_SERVER_MAX_TEST_CONN + 1);
360
361 rv = vppcom_app_create ("vcl_test_server");
362 if (rv)
363 {
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);
368 return -1;
369 }
370 else
371 {
372 ssm->listen_fd = vppcom_session_create (ssm->cfg.transport_udp ?
373 VPPCOM_PROTO_UDP :
374 VPPCOM_PROTO_TCP,
375 0 /* is_nonblocking */ );
376 }
377 if (ssm->listen_fd < 0)
378 {
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);
383 return -1;
384 }
385
386 memset (&servaddr, 0, sizeof (servaddr));
387
388 if (ssm->cfg.address_ip6)
389 {
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);
394 }
395 else
396 {
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);
401 }
402
403 if (ssm->cfg.address_ip6)
404 {
405 struct sockaddr_in6 *server_addr = (struct sockaddr_in6 *) &servaddr;
406 endpt.is_ip4 = 0;
407 endpt.ip = (uint8_t *) & server_addr->sin6_addr;
408 endpt.port = (uint16_t) server_addr->sin6_port;
409 }
410 else
411 {
412 struct sockaddr_in *server_addr = (struct sockaddr_in *) &servaddr;
413 endpt.is_ip4 = 1;
414 endpt.ip = (uint8_t *) & server_addr->sin_addr;
415 endpt.port = (uint16_t) server_addr->sin_port;
416 }
417
418 rv = vppcom_session_bind (ssm->listen_fd, &endpt);
419 if (rv < 0)
420 {
421 errno_val = errno = -rv;
422 perror ("ERROR in main()");
423 fprintf (stderr, "SERVER: ERROR: bind failed (errno = %d)!\n",
424 errno_val);
425 return -1;
426 }
427
Florin Coras460dce62018-07-27 05:45:06 -0700428 if (!ssm->cfg.transport_udp)
Dave Wallacee4d5a652018-06-24 21:21:21 -0400429 {
Florin Coras460dce62018-07-27 05:45:06 -0700430 rv = vppcom_session_listen (ssm->listen_fd, 10);
431 if (rv < 0)
432 {
433 errno_val = errno = -rv;
434 perror ("ERROR in main()");
435 fprintf (stderr, "SERVER: ERROR: listen failed "
436 "(errno = %d)!\n", errno_val);
437 return -1;
438 }
Dave Wallacee4d5a652018-06-24 21:21:21 -0400439 }
440
441 ssm->epfd = vppcom_epoll_create ();
442 if (ssm->epfd < 0)
443 {
444 errno_val = errno = -ssm->epfd;
445 perror ("ERROR in main()");
446 fprintf (stderr, "SERVER: ERROR: epoll_create failed (errno = %d)!\n",
447 errno_val);
448 return -1;
449 }
450
451 ssm->listen_ev.events = EPOLLIN;
452 ssm->listen_ev.data.u32 = ~0;
453 rv = vppcom_epoll_ctl (ssm->epfd, EPOLL_CTL_ADD, ssm->listen_fd,
454 &ssm->listen_ev);
455 if (rv < 0)
456 {
457 errno_val = errno = -rv;
458 perror ("ERROR in main()");
459 fprintf (stderr, "SERVER: ERROR: epoll_ctl failed "
460 "(errno = %d)!\n", errno_val);
461 return -1;
462 }
463 printf ("\nSERVER: Waiting for a client to connect on port %d...\n", port);
464
465 while (1)
466 {
467 int num_ev;
468 num_ev = vppcom_epoll_wait (ssm->epfd, ssm->wait_events,
Florin Coras54693d22018-07-17 10:46:29 -0700469 SOCK_SERVER_MAX_EPOLL_EVENTS, 60000.0);
Dave Wallacee4d5a652018-06-24 21:21:21 -0400470 if (num_ev < 0)
471 {
472 errno = -num_ev;
473 perror ("epoll_wait()");
474 fprintf (stderr, "\nSERVER: ERROR: epoll_wait() "
475 "failed -- aborting!\n");
476 main_rv = -1;
477 goto done;
478 }
479 else if (num_ev == 0)
480 {
481 fprintf (stderr, "\nSERVER: epoll_wait() timeout!\n");
482 continue;
483 }
484 for (i = 0; i < num_ev; i++)
485 {
486 conn = &ssm->conn_pool[ssm->wait_events[i].data.u32];
487 if (ssm->wait_events[i].events & (EPOLLHUP | EPOLLRDHUP))
488 {
489 vppcom_session_close (conn->fd);
490 continue;
491 }
492 if (ssm->wait_events[i].data.u32 == ~0)
493 {
494 new_client ();
495 continue;
496 }
497 client_fd = conn->fd;
498
499 if (EPOLLIN & ssm->wait_events[i].events)
500 {
501 rx_bytes = vcl_test_read (client_fd, conn->buf,
502 conn->buf_size, &conn->stats);
503 if (rx_bytes > 0)
504 {
505 rx_cfg = (sock_test_cfg_t *) conn->buf;
506 if (rx_cfg->magic == SOCK_TEST_CFG_CTRL_MAGIC)
507 {
508 if (rx_cfg->verbose)
509 {
510 printf ("SERVER (fd %d): Received a cfg message!\n",
511 client_fd);
512 sock_test_cfg_dump (rx_cfg, 0 /* is_client */ );
513 }
514
515 if (rx_bytes != sizeof (*rx_cfg))
516 {
517 printf ("SERVER (fd %d): Invalid cfg message "
518 "size (%d)!\n Should be %lu bytes.\n",
519 client_fd, rx_bytes, sizeof (*rx_cfg));
520 conn->cfg.rxbuf_size = 0;
521 conn->cfg.num_writes = 0;
522 if (conn->cfg.verbose)
523 {
524 printf ("SERVER (fd %d): Replying to "
525 "cfg message!\n", client_fd);
526 sock_test_cfg_dump (rx_cfg, 0 /* is_client */ );
527 }
528 vcl_test_write (client_fd, (uint8_t *) & conn->cfg,
529 sizeof (conn->cfg), NULL,
530 conn->cfg.verbose);
531 continue;
532 }
533
534 switch (rx_cfg->test)
535 {
536 case SOCK_TEST_TYPE_NONE:
537 case SOCK_TEST_TYPE_ECHO:
538 sync_config_and_reply (conn, rx_cfg);
539 break;
540
541 case SOCK_TEST_TYPE_BI:
542 case SOCK_TEST_TYPE_UNI:
543 stream_test_server_start_stop (conn, rx_cfg);
544 break;
545
546 case SOCK_TEST_TYPE_EXIT:
547 printf ("SERVER: Have a great day, "
548 "connection %d!\n", client_fd);
549 vppcom_session_close (client_fd);
550 conn_pool_free (conn);
551 printf ("SERVER: Closed client fd %d\n", client_fd);
552 ssm->nfds--;
553 if (!ssm->nfds)
554 {
555 printf ("SERVER: All client connections "
556 "closed.\n\nSERVER: "
557 "May the force be with you!\n\n");
558 goto done;
559 }
560 break;
561
562 default:
563 fprintf (stderr,
564 "SERVER: ERROR: Unknown test type!\n");
565 sock_test_cfg_dump (rx_cfg, 0 /* is_client */ );
566 break;
567 }
568 continue;
569 }
570
571 else if ((conn->cfg.test == SOCK_TEST_TYPE_UNI) ||
572 (conn->cfg.test == SOCK_TEST_TYPE_BI))
573 {
574 stream_test_server (conn, rx_bytes);
575 continue;
576 }
577
578 else if (isascii (conn->buf[0]))
579 {
580 /* If it looks vaguely like a string,
581 * make sure it's terminated.
582 */
583 ((char *) conn->buf)[rx_bytes <
584 conn->buf_size ? rx_bytes :
585 conn->buf_size - 1] = 0;
586 printf ("SERVER (fd %d): RX (%d bytes) - '%s'\n",
587 conn->fd, rx_bytes, conn->buf);
588 }
589 }
590 else // rx_bytes < 0
591 {
592 if (errno == ECONNRESET)
593 {
594 printf ("\nSERVER: Connection reset by remote peer.\n"
595 " Y'all have a great day now!\n\n");
596 break;
597 }
598 else
599 continue;
600 }
601
602 if (isascii (conn->buf[0]))
603 {
604 /* If it looks vaguely like a string,
605 * make sure it's terminated
606 */
607 ((char *) conn->buf)[rx_bytes <
608 conn->buf_size ? rx_bytes :
609 conn->buf_size - 1] = 0;
610 if (xtra)
611 fprintf (stderr, "SERVER: ERROR: "
612 "FIFO not drained in previous test!\n"
613 " extra chunks %u (0x%x)\n"
614 " extra bytes %lu (0x%lx)\n",
615 xtra, xtra, xtra_bytes, xtra_bytes);
616
617 xtra = 0;
618 xtra_bytes = 0;
619
620 if (conn->cfg.verbose)
621 printf ("SERVER (fd %d): Echoing back\n", client_fd);
622
623 nbytes = strlen ((const char *) conn->buf) + 1;
624
625 tx_bytes = vcl_test_write (client_fd, conn->buf,
626 nbytes, &conn->stats,
627 conn->cfg.verbose);
628 if (tx_bytes >= 0)
629 printf ("SERVER (fd %d): TX (%d bytes) - '%s'\n",
630 conn->fd, tx_bytes, conn->buf);
631 }
Dave Wallacee4d5a652018-06-24 21:21:21 -0400632 else // Extraneous read data from non-echo tests???
633 {
634 xtra++;
635 xtra_bytes += rx_bytes;
636 }
637 }
638 }
639 }
640
641done:
642 vppcom_session_close (ssm->listen_fd);
643 vppcom_app_destroy ();
644
645 if (ssm->conn_pool)
646 free (ssm->conn_pool);
647
648 return main_rv;
649}
Dave Wallace543852a2017-08-03 02:11:34 -0400650
651/*
652 * fd.io coding-style-patch-verification: ON
653 *
654 * Local Variables:
655 * eval: (c-set-style "gnu")
656 * End:
657 */