tls: move test certificates to separate header file
[vpp.git] / src / tests / vnet / session / tcp_echo.c
1 /*
2  * Copyright (c) 2016 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 <stdio.h>
17 #include <signal.h>
18
19 #include <vnet/session/application_interface.h>
20 #include <svm/svm_fifo_segment.h>
21 #include <vlibmemory/api.h>
22
23 #include <vpp/api/vpe_msg_enum.h>
24
25 #define vl_typedefs             /* define message structures */
26 #include <vpp/api/vpe_all_api_h.h>
27 #undef vl_typedefs
28
29 /* declare message handlers for each api */
30
31 #define vl_endianfun            /* define message structures */
32 #include <vpp/api/vpe_all_api_h.h>
33 #undef vl_endianfun
34
35 /* instantiate all the print functions we know about */
36 #define vl_print(handle, ...)
37 #define vl_printfun
38 #include <vpp/api/vpe_all_api_h.h>
39 #undef vl_printfun
40
41 #define TCP_ECHO_DBG 0
42 #define DBG(_fmt,_args...)                      \
43     if (TCP_ECHO_DBG)                           \
44       clib_warning (_fmt, _args)
45
46 typedef struct
47 {
48   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
49 #define _(type, name) type name;
50   foreach_app_session_field
51 #undef _
52   u64 vpp_session_handle;
53   u64 bytes_sent;
54   u64 bytes_to_send;
55   volatile u64 bytes_received;
56   volatile u64 bytes_to_receive;
57   f64 start;
58 } echo_session_t;
59
60 typedef enum
61 {
62   STATE_START,
63   STATE_ATTACHED,
64   STATE_LISTEN,
65   STATE_READY,
66   STATE_DISCONNECTING,
67   STATE_FAILED,
68   STATE_DETACHED
69 } connection_state_t;
70
71 typedef struct
72 {
73   /* vpe input queue */
74   svm_queue_t *vl_input_queue;
75
76   /* API client handle */
77   u32 my_client_index;
78
79   /* The URI we're playing with */
80   u8 *uri;
81
82   /* Session pool */
83   echo_session_t *sessions;
84
85   /* Hash table for disconnect processing */
86   uword *session_index_by_vpp_handles;
87
88   /* intermediate rx buffer */
89   u8 *rx_buf;
90
91   /* URI for slave's connect */
92   u8 *connect_uri;
93
94   u32 connected_session_index;
95
96   int i_am_master;
97
98   /* drop all packets */
99   int no_return;
100
101   /* Our event queue */
102   svm_msg_q_t *our_event_queue;
103
104   u8 *socket_name;
105
106   pid_t my_pid;
107
108   /* For deadman timers */
109   clib_time_t clib_time;
110
111   /* State of the connection, shared between msg RX thread and main thread */
112   volatile connection_state_t state;
113
114   /* Signal variables */
115   volatile int time_to_stop;
116   volatile int time_to_print_stats;
117
118   u32 configured_segment_size;
119
120   /* VNET_API_ERROR_FOO -> "Foo" hash table */
121   uword *error_string_by_error_number;
122
123   u8 *connect_test_data;
124   pthread_t *client_thread_handles;
125   u32 *thread_args;
126   u32 client_bytes_received;
127   u8 test_return_packets;
128   u64 bytes_to_send;
129   u32 fifo_size;
130
131   u32 n_clients;
132   u64 tx_total;
133   u64 rx_total;
134
135   volatile u32 n_clients_connected;
136   volatile u32 n_active_clients;
137
138
139   /** Flag that decides if socket, instead of svm, api is used to connect to
140    * vpp. If sock api is used, shm binary api is subsequently bootstrapped
141    * and all other messages are exchanged using shm IPC. */
142   u8 use_sock_api;
143
144   svm_fifo_segment_main_t segment_main;
145 } echo_main_t;
146
147 echo_main_t echo_main;
148
149 #if CLIB_DEBUG > 0
150 #define NITER 10000
151 #else
152 #define NITER 4000000
153 #endif
154
155 static u8 *
156 format_api_error (u8 * s, va_list * args)
157 {
158   echo_main_t *em = &echo_main;
159   i32 error = va_arg (*args, u32);
160   uword *p;
161
162   p = hash_get (em->error_string_by_error_number, -error);
163
164   if (p)
165     s = format (s, "%s", p[0]);
166   else
167     s = format (s, "%d", error);
168   return s;
169 }
170
171 static void
172 init_error_string_table (echo_main_t * em)
173 {
174   em->error_string_by_error_number = hash_create (0, sizeof (uword));
175
176 #define _(n,v,s) hash_set (em->error_string_by_error_number, -v, s);
177   foreach_vnet_api_error;
178 #undef _
179
180   hash_set (em->error_string_by_error_number, 99, "Misc");
181 }
182
183 static void handle_mq_event (session_event_t * e);
184
185 static int
186 wait_for_state_change (echo_main_t * em, connection_state_t state)
187 {
188   svm_msg_q_msg_t msg;
189   session_event_t *e;
190   f64 timeout;
191
192 #if CLIB_DEBUG > 0
193 #define TIMEOUT 600.0
194 #else
195 #define TIMEOUT 600.0
196 #endif
197
198   timeout = clib_time_now (&em->clib_time) + TIMEOUT;
199
200   while (clib_time_now (&em->clib_time) < timeout)
201     {
202       if (em->state == state)
203         return 0;
204       if (em->state == STATE_FAILED)
205         return -1;
206       if (em->time_to_stop == 1)
207         return 0;
208       if (!em->our_event_queue || em->state < STATE_ATTACHED)
209         continue;
210
211       if (svm_msg_q_sub (em->our_event_queue, &msg, SVM_Q_NOWAIT, 0))
212         continue;
213       e = svm_msg_q_msg_data (em->our_event_queue, &msg);
214       handle_mq_event (e);
215       svm_msg_q_free_msg (em->our_event_queue, &msg);
216     }
217   clib_warning ("timeout waiting for state %d", state);
218   return -1;
219 }
220
221 void
222 application_send_attach (echo_main_t * em)
223 {
224   vl_api_application_attach_t *bmp;
225   vl_api_application_tls_cert_add_t *cert_mp;
226   vl_api_application_tls_key_add_t *key_mp;
227
228   bmp = vl_msg_api_alloc (sizeof (*bmp));
229   clib_memset (bmp, 0, sizeof (*bmp));
230
231   bmp->_vl_msg_id = ntohs (VL_API_APPLICATION_ATTACH);
232   bmp->client_index = em->my_client_index;
233   bmp->context = ntohl (0xfeedface);
234   bmp->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_ACCEPT_REDIRECT;
235   bmp->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_ADD_SEGMENT;
236   bmp->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_MQ_FOR_CTRL_MSGS;
237   bmp->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = 16;
238   bmp->options[APP_OPTIONS_RX_FIFO_SIZE] = em->fifo_size;
239   bmp->options[APP_OPTIONS_TX_FIFO_SIZE] = em->fifo_size;
240   bmp->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = 128 << 20;
241   bmp->options[APP_OPTIONS_SEGMENT_SIZE] = 256 << 20;
242   bmp->options[APP_OPTIONS_EVT_QUEUE_SIZE] = 256;
243   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & bmp);
244
245   cert_mp = vl_msg_api_alloc (sizeof (*cert_mp) + test_srv_crt_rsa_len);
246   clib_memset (cert_mp, 0, sizeof (*cert_mp));
247   cert_mp->_vl_msg_id = ntohs (VL_API_APPLICATION_TLS_CERT_ADD);
248   cert_mp->client_index = em->my_client_index;
249   cert_mp->context = ntohl (0xfeedface);
250   cert_mp->cert_len = clib_host_to_net_u16 (test_srv_crt_rsa_len);
251   clib_memcpy_fast (cert_mp->cert, test_srv_crt_rsa, test_srv_crt_rsa_len);
252   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & cert_mp);
253
254   key_mp = vl_msg_api_alloc (sizeof (*key_mp) + test_srv_key_rsa_len);
255   clib_memset (key_mp, 0, sizeof (*key_mp) + test_srv_key_rsa_len);
256   key_mp->_vl_msg_id = ntohs (VL_API_APPLICATION_TLS_KEY_ADD);
257   key_mp->client_index = em->my_client_index;
258   key_mp->context = ntohl (0xfeedface);
259   key_mp->key_len = clib_host_to_net_u16 (test_srv_key_rsa_len);
260   clib_memcpy_fast (key_mp->key, test_srv_key_rsa, test_srv_key_rsa_len);
261   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & key_mp);
262 }
263
264 static int
265 application_attach (echo_main_t * em)
266 {
267   application_send_attach (em);
268   if (wait_for_state_change (em, STATE_ATTACHED))
269     {
270       clib_warning ("timeout waiting for STATE_ATTACHED");
271       return -1;
272     }
273   return 0;
274 }
275
276 void
277 application_detach (echo_main_t * em)
278 {
279   vl_api_application_detach_t *bmp;
280   bmp = vl_msg_api_alloc (sizeof (*bmp));
281   clib_memset (bmp, 0, sizeof (*bmp));
282
283   bmp->_vl_msg_id = ntohs (VL_API_APPLICATION_DETACH);
284   bmp->client_index = em->my_client_index;
285   bmp->context = ntohl (0xfeedface);
286   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & bmp);
287
288   DBG ("%s", "Sent detach");
289 }
290
291 static int
292 ssvm_segment_attach (char *name, ssvm_segment_type_t type, int fd)
293 {
294   svm_fifo_segment_create_args_t _a, *a = &_a;
295   svm_fifo_segment_main_t *sm = &echo_main.segment_main;
296   int rv;
297
298   clib_memset (a, 0, sizeof (*a));
299   a->segment_name = (char *) name;
300   a->segment_type = type;
301
302   if (type == SSVM_SEGMENT_MEMFD)
303     a->memfd_fd = fd;
304
305   if ((rv = svm_fifo_segment_attach (sm, a)))
306     {
307       clib_warning ("svm_fifo_segment_attach ('%s') failed", name);
308       return rv;
309     }
310
311   vec_reset_length (a->new_segment_indices);
312   return 0;
313 }
314
315 static void
316 vl_api_application_attach_reply_t_handler (vl_api_application_attach_reply_t *
317                                            mp)
318 {
319   echo_main_t *em = &echo_main;
320   int *fds = 0;
321   u32 n_fds = 0;
322
323   if (mp->retval)
324     {
325       clib_warning ("attach failed: %U", format_api_error,
326                     clib_net_to_host_u32 (mp->retval));
327       goto failed;
328     }
329
330   if (mp->segment_name_length == 0)
331     {
332       clib_warning ("segment_name_length zero");
333       goto failed;
334     }
335
336   ASSERT (mp->app_event_queue_address);
337   em->our_event_queue = uword_to_pointer (mp->app_event_queue_address,
338                                           svm_msg_q_t *);
339
340   if (mp->n_fds)
341     {
342       vec_validate (fds, mp->n_fds);
343       vl_socket_client_recv_fd_msg (fds, mp->n_fds, 5);
344
345       if (mp->fd_flags & SESSION_FD_F_VPP_MQ_SEGMENT)
346         if (ssvm_segment_attach (0, SSVM_SEGMENT_MEMFD, fds[n_fds++]))
347           goto failed;
348
349       if (mp->fd_flags & SESSION_FD_F_MEMFD_SEGMENT)
350         if (ssvm_segment_attach ((char *) mp->segment_name,
351                                  SSVM_SEGMENT_MEMFD, fds[n_fds++]))
352           goto failed;
353
354       if (mp->fd_flags & SESSION_FD_F_MQ_EVENTFD)
355         svm_msg_q_set_consumer_eventfd (em->our_event_queue, fds[n_fds++]);
356
357       vec_free (fds);
358     }
359   else
360     {
361       if (ssvm_segment_attach ((char *) mp->segment_name, SSVM_SEGMENT_SHM,
362                                -1))
363         goto failed;
364     }
365
366   em->state = STATE_ATTACHED;
367   return;
368 failed:
369   em->state = STATE_FAILED;
370   return;
371 }
372
373 static void
374 vl_api_application_detach_reply_t_handler (vl_api_application_detach_reply_t *
375                                            mp)
376 {
377   if (mp->retval)
378     clib_warning ("detach returned with err: %d", mp->retval);
379   echo_main.state = STATE_DETACHED;
380 }
381
382 static void
383 stop_signal (int signum)
384 {
385   echo_main_t *um = &echo_main;
386
387   um->time_to_stop = 1;
388 }
389
390 static void
391 stats_signal (int signum)
392 {
393   echo_main_t *um = &echo_main;
394
395   um->time_to_print_stats = 1;
396 }
397
398 static clib_error_t *
399 setup_signal_handlers (void)
400 {
401   signal (SIGINT, stats_signal);
402   signal (SIGQUIT, stop_signal);
403   signal (SIGTERM, stop_signal);
404
405   return 0;
406 }
407
408 void
409 vlib_cli_output (struct vlib_main_t *vm, char *fmt, ...)
410 {
411   clib_warning ("BUG");
412 }
413
414 int
415 connect_to_vpp (char *name)
416 {
417   echo_main_t *em = &echo_main;
418   api_main_t *am = &api_main;
419
420   if (em->use_sock_api)
421     {
422       if (vl_socket_client_connect ((char *) em->socket_name, name,
423                                     0 /* default rx, tx buffer */ ))
424         {
425           clib_warning ("socket connect failed");
426           return -1;
427         }
428
429       if (vl_socket_client_init_shm (0, 1 /* want_pthread */ ))
430         {
431           clib_warning ("init shm api failed");
432           return -1;
433         }
434     }
435   else
436     {
437       if (vl_client_connect_to_vlib ("/vpe-api", name, 32) < 0)
438         {
439           clib_warning ("shmem connect failed");
440           return -1;
441         }
442     }
443   em->vl_input_queue = am->shmem_hdr->vl_input_queue;
444   em->my_client_index = am->my_client_index;
445   return 0;
446 }
447
448 void
449 disconnect_from_vpp (echo_main_t * em)
450 {
451   if (em->use_sock_api)
452     vl_socket_client_disconnect ();
453   else
454     vl_client_disconnect_from_vlib ();
455 }
456
457 static void
458 vl_api_map_another_segment_t_handler (vl_api_map_another_segment_t * mp)
459 {
460   svm_fifo_segment_main_t *sm = &echo_main.segment_main;
461   svm_fifo_segment_create_args_t _a, *a = &_a;
462   int rv;
463
464   clib_memset (a, 0, sizeof (*a));
465   a->segment_name = (char *) mp->segment_name;
466   a->segment_size = mp->segment_size;
467   /* Attach to the segment vpp created */
468   rv = svm_fifo_segment_attach (sm, a);
469   if (rv)
470     {
471       clib_warning ("svm_fifo_segment_attach ('%s') failed",
472                     mp->segment_name);
473       return;
474     }
475   clib_warning ("Mapped new segment '%s' size %d", mp->segment_name,
476                 mp->segment_size);
477 }
478
479 static void
480 session_print_stats (echo_main_t * em, echo_session_t * session)
481 {
482   f64 deltat;
483   u64 bytes;
484
485   deltat = clib_time_now (&em->clib_time) - session->start;
486   bytes = em->i_am_master ? session->bytes_received : em->bytes_to_send;
487   fformat (stdout, "Finished in %.6f\n", deltat);
488   fformat (stdout, "%.4f Gbit/second\n", (bytes * 8.0) / deltat / 1e9);
489 }
490
491 static void
492 test_recv_bytes (echo_session_t * s, u8 * rx_buf, u32 n_read)
493 {
494   int i;
495   for (i = 0; i < n_read; i++)
496     {
497       if (rx_buf[i] != ((s->bytes_received + i) & 0xff))
498         {
499           clib_warning ("error at byte %lld, 0x%x not 0x%x",
500                         s->bytes_received + i, rx_buf[i],
501                         ((s->bytes_received + i) & 0xff));
502         }
503     }
504 }
505
506 static void
507 recv_data_chunk (echo_main_t * em, echo_session_t * s, u8 * rx_buf)
508 {
509   int n_to_read, n_read;
510
511   n_to_read = svm_fifo_max_dequeue (s->rx_fifo);
512   if (!n_to_read)
513     return;
514
515   do
516     {
517       n_read = app_recv_stream ((app_session_t *) s, rx_buf,
518                                 vec_len (rx_buf));
519
520       if (n_read > 0)
521         {
522           if (em->test_return_packets)
523             test_recv_bytes (s, rx_buf, n_read);
524
525           n_to_read -= n_read;
526
527           s->bytes_received += n_read;
528           s->bytes_to_receive -= n_read;
529         }
530       else
531         break;
532     }
533   while (n_to_read > 0);
534 }
535
536 void
537 client_handle_rx (echo_main_t * em, session_event_t * e, u8 * rx_buf)
538 {
539   echo_session_t *s;
540
541   s = pool_elt_at_index (em->sessions, e->fifo->client_session_index);
542   recv_data_chunk (em, s, rx_buf);
543 }
544
545 static void
546 send_data_chunk (echo_main_t * em, echo_session_t * s)
547 {
548   u64 test_buf_len, bytes_this_chunk, test_buf_offset;
549   u8 *test_data = em->connect_test_data;
550   int n_sent;
551
552   test_buf_len = vec_len (test_data);
553   test_buf_offset = s->bytes_sent % test_buf_len;
554   bytes_this_chunk = clib_min (test_buf_len - test_buf_offset,
555                                s->bytes_to_send);
556
557   n_sent = app_send_stream ((app_session_t *) s, test_data + test_buf_offset,
558                             bytes_this_chunk, 0);
559
560   if (n_sent > 0)
561     {
562       s->bytes_to_send -= n_sent;
563       s->bytes_sent += n_sent;
564     }
565 }
566
567 /*
568  * Rx/Tx polling thread per connection
569  */
570 static void *
571 client_thread_fn (void *arg)
572 {
573   echo_main_t *em = &echo_main;
574   static u8 *rx_buf = 0;
575   u32 session_index = *(u32 *) arg;
576   echo_session_t *s;
577
578   vec_validate (rx_buf, 1 << 20);
579
580   while (!em->time_to_stop && em->state != STATE_READY)
581     ;
582
583   s = pool_elt_at_index (em->sessions, session_index);
584   while (!em->time_to_stop)
585     {
586       send_data_chunk (em, s);
587       recv_data_chunk (em, s, rx_buf);
588       if (!s->bytes_to_send && !s->bytes_to_receive)
589         break;
590     }
591
592   DBG ("session %d done", session_index);
593   em->tx_total += s->bytes_sent;
594   em->rx_total += s->bytes_received;
595   em->n_active_clients--;
596
597   pthread_exit (0);
598 }
599
600 /*
601  * Rx thread that handles all connections.
602  *
603  * Not used.
604  */
605 void *
606 client_rx_thread_fn (void *arg)
607 {
608   session_event_t _e, *e = &_e;
609   echo_main_t *em = &echo_main;
610   static u8 *rx_buf = 0;
611   svm_msg_q_msg_t msg;
612
613   vec_validate (rx_buf, 1 << 20);
614
615   while (!em->time_to_stop && em->state != STATE_READY)
616     ;
617
618   while (!em->time_to_stop)
619     {
620       svm_msg_q_sub (em->our_event_queue, &msg, SVM_Q_WAIT, 0);
621       e = svm_msg_q_msg_data (em->our_event_queue, &msg);
622       switch (e->event_type)
623         {
624         case FIFO_EVENT_APP_RX:
625           client_handle_rx (em, e, rx_buf);
626           break;
627         default:
628           clib_warning ("unknown event type %d", e->event_type);
629           break;
630         }
631       svm_msg_q_free_msg (em->our_event_queue, &msg);
632     }
633   pthread_exit (0);
634 }
635
636 void
637 client_send_connect (echo_main_t * em)
638 {
639   vl_api_connect_uri_t *cmp;
640   cmp = vl_msg_api_alloc (sizeof (*cmp));
641   clib_memset (cmp, 0, sizeof (*cmp));
642
643   cmp->_vl_msg_id = ntohs (VL_API_CONNECT_URI);
644   cmp->client_index = em->my_client_index;
645   cmp->context = ntohl (0xfeedface);
646   memcpy (cmp->uri, em->connect_uri, vec_len (em->connect_uri));
647   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & cmp);
648 }
649
650 void
651 client_send_disconnect (echo_main_t * em, echo_session_t * s)
652 {
653   vl_api_disconnect_session_t *dmp;
654   dmp = vl_msg_api_alloc (sizeof (*dmp));
655   clib_memset (dmp, 0, sizeof (*dmp));
656   dmp->_vl_msg_id = ntohs (VL_API_DISCONNECT_SESSION);
657   dmp->client_index = em->my_client_index;
658   dmp->handle = s->vpp_session_handle;
659   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & dmp);
660 }
661
662 int
663 client_disconnect (echo_main_t * em, echo_session_t * s)
664 {
665   client_send_disconnect (em, s);
666   pool_put (em->sessions, s);
667   clib_memset (s, 0xfe, sizeof (*s));
668   return 0;
669 }
670
671 static void
672 session_bound_handler (session_bound_msg_t * mp)
673 {
674   echo_main_t *em = &echo_main;
675
676   if (mp->retval)
677     {
678       clib_warning ("bind failed: %U", format_api_error,
679                     clib_net_to_host_u32 (mp->retval));
680       em->state = STATE_FAILED;
681       return;
682     }
683
684   clib_warning ("listening on %U:%u", format_ip46_address, mp->lcl_ip,
685                 mp->lcl_is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6, mp->lcl_port);
686   em->state = STATE_READY;
687 }
688
689 static void
690 session_accepted_handler (session_accepted_msg_t * mp)
691 {
692   app_session_evt_t _app_evt, *app_evt = &_app_evt;
693   session_accepted_reply_msg_t *rmp;
694   svm_fifo_t *rx_fifo, *tx_fifo;
695   echo_main_t *em = &echo_main;
696   echo_session_t *session;
697   static f64 start_time;
698   u32 session_index;
699   u8 *ip_str;
700
701   if (start_time == 0.0)
702     start_time = clib_time_now (&em->clib_time);
703
704   ip_str = format (0, "%U", format_ip46_address, &mp->ip, mp->is_ip4);
705   clib_warning ("Accepted session from: %s:%d", ip_str,
706                 clib_net_to_host_u16 (mp->port));
707
708   /* Allocate local session and set it up */
709   pool_get (em->sessions, session);
710   session_index = session - em->sessions;
711
712   rx_fifo = uword_to_pointer (mp->server_rx_fifo, svm_fifo_t *);
713   rx_fifo->client_session_index = session_index;
714   tx_fifo = uword_to_pointer (mp->server_tx_fifo, svm_fifo_t *);
715   tx_fifo->client_session_index = session_index;
716
717   session->rx_fifo = rx_fifo;
718   session->tx_fifo = tx_fifo;
719   session->vpp_evt_q = uword_to_pointer (mp->vpp_event_queue_address,
720                                          svm_msg_q_t *);
721
722   /* Add it to lookup table */
723   hash_set (em->session_index_by_vpp_handles, mp->handle, session_index);
724
725   em->state = STATE_READY;
726
727   /* Stats printing */
728   if (pool_elts (em->sessions) && (pool_elts (em->sessions) % 20000) == 0)
729     {
730       f64 now = clib_time_now (&em->clib_time);
731       fformat (stdout, "%d active sessions in %.2f seconds, %.2f/sec...\n",
732                pool_elts (em->sessions), now - start_time,
733                (f64) pool_elts (em->sessions) / (now - start_time));
734     }
735
736   /*
737    * Send accept reply to vpp
738    */
739   app_alloc_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt,
740                              SESSION_CTRL_EVT_ACCEPTED_REPLY);
741   rmp = (session_accepted_reply_msg_t *) app_evt->evt->data;
742   rmp->handle = mp->handle;
743   rmp->context = mp->context;
744   app_send_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt);
745
746   session->bytes_received = 0;
747   session->start = clib_time_now (&em->clib_time);
748 }
749
750 static void
751 session_connected_handler (session_connected_msg_t * mp)
752 {
753   echo_main_t *em = &echo_main;
754   echo_session_t *session;
755   u32 session_index;
756   svm_fifo_t *rx_fifo, *tx_fifo;
757   int rv;
758
759   if (mp->retval)
760     {
761       clib_warning ("connection failed with code: %U", format_api_error,
762                     clib_net_to_host_u32 (mp->retval));
763       em->state = STATE_FAILED;
764       return;
765     }
766
767   /*
768    * Setup session
769    */
770
771   pool_get (em->sessions, session);
772   clib_memset (session, 0, sizeof (*session));
773   session_index = session - em->sessions;
774
775   rx_fifo = uword_to_pointer (mp->server_rx_fifo, svm_fifo_t *);
776   rx_fifo->client_session_index = session_index;
777   tx_fifo = uword_to_pointer (mp->server_tx_fifo, svm_fifo_t *);
778   tx_fifo->client_session_index = session_index;
779
780   session->rx_fifo = rx_fifo;
781   session->tx_fifo = tx_fifo;
782   session->vpp_session_handle = mp->handle;
783   session->start = clib_time_now (&em->clib_time);
784   session->vpp_evt_q = uword_to_pointer (mp->vpp_event_queue_address,
785                                          svm_msg_q_t *);
786
787   hash_set (em->session_index_by_vpp_handles, mp->handle, session_index);
788
789   /*
790    * Start RX thread
791    */
792   em->thread_args[em->n_clients_connected] = session_index;
793   rv = pthread_create (&em->client_thread_handles[em->n_clients_connected],
794                        NULL /*attr */ , client_thread_fn,
795                        (void *) &em->thread_args[em->n_clients_connected]);
796   if (rv)
797     {
798       clib_warning ("pthread_create returned %d", rv);
799       return;
800     }
801
802   em->n_clients_connected += 1;
803   clib_warning ("session %u (0x%llx) connected with local ip %U port %d",
804                 session_index, mp->handle, format_ip46_address, mp->lcl_ip,
805                 mp->is_ip4, clib_net_to_host_u16 (mp->lcl_port));
806 }
807
808 static void
809 session_disconnected_handler (session_disconnected_msg_t * mp)
810 {
811   app_session_evt_t _app_evt, *app_evt = &_app_evt;
812   session_disconnected_reply_msg_t *rmp;
813   echo_main_t *em = &echo_main;
814   echo_session_t *session = 0;
815   uword *p;
816   int rv = 0;
817
818   p = hash_get (em->session_index_by_vpp_handles, mp->handle);
819   if (!p)
820     {
821       clib_warning ("couldn't find session key %llx", mp->handle);
822       return;
823     }
824
825   session = pool_elt_at_index (em->sessions, p[0]);
826   hash_unset (em->session_index_by_vpp_handles, mp->handle);
827   pool_put (em->sessions, session);
828
829   app_alloc_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt,
830                              SESSION_CTRL_EVT_DISCONNECTED_REPLY);
831   rmp = (session_disconnected_reply_msg_t *) app_evt->evt->data;
832   rmp->retval = rv;
833   rmp->handle = mp->handle;
834   rmp->context = mp->context;
835   app_send_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt);
836
837   session_print_stats (em, session);
838 }
839
840 static void
841 session_reset_handler (session_reset_msg_t * mp)
842 {
843   app_session_evt_t _app_evt, *app_evt = &_app_evt;
844   echo_main_t *em = &echo_main;
845   session_reset_reply_msg_t *rmp;
846   echo_session_t *session = 0;
847   uword *p;
848   int rv = 0;
849
850   p = hash_get (em->session_index_by_vpp_handles, mp->handle);
851
852   if (p)
853     {
854       session = pool_elt_at_index (em->sessions, p[0]);
855       clib_warning ("got reset");
856       /* Cleanup later */
857       em->time_to_stop = 1;
858     }
859   else
860     {
861       clib_warning ("couldn't find session key %llx", mp->handle);
862       return;
863     }
864
865   app_alloc_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt,
866                              SESSION_CTRL_EVT_RESET_REPLY);
867   rmp = (session_reset_reply_msg_t *) app_evt->evt->data;
868   rmp->retval = rv;
869   rmp->handle = mp->handle;
870   app_send_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt);
871 }
872
873 static void
874 handle_mq_event (session_event_t * e)
875 {
876   switch (e->event_type)
877     {
878     case SESSION_CTRL_EVT_BOUND:
879       session_bound_handler ((session_bound_msg_t *) e->data);
880       break;
881     case SESSION_CTRL_EVT_ACCEPTED:
882       session_accepted_handler ((session_accepted_msg_t *) e->data);
883       break;
884     case SESSION_CTRL_EVT_CONNECTED:
885       session_connected_handler ((session_connected_msg_t *) e->data);
886       break;
887     case SESSION_CTRL_EVT_DISCONNECTED:
888       session_disconnected_handler ((session_disconnected_msg_t *) e->data);
889       break;
890     case SESSION_CTRL_EVT_RESET:
891       session_reset_handler ((session_reset_msg_t *) e->data);
892       break;
893     default:
894       clib_warning ("unhandled %u", e->event_type);
895     }
896 }
897
898 static void
899 clients_run (echo_main_t * em)
900 {
901   f64 start_time, deltat, timeout = 100.0;
902   svm_msg_q_msg_t msg;
903   session_event_t *e;
904   echo_session_t *s;
905   int i;
906
907   /* Init test data */
908   vec_validate (em->connect_test_data, 1024 * 1024 - 1);
909   for (i = 0; i < vec_len (em->connect_test_data); i++)
910     em->connect_test_data[i] = i & 0xff;
911
912   /*
913    * Attach and connect the clients
914    */
915   if (application_attach (em))
916     return;
917
918   for (i = 0; i < em->n_clients; i++)
919     client_send_connect (em);
920
921   start_time = clib_time_now (&em->clib_time);
922   while (em->n_clients_connected < em->n_clients
923          && (clib_time_now (&em->clib_time) - start_time < timeout)
924          && em->state != STATE_FAILED)
925
926     {
927       svm_msg_q_sub (em->our_event_queue, &msg, SVM_Q_WAIT, 0);
928       e = svm_msg_q_msg_data (em->our_event_queue, &msg);
929       handle_mq_event (e);
930       svm_msg_q_free_msg (em->our_event_queue, &msg);
931     }
932
933   if (em->n_clients_connected != em->n_clients)
934     {
935       clib_warning ("failed to initialize all connections");
936       return;
937     }
938
939   /*
940    * Initialize connections
941    */
942   for (i = 0; i < em->n_clients; i++)
943     {
944       s = pool_elt_at_index (em->sessions, i);
945       s->bytes_to_send = em->bytes_to_send;
946       if (!em->no_return)
947         s->bytes_to_receive = em->bytes_to_send;
948     }
949   em->n_active_clients = em->n_clients_connected;
950
951   /*
952    * Wait for client threads to send the data
953    */
954   start_time = clib_time_now (&em->clib_time);
955   em->state = STATE_READY;
956   while (em->n_active_clients)
957     if (!svm_msg_q_is_empty (em->our_event_queue))
958       {
959         if (svm_msg_q_sub (em->our_event_queue, &msg, SVM_Q_WAIT, 0))
960           {
961             clib_warning ("svm msg q returned");
962             continue;
963           }
964         e = svm_msg_q_msg_data (em->our_event_queue, &msg);
965         if (e->event_type != FIFO_EVENT_APP_RX)
966           handle_mq_event (e);
967         svm_msg_q_free_msg (em->our_event_queue, &msg);
968       }
969
970   for (i = 0; i < em->n_clients; i++)
971     {
972       s = pool_elt_at_index (em->sessions, i);
973       client_disconnect (em, s);
974     }
975
976   /*
977    * Stats and detach
978    */
979   deltat = clib_time_now (&em->clib_time) - start_time;
980   fformat (stdout, "%lld bytes (%lld mbytes, %lld gbytes) in %.2f seconds\n",
981            em->tx_total, em->tx_total / (1ULL << 20),
982            em->tx_total / (1ULL << 30), deltat);
983   fformat (stdout, "%.4f Gbit/second\n", (em->tx_total * 8.0) / deltat / 1e9);
984
985   application_detach (em);
986 }
987
988 static void
989 vl_api_bind_uri_reply_t_handler (vl_api_bind_uri_reply_t * mp)
990 {
991   echo_main_t *em = &echo_main;
992
993   if (mp->retval)
994     {
995       clib_warning ("bind failed: %U", format_api_error,
996                     clib_net_to_host_u32 (mp->retval));
997       em->state = STATE_FAILED;
998       return;
999     }
1000
1001   em->state = STATE_READY;
1002 }
1003
1004 static void
1005 vl_api_unbind_uri_reply_t_handler (vl_api_unbind_uri_reply_t * mp)
1006 {
1007   echo_main_t *em = &echo_main;
1008
1009   if (mp->retval != 0)
1010     clib_warning ("returned %d", ntohl (mp->retval));
1011
1012   em->state = STATE_START;
1013 }
1014
1015 u8 *
1016 format_ip4_address (u8 * s, va_list * args)
1017 {
1018   u8 *a = va_arg (*args, u8 *);
1019   return format (s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
1020 }
1021
1022 u8 *
1023 format_ip6_address (u8 * s, va_list * args)
1024 {
1025   ip6_address_t *a = va_arg (*args, ip6_address_t *);
1026   u32 i, i_max_n_zero, max_n_zeros, i_first_zero, n_zeros, last_double_colon;
1027
1028   i_max_n_zero = ARRAY_LEN (a->as_u16);
1029   max_n_zeros = 0;
1030   i_first_zero = i_max_n_zero;
1031   n_zeros = 0;
1032   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
1033     {
1034       u32 is_zero = a->as_u16[i] == 0;
1035       if (is_zero && i_first_zero >= ARRAY_LEN (a->as_u16))
1036         {
1037           i_first_zero = i;
1038           n_zeros = 0;
1039         }
1040       n_zeros += is_zero;
1041       if ((!is_zero && n_zeros > max_n_zeros)
1042           || (i + 1 >= ARRAY_LEN (a->as_u16) && n_zeros > max_n_zeros))
1043         {
1044           i_max_n_zero = i_first_zero;
1045           max_n_zeros = n_zeros;
1046           i_first_zero = ARRAY_LEN (a->as_u16);
1047           n_zeros = 0;
1048         }
1049     }
1050
1051   last_double_colon = 0;
1052   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
1053     {
1054       if (i == i_max_n_zero && max_n_zeros > 1)
1055         {
1056           s = format (s, "::");
1057           i += max_n_zeros - 1;
1058           last_double_colon = 1;
1059         }
1060       else
1061         {
1062           s = format (s, "%s%x",
1063                       (last_double_colon || i == 0) ? "" : ":",
1064                       clib_net_to_host_u16 (a->as_u16[i]));
1065           last_double_colon = 0;
1066         }
1067     }
1068
1069   return s;
1070 }
1071
1072 /* Format an IP46 address. */
1073 u8 *
1074 format_ip46_address (u8 * s, va_list * args)
1075 {
1076   ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
1077   ip46_type_t type = va_arg (*args, ip46_type_t);
1078   int is_ip4 = 1;
1079
1080   switch (type)
1081     {
1082     case IP46_TYPE_ANY:
1083       is_ip4 = ip46_address_is_ip4 (ip46);
1084       break;
1085     case IP46_TYPE_IP4:
1086       is_ip4 = 1;
1087       break;
1088     case IP46_TYPE_IP6:
1089       is_ip4 = 0;
1090       break;
1091     }
1092
1093   return is_ip4 ?
1094     format (s, "%U", format_ip4_address, &ip46->ip4) :
1095     format (s, "%U", format_ip6_address, &ip46->ip6);
1096 }
1097
1098 static void
1099 server_handle_rx (echo_main_t * em, session_event_t * e)
1100 {
1101   int n_read, max_dequeue, n_sent;
1102   u32 offset, to_dequeue;
1103   echo_session_t *s;
1104
1105   s = pool_elt_at_index (em->sessions, e->fifo->client_session_index);
1106
1107   /* Clear event only once. Otherwise, if we do it in the loop by calling
1108    * app_recv_stream, we may end up with a lot of unhandled rx events on the
1109    * message queue */
1110   svm_fifo_unset_event (s->rx_fifo);
1111
1112   max_dequeue = svm_fifo_max_dequeue (s->rx_fifo);
1113   if (PREDICT_FALSE (!max_dequeue))
1114     return;
1115
1116   do
1117     {
1118       /* The options here are to limit ourselves to max_dequeue or read
1119        * even the data that was enqueued while we were dequeueing and which
1120        * now has an rx event in the mq. Either of the two work. */
1121       to_dequeue = clib_min (max_dequeue, vec_len (em->rx_buf));
1122       n_read = app_recv_stream_raw (s->rx_fifo, em->rx_buf, to_dequeue,
1123                                     0 /* clear evt */ , 0 /* peek */ );
1124       if (n_read > 0)
1125         {
1126           max_dequeue -= n_read;
1127           s->bytes_received += n_read;
1128         }
1129       else
1130         break;
1131
1132       /* Reflect if a non-drop session */
1133       if (!em->no_return && n_read > 0)
1134         {
1135           offset = 0;
1136           do
1137             {
1138               n_sent = app_send_stream ((app_session_t *) s,
1139                                         &em->rx_buf[offset],
1140                                         n_read, SVM_Q_WAIT);
1141               if (n_sent > 0)
1142                 {
1143                   n_read -= n_sent;
1144                   offset += n_sent;
1145                 }
1146             }
1147           while ((n_sent <= 0 || n_read > 0) && !em->time_to_stop);
1148         }
1149     }
1150   while (max_dequeue > 0 && !em->time_to_stop);
1151 }
1152
1153 static void
1154 server_handle_mq (echo_main_t * em)
1155 {
1156   svm_msg_q_msg_t msg;
1157   session_event_t *e;
1158
1159   while (1)
1160     {
1161       svm_msg_q_sub (em->our_event_queue, &msg, SVM_Q_WAIT, 0);
1162       e = svm_msg_q_msg_data (em->our_event_queue, &msg);
1163       switch (e->event_type)
1164         {
1165         case FIFO_EVENT_APP_RX:
1166           server_handle_rx (em, e);
1167           break;
1168         default:
1169           handle_mq_event (e);
1170           break;
1171         }
1172       if (PREDICT_FALSE (em->time_to_stop == 1))
1173         break;
1174       if (PREDICT_FALSE (em->time_to_print_stats == 1))
1175         {
1176           em->time_to_print_stats = 0;
1177           fformat (stdout, "%d connections\n", pool_elts (em->sessions));
1178         }
1179       svm_msg_q_free_msg (em->our_event_queue, &msg);
1180     }
1181 }
1182
1183 void
1184 server_send_listen (echo_main_t * em)
1185 {
1186   vl_api_bind_uri_t *bmp;
1187   bmp = vl_msg_api_alloc (sizeof (*bmp));
1188   clib_memset (bmp, 0, sizeof (*bmp));
1189
1190   bmp->_vl_msg_id = ntohs (VL_API_BIND_URI);
1191   bmp->client_index = em->my_client_index;
1192   bmp->context = ntohl (0xfeedface);
1193   memcpy (bmp->uri, em->uri, vec_len (em->uri));
1194   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & bmp);
1195 }
1196
1197 int
1198 server_listen (echo_main_t * em)
1199 {
1200   server_send_listen (em);
1201   if (wait_for_state_change (em, STATE_READY))
1202     {
1203       clib_warning ("timeout waiting for STATE_READY");
1204       return -1;
1205     }
1206   return 0;
1207 }
1208
1209 void
1210 server_send_unbind (echo_main_t * em)
1211 {
1212   vl_api_unbind_uri_t *ump;
1213
1214   ump = vl_msg_api_alloc (sizeof (*ump));
1215   clib_memset (ump, 0, sizeof (*ump));
1216
1217   ump->_vl_msg_id = ntohs (VL_API_UNBIND_URI);
1218   ump->client_index = em->my_client_index;
1219   memcpy (ump->uri, em->uri, vec_len (em->uri));
1220   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & ump);
1221 }
1222
1223 int
1224 server_unbind (echo_main_t * em)
1225 {
1226   server_send_unbind (em);
1227   if (wait_for_state_change (em, STATE_START))
1228     {
1229       clib_warning ("timeout waiting for STATE_START");
1230       return -1;
1231     }
1232   return 0;
1233 }
1234
1235 void
1236 server_run (echo_main_t * em)
1237 {
1238   echo_session_t *session;
1239   int i;
1240
1241   /* $$$$ hack preallocation */
1242   for (i = 0; i < 200000; i++)
1243     {
1244       pool_get (em->sessions, session);
1245       clib_memset (session, 0, sizeof (*session));
1246     }
1247   for (i = 0; i < 200000; i++)
1248     pool_put_index (em->sessions, i);
1249
1250   if (application_attach (em))
1251     return;
1252
1253   /* Bind to uri */
1254   if (server_listen (em))
1255     return;
1256
1257   /* Enter handle event loop */
1258   server_handle_mq (em);
1259
1260   /* Cleanup */
1261   server_send_unbind (em);
1262
1263   application_detach (em);
1264
1265   fformat (stdout, "Test complete...\n");
1266 }
1267
1268 static void
1269 vl_api_disconnect_session_reply_t_handler (vl_api_disconnect_session_reply_t *
1270                                            mp)
1271 {
1272   echo_main_t *em = &echo_main;
1273   uword *p;
1274
1275   if (mp->retval)
1276     {
1277       clib_warning ("vpp complained about disconnect: %d",
1278                     ntohl (mp->retval));
1279       return;
1280     }
1281
1282   em->state = STATE_START;
1283
1284   p = hash_get (em->session_index_by_vpp_handles, mp->handle);
1285   if (p)
1286     {
1287       hash_unset (em->session_index_by_vpp_handles, mp->handle);
1288     }
1289   else
1290     {
1291       clib_warning ("couldn't find session key %llx", mp->handle);
1292     }
1293 }
1294
1295 static void
1296   vl_api_application_tls_cert_add_reply_t_handler
1297   (vl_api_application_tls_cert_add_reply_t * mp)
1298 {
1299   if (mp->retval)
1300     clib_warning ("failed to add tls cert");
1301 }
1302
1303 static void
1304   vl_api_application_tls_key_add_reply_t_handler
1305   (vl_api_application_tls_key_add_reply_t * mp)
1306 {
1307   if (mp->retval)
1308     clib_warning ("failed to add tls key");
1309 }
1310
1311 #define foreach_tcp_echo_msg                                            \
1312 _(BIND_URI_REPLY, bind_uri_reply)                                       \
1313 _(UNBIND_URI_REPLY, unbind_uri_reply)                                   \
1314 _(DISCONNECT_SESSION_REPLY, disconnect_session_reply)                   \
1315 _(APPLICATION_ATTACH_REPLY, application_attach_reply)                   \
1316 _(APPLICATION_DETACH_REPLY, application_detach_reply)                   \
1317 _(MAP_ANOTHER_SEGMENT, map_another_segment)                             \
1318 _(APPLICATION_TLS_CERT_ADD_REPLY, application_tls_cert_add_reply)       \
1319 _(APPLICATION_TLS_KEY_ADD_REPLY, application_tls_key_add_reply)         \
1320
1321 void
1322 tcp_echo_api_hookup (echo_main_t * em)
1323 {
1324 #define _(N,n)                                                  \
1325     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1326                            vl_api_##n##_t_handler,              \
1327                            vl_noop_handler,                     \
1328                            vl_api_##n##_t_endian,               \
1329                            vl_api_##n##_t_print,                \
1330                            sizeof(vl_api_##n##_t), 1);
1331   foreach_tcp_echo_msg;
1332 #undef _
1333 }
1334
1335 int
1336 main (int argc, char **argv)
1337 {
1338   int i_am_server = 1, test_return_packets = 0;
1339   echo_main_t *em = &echo_main;
1340   svm_fifo_segment_main_t *sm = &em->segment_main;
1341   unformat_input_t _argv, *a = &_argv;
1342   u8 *chroot_prefix;
1343   u8 *uri = 0;
1344   u8 *bind_uri = (u8 *) "tcp://0.0.0.0/1234";
1345   u8 *connect_uri = (u8 *) "tcp://6.0.1.1/1234";
1346   u64 bytes_to_send = 64 << 10, mbytes;
1347   char *app_name;
1348   u32 tmp;
1349
1350   clib_mem_init_thread_safe (0, 256 << 20);
1351
1352   clib_memset (em, 0, sizeof (*em));
1353   em->session_index_by_vpp_handles = hash_create (0, sizeof (uword));
1354   em->my_pid = getpid ();
1355   em->configured_segment_size = 1 << 20;
1356   em->socket_name = 0;
1357   em->use_sock_api = 1;
1358   em->fifo_size = 64 << 10;
1359   em->n_clients = 1;
1360
1361   clib_time_init (&em->clib_time);
1362   init_error_string_table (em);
1363   svm_fifo_segment_main_init (sm, HIGH_SEGMENT_BASEVA, 20);
1364   unformat_init_command_line (a, argv);
1365
1366   while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
1367     {
1368       if (unformat (a, "chroot prefix %s", &chroot_prefix))
1369         {
1370           vl_set_memory_root_path ((char *) chroot_prefix);
1371         }
1372       else if (unformat (a, "uri %s", &uri))
1373         ;
1374       else if (unformat (a, "segment-size %dM", &tmp))
1375         em->configured_segment_size = tmp << 20;
1376       else if (unformat (a, "segment-size %dG", &tmp))
1377         em->configured_segment_size = tmp << 30;
1378       else if (unformat (a, "server"))
1379         i_am_server = 1;
1380       else if (unformat (a, "client"))
1381         i_am_server = 0;
1382       else if (unformat (a, "no-return"))
1383         em->no_return = 1;
1384       else if (unformat (a, "test"))
1385         test_return_packets = 1;
1386       else if (unformat (a, "mbytes %lld", &mbytes))
1387         {
1388           bytes_to_send = mbytes << 20;
1389         }
1390       else if (unformat (a, "gbytes %lld", &mbytes))
1391         {
1392           bytes_to_send = mbytes << 30;
1393         }
1394       else if (unformat (a, "socket-name %s", &em->socket_name))
1395         ;
1396       else if (unformat (a, "use-svm-api"))
1397         em->use_sock_api = 0;
1398       else if (unformat (a, "fifo-size %d", &tmp))
1399         em->fifo_size = tmp << 10;
1400       else if (unformat (a, "nclients %d", &em->n_clients))
1401         ;
1402       else
1403         {
1404           fformat (stderr, "%s: usage [master|slave]\n", argv[0]);
1405           exit (1);
1406         }
1407     }
1408
1409   if (!em->socket_name)
1410     em->socket_name = format (0, "%s%c", API_SOCKET_FILE, 0);
1411
1412   if (uri)
1413     {
1414       em->uri = format (0, "%s%c", uri, 0);
1415       em->connect_uri = format (0, "%s%c", uri, 0);
1416     }
1417   else
1418     {
1419       em->uri = format (0, "%s%c", bind_uri, 0);
1420       em->connect_uri = format (0, "%s%c", connect_uri, 0);
1421     }
1422
1423   em->i_am_master = i_am_server;
1424   em->test_return_packets = test_return_packets;
1425   em->bytes_to_send = bytes_to_send;
1426   em->time_to_stop = 0;
1427   vec_validate (em->rx_buf, 4 << 20);
1428   vec_validate (em->client_thread_handles, em->n_clients - 1);
1429   vec_validate (em->thread_args, em->n_clients - 1);
1430
1431   setup_signal_handlers ();
1432   tcp_echo_api_hookup (em);
1433
1434   app_name = i_am_server ? "tcp_echo_server" : "tcp_echo_client";
1435   if (connect_to_vpp (app_name) < 0)
1436     {
1437       svm_region_exit ();
1438       fformat (stderr, "Couldn't connect to vpe, exiting...\n");
1439       exit (1);
1440     }
1441
1442   if (i_am_server == 0)
1443     clients_run (em);
1444   else
1445     server_run (em);
1446
1447   /* Make sure detach finishes */
1448   wait_for_state_change (em, STATE_DETACHED);
1449
1450   disconnect_from_vpp (em);
1451   exit (0);
1452 }
1453
1454 /*
1455  * fd.io coding-style-patch-verification: ON
1456  *
1457  * Local Variables:
1458  * eval: (c-set-style "gnu")
1459  * End:
1460  */