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