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