vcl: support for eventfd mq signaling
[vpp.git] / src / vnet / session / application.c
1 /*
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 <vnet/session/application.h>
17 #include <vnet/session/application_interface.h>
18 #include <vnet/session/application_namespace.h>
19 #include <vnet/session/session.h>
20
21 /**
22  * Pool from which we allocate all applications
23  */
24 static application_t *app_pool;
25
26 /**
27  * Hash table of apps by api client index
28  */
29 static uword *app_by_api_client_index;
30
31 /**
32  * Hash table of builtin apps by name
33  */
34 static uword *app_by_name;
35
36 static u8 *
37 app_get_name_from_reg_index (application_t * app)
38 {
39   u8 *app_name;
40
41   vl_api_registration_t *regp;
42   regp = vl_api_client_index_to_registration (app->api_client_index);
43   if (!regp)
44     app_name = format (0, "builtin-%d%c", app->index, 0);
45   else
46     app_name = format (0, "%s%c", regp->name, 0);
47
48   return app_name;
49 }
50
51 static u8 *
52 app_get_name (application_t * app)
53 {
54   if (!app->name)
55     return app_get_name_from_reg_index (app);
56   return app->name;
57 }
58
59 u32
60 application_session_table (application_t * app, u8 fib_proto)
61 {
62   app_namespace_t *app_ns;
63   app_ns = app_namespace_get (app->ns_index);
64   if (!application_has_global_scope (app))
65     return APP_INVALID_INDEX;
66   if (fib_proto == FIB_PROTOCOL_IP4)
67     return session_lookup_get_index_for_fib (fib_proto,
68                                              app_ns->ip4_fib_index);
69   else
70     return session_lookup_get_index_for_fib (fib_proto,
71                                              app_ns->ip6_fib_index);
72 }
73
74 u32
75 application_local_session_table (application_t * app)
76 {
77   app_namespace_t *app_ns;
78   if (!application_has_local_scope (app))
79     return APP_INVALID_INDEX;
80   app_ns = app_namespace_get (app->ns_index);
81   return app_ns->local_table_index;
82 }
83
84 int
85 application_api_queue_is_full (application_t * app)
86 {
87   svm_queue_t *q;
88
89   /* builtin servers are always OK */
90   if (app->api_client_index == ~0)
91     return 0;
92
93   q = vl_api_client_index_to_input_queue (app->api_client_index);
94   if (!q)
95     return 1;
96
97   if (q->cursize == q->maxsize)
98     return 1;
99   return 0;
100 }
101
102 /**
103  * Returns app name
104  *
105  * Since the name is not stored per app, we generate it on the fly. It is
106  * the caller's responsibility to free the vector
107  */
108 u8 *
109 application_name_from_index (u32 app_index)
110 {
111   application_t *app = application_get (app_index);
112   if (!app)
113     return 0;
114   return app_get_name_from_reg_index (app);
115 }
116
117 static void
118 application_table_add (application_t * app)
119 {
120   if (app->api_client_index != APP_INVALID_INDEX)
121     hash_set (app_by_api_client_index, app->api_client_index, app->index);
122   else if (app->name)
123     hash_set_mem (app_by_name, app->name, app->index);
124 }
125
126 static void
127 application_table_del (application_t * app)
128 {
129   if (app->api_client_index != APP_INVALID_INDEX)
130     hash_unset (app_by_api_client_index, app->api_client_index);
131   else if (app->name)
132     hash_unset_mem (app_by_name, app->name);
133 }
134
135 application_t *
136 application_lookup (u32 api_client_index)
137 {
138   uword *p;
139   p = hash_get (app_by_api_client_index, api_client_index);
140   if (p)
141     return application_get (p[0]);
142
143   return 0;
144 }
145
146 application_t *
147 application_lookup_name (const u8 * name)
148 {
149   uword *p;
150   p = hash_get_mem (app_by_name, name);
151   if (p)
152     return application_get (p[0]);
153
154   return 0;
155 }
156
157 application_t *
158 application_new ()
159 {
160   application_t *app;
161   pool_get (app_pool, app);
162   memset (app, 0, sizeof (*app));
163   app->index = application_get_index (app);
164   app->connects_seg_manager = APP_INVALID_SEGMENT_MANAGER_INDEX;
165   app->first_segment_manager = APP_INVALID_SEGMENT_MANAGER_INDEX;
166   app->local_segment_manager = APP_INVALID_SEGMENT_MANAGER_INDEX;
167   if (CLIB_DEBUG > 1)
168     clib_warning ("[%d] New app (%d)", getpid (), app->index);
169   return app;
170 }
171
172 void
173 application_del (application_t * app)
174 {
175   vnet_unbind_args_t _a, *a = &_a;
176   u64 handle, *handles = 0;
177   segment_manager_t *sm;
178   u32 index;
179   int i;
180
181   /*
182    * The app event queue allocated in first segment is cleared with
183    * the segment manager. No need to explicitly free it.
184    */
185   if (CLIB_DEBUG > 1)
186     clib_warning ("[%d] Delete app (%d)", getpid (), app->index);
187
188   if (application_is_proxy (app))
189     application_remove_proxy (app);
190
191   /*
192    *  Listener cleanup
193    */
194
195   /* *INDENT-OFF* */
196   hash_foreach (handle, index, app->listeners_table,
197   ({
198     vec_add1 (handles, handle);
199     sm = segment_manager_get (index);
200     sm->app_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
201   }));
202   /* *INDENT-ON* */
203
204   for (i = 0; i < vec_len (handles); i++)
205     {
206       a->app_index = app->index;
207       a->handle = handles[i];
208       /* seg manager is removed when unbind completes */
209       vnet_unbind (a);
210     }
211
212   /*
213    * Connects segment manager cleanup
214    */
215
216   if (app->connects_seg_manager != APP_INVALID_SEGMENT_MANAGER_INDEX)
217     {
218       sm = segment_manager_get (app->connects_seg_manager);
219       sm->app_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
220       segment_manager_init_del (sm);
221     }
222
223   /* If first segment manager is used by a listener */
224   if (app->first_segment_manager != APP_INVALID_SEGMENT_MANAGER_INDEX
225       && app->first_segment_manager != app->connects_seg_manager)
226     {
227       sm = segment_manager_get (app->first_segment_manager);
228       /* .. and has no fifos, e.g. it might be used for redirected sessions,
229        * remove it */
230       if (!segment_manager_has_fifos (sm))
231         {
232           sm->app_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
233           segment_manager_del (sm);
234         }
235     }
236
237   /*
238    * Local connections cleanup
239    */
240   application_local_sessions_del (app);
241
242   vec_free (app->tls_cert);
243   vec_free (app->tls_key);
244
245   application_table_del (app);
246   vec_free (app->name);
247   pool_put (app_pool, app);
248 }
249
250 static void
251 application_verify_cb_fns (session_cb_vft_t * cb_fns)
252 {
253   if (cb_fns->session_accept_callback == 0)
254     clib_warning ("No accept callback function provided");
255   if (cb_fns->session_connected_callback == 0)
256     clib_warning ("No session connected callback function provided");
257   if (cb_fns->session_disconnect_callback == 0)
258     clib_warning ("No session disconnect callback function provided");
259   if (cb_fns->session_reset_callback == 0)
260     clib_warning ("No session reset callback function provided");
261 }
262
263 /**
264  * Check app config for given segment type
265  *
266  * Returns 1 on success and 0 otherwise
267  */
268 static u8
269 application_verify_cfg (ssvm_segment_type_t st)
270 {
271   u8 is_valid;
272   if (st == SSVM_SEGMENT_MEMFD)
273     {
274       is_valid = (session_manager_get_evt_q_segment () != 0);
275       if (!is_valid)
276         clib_warning ("memfd seg: vpp's event qs IN binary api svm region");
277       return is_valid;
278     }
279   else if (st == SSVM_SEGMENT_SHM)
280     {
281       is_valid = (session_manager_get_evt_q_segment () == 0);
282       if (!is_valid)
283         clib_warning ("shm seg: vpp's event qs NOT IN binary api svm region");
284       return is_valid;
285     }
286   else
287     return 1;
288 }
289
290 int
291 application_init (application_t * app, u32 api_client_index, u8 * app_name,
292                   u64 * options, session_cb_vft_t * cb_fns)
293 {
294   ssvm_segment_type_t seg_type = SSVM_SEGMENT_MEMFD;
295   u32 first_seg_size, prealloc_fifo_pairs;
296   segment_manager_properties_t *props;
297   vl_api_registration_t *reg;
298   segment_manager_t *sm;
299   int rv;
300
301   /*
302    * Make sure we support the requested configuration
303    */
304
305   if (!(options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_IS_BUILTIN))
306     {
307       reg = vl_api_client_index_to_registration (api_client_index);
308       if (!reg)
309         return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
310       if (vl_api_registration_file_index (reg) == VL_API_INVALID_FI)
311         seg_type = SSVM_SEGMENT_SHM;
312     }
313   else
314     {
315       if (options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_EVT_MQ_USE_EVENTFD)
316         {
317           clib_warning ("mq eventfds can only be used if socket transport is "
318                         "used for api");
319           return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
320         }
321       seg_type = SSVM_SEGMENT_PRIVATE;
322     }
323
324   if (!application_verify_cfg (seg_type))
325     return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
326
327   /*
328    * Setup segment manager
329    */
330   sm = segment_manager_new ();
331   sm->app_index = app->index;
332   props = application_segment_manager_properties (app);
333   segment_manager_properties_init (props);
334   if (options[APP_OPTIONS_ADD_SEGMENT_SIZE])
335     {
336       props->add_segment_size = options[APP_OPTIONS_ADD_SEGMENT_SIZE];
337       props->add_segment = 1;
338     }
339   if (options[APP_OPTIONS_RX_FIFO_SIZE])
340     props->rx_fifo_size = options[APP_OPTIONS_RX_FIFO_SIZE];
341   if (options[APP_OPTIONS_TX_FIFO_SIZE])
342     props->tx_fifo_size = options[APP_OPTIONS_TX_FIFO_SIZE];
343   if (options[APP_OPTIONS_EVT_QUEUE_SIZE])
344     props->evt_q_size = options[APP_OPTIONS_EVT_QUEUE_SIZE];
345   if (options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_EVT_MQ_USE_EVENTFD)
346     props->use_mq_eventfd = 1;
347   if (options[APP_OPTIONS_TLS_ENGINE])
348     app->tls_engine = options[APP_OPTIONS_TLS_ENGINE];
349   props->segment_type = seg_type;
350
351   first_seg_size = options[APP_OPTIONS_SEGMENT_SIZE];
352   prealloc_fifo_pairs = options[APP_OPTIONS_PREALLOC_FIFO_PAIRS];
353
354   if ((rv = segment_manager_init (sm, first_seg_size, prealloc_fifo_pairs)))
355     return rv;
356   sm->first_is_protected = 1;
357
358   /*
359    * Setup application
360    */
361   app->first_segment_manager = segment_manager_index (sm);
362   app->api_client_index = api_client_index;
363   app->flags = options[APP_OPTIONS_FLAGS];
364   app->cb_fns = *cb_fns;
365   app->ns_index = options[APP_OPTIONS_NAMESPACE];
366   app->listeners_table = hash_create (0, sizeof (u64));
367   app->local_connects = hash_create (0, sizeof (u64));
368   app->proxied_transports = options[APP_OPTIONS_PROXY_TRANSPORT];
369   app->event_queue = segment_manager_event_queue (sm);
370   app->name = vec_dup (app_name);
371
372   /* If no scope enabled, default to global */
373   if (!application_has_global_scope (app)
374       && !application_has_local_scope (app))
375     app->flags |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
376
377   /* Check that the obvious things are properly set up */
378   application_verify_cb_fns (cb_fns);
379
380   /* Add app to lookup by api_client_index table */
381   application_table_add (app);
382
383   /*
384    * Segment manager for local sessions
385    */
386   sm = segment_manager_new ();
387   sm->app_index = app->index;
388   app->local_segment_manager = segment_manager_index (sm);
389
390   return 0;
391 }
392
393 application_t *
394 application_get (u32 index)
395 {
396   if (index == APP_INVALID_INDEX)
397     return 0;
398   return pool_elt_at_index (app_pool, index);
399 }
400
401 application_t *
402 application_get_if_valid (u32 index)
403 {
404   if (pool_is_free_index (app_pool, index))
405     return 0;
406
407   return pool_elt_at_index (app_pool, index);
408 }
409
410 u32
411 application_get_index (application_t * app)
412 {
413   return app - app_pool;
414 }
415
416 static segment_manager_t *
417 application_alloc_segment_manager (application_t * app)
418 {
419   segment_manager_t *sm = 0;
420
421   /* If the first segment manager is not in use, don't allocate a new one */
422   if (app->first_segment_manager != APP_INVALID_SEGMENT_MANAGER_INDEX
423       && app->first_segment_manager_in_use == 0)
424     {
425       sm = segment_manager_get (app->first_segment_manager);
426       app->first_segment_manager_in_use = 1;
427       return sm;
428     }
429
430   sm = segment_manager_new ();
431   sm->app_index = app->index;
432
433   return sm;
434 }
435
436 /**
437  * Start listening local transport endpoint for requested transport.
438  *
439  * Creates a 'dummy' stream session with state LISTENING to be used in session
440  * lookups, prior to establishing connection. Requests transport to build
441  * it's own specific listening connection.
442  */
443 int
444 application_start_listen (application_t * srv, session_endpoint_t * sep,
445                           session_handle_t * res)
446 {
447   segment_manager_t *sm;
448   stream_session_t *s;
449   session_handle_t handle;
450   session_type_t sst;
451
452   sst = session_type_from_proto_and_ip (sep->transport_proto, sep->is_ip4);
453   s = listen_session_new (0, sst);
454   s->app_index = srv->index;
455
456   /* Allocate segment manager. All sessions derived out of a listen session
457    * have fifos allocated by the same segment manager. */
458   if (!(sm = application_alloc_segment_manager (srv)))
459     goto err;
460
461   /* Add to app's listener table. Useful to find all child listeners
462    * when app goes down, although, just for unbinding this is not needed */
463   handle = listen_session_get_handle (s);
464   hash_set (srv->listeners_table, handle, segment_manager_index (sm));
465
466   if (stream_session_listen (s, sep))
467     {
468       segment_manager_del (sm);
469       hash_unset (srv->listeners_table, handle);
470       goto err;
471     }
472
473   *res = handle;
474   return 0;
475
476 err:
477   listen_session_del (s);
478   return -1;
479 }
480
481 /**
482  * Stop listening on session associated to handle
483  */
484 int
485 application_stop_listen (application_t * srv, session_handle_t handle)
486 {
487   stream_session_t *listener;
488   uword *indexp;
489   segment_manager_t *sm;
490
491   if (srv && hash_get (srv->listeners_table, handle) == 0)
492     {
493       clib_warning ("app doesn't own handle %llu!", handle);
494       return -1;
495     }
496
497   listener = listen_session_get_from_handle (handle);
498   stream_session_stop_listen (listener);
499
500   indexp = hash_get (srv->listeners_table, handle);
501   ASSERT (indexp);
502
503   sm = segment_manager_get (*indexp);
504   if (srv->first_segment_manager == *indexp)
505     {
506       /* Delete sessions but don't remove segment manager */
507       srv->first_segment_manager_in_use = 0;
508       segment_manager_del_sessions (sm);
509     }
510   else
511     {
512       segment_manager_init_del (sm);
513     }
514   hash_unset (srv->listeners_table, handle);
515   listen_session_del (listener);
516
517   return 0;
518 }
519
520 int
521 application_open_session (application_t * app, session_endpoint_t * sep,
522                           u32 api_context)
523 {
524   int rv;
525
526   /* Make sure we have a segment manager for connects */
527   application_alloc_connects_segment_manager (app);
528
529   if ((rv = session_open (app->index, sep, api_context)))
530     return rv;
531
532   return 0;
533 }
534
535 int
536 application_alloc_connects_segment_manager (application_t * app)
537 {
538   segment_manager_t *sm;
539
540   if (app->connects_seg_manager == APP_INVALID_SEGMENT_MANAGER_INDEX)
541     {
542       sm = application_alloc_segment_manager (app);
543       if (sm == 0)
544         return -1;
545       app->connects_seg_manager = segment_manager_index (sm);
546     }
547   return 0;
548 }
549
550 segment_manager_t *
551 application_get_connect_segment_manager (application_t * app)
552 {
553   ASSERT (app->connects_seg_manager != (u32) ~ 0);
554   return segment_manager_get (app->connects_seg_manager);
555 }
556
557 segment_manager_t *
558 application_get_listen_segment_manager (application_t * app,
559                                         stream_session_t * s)
560 {
561   uword *smp;
562   smp = hash_get (app->listeners_table, listen_session_get_handle (s));
563   ASSERT (smp != 0);
564   return segment_manager_get (*smp);
565 }
566
567 segment_manager_t *
568 application_get_local_segment_manager (application_t * app)
569 {
570   return segment_manager_get (app->local_segment_manager);
571 }
572
573 segment_manager_t *
574 application_get_local_segment_manager_w_session (application_t * app,
575                                                  local_session_t * ls)
576 {
577   stream_session_t *listener;
578   if (application_local_session_listener_has_transport (ls))
579     {
580       listener = listen_session_get (ls->listener_index);
581       return application_get_listen_segment_manager (app, listener);
582     }
583   return segment_manager_get (app->local_segment_manager);
584 }
585
586 int
587 application_is_proxy (application_t * app)
588 {
589   return (app->flags & APP_OPTIONS_FLAGS_IS_PROXY);
590 }
591
592 int
593 application_is_builtin (application_t * app)
594 {
595   return (app->flags & APP_OPTIONS_FLAGS_IS_BUILTIN);
596 }
597
598 int
599 application_is_builtin_proxy (application_t * app)
600 {
601   return (application_is_proxy (app) && application_is_builtin (app));
602 }
603
604 /**
605  * Send an API message to the external app, to map new segment
606  */
607 int
608 application_add_segment_notify (u32 app_index, ssvm_private_t * fs)
609 {
610   application_t *app = application_get (app_index);
611   return app->cb_fns.add_segment_callback (app->api_client_index, fs);
612 }
613
614 u8
615 application_has_local_scope (application_t * app)
616 {
617   return app->flags & APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
618 }
619
620 u8
621 application_has_global_scope (application_t * app)
622 {
623   return app->flags & APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
624 }
625
626 u32
627 application_n_listeners (application_t * app)
628 {
629   return hash_elts (app->listeners_table);
630 }
631
632 stream_session_t *
633 application_first_listener (application_t * app, u8 fib_proto,
634                             u8 transport_proto)
635 {
636   stream_session_t *listener;
637   u64 handle;
638   u32 sm_index;
639   u8 sst;
640
641   sst = session_type_from_proto_and_ip (transport_proto,
642                                         fib_proto == FIB_PROTOCOL_IP4);
643
644   /* *INDENT-OFF* */
645    hash_foreach (handle, sm_index, app->listeners_table, ({
646      listener = listen_session_get_from_handle (handle);
647      if (listener->session_type == sst
648          && listener->listener_index != SESSION_PROXY_LISTENER_INDEX)
649        return listener;
650    }));
651   /* *INDENT-ON* */
652
653   return 0;
654 }
655
656 stream_session_t *
657 application_proxy_listener (application_t * app, u8 fib_proto,
658                             u8 transport_proto)
659 {
660   stream_session_t *listener;
661   u64 handle;
662   u32 sm_index;
663   u8 sst;
664
665   sst = session_type_from_proto_and_ip (transport_proto,
666                                         fib_proto == FIB_PROTOCOL_IP4);
667
668   /* *INDENT-OFF* */
669    hash_foreach (handle, sm_index, app->listeners_table, ({
670      listener = listen_session_get_from_handle (handle);
671      if (listener->session_type == sst
672          && listener->listener_index == SESSION_PROXY_LISTENER_INDEX)
673        return listener;
674    }));
675   /* *INDENT-ON* */
676
677   return 0;
678 }
679
680 static clib_error_t *
681 application_start_stop_proxy_fib_proto (application_t * app, u8 fib_proto,
682                                         u8 transport_proto, u8 is_start)
683 {
684   app_namespace_t *app_ns = app_namespace_get (app->ns_index);
685   u8 is_ip4 = (fib_proto == FIB_PROTOCOL_IP4);
686   session_endpoint_t sep = SESSION_ENDPOINT_NULL;
687   transport_connection_t *tc;
688   stream_session_t *s;
689   u64 handle;
690
691   if (is_start)
692     {
693       s = application_first_listener (app, fib_proto, transport_proto);
694       if (!s)
695         {
696           sep.is_ip4 = is_ip4;
697           sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
698           sep.sw_if_index = app_ns->sw_if_index;
699           sep.transport_proto = transport_proto;
700           application_start_listen (app, &sep, &handle);
701           s = listen_session_get_from_handle (handle);
702           s->listener_index = SESSION_PROXY_LISTENER_INDEX;
703         }
704     }
705   else
706     {
707       s = application_proxy_listener (app, fib_proto, transport_proto);
708       ASSERT (s);
709     }
710
711   tc = listen_session_get_transport (s);
712
713   if (!ip_is_zero (&tc->lcl_ip, 1))
714     {
715       u32 sti;
716       sep.is_ip4 = is_ip4;
717       sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
718       sep.transport_proto = transport_proto;
719       sep.port = 0;
720       sti = session_lookup_get_index_for_fib (fib_proto, sep.fib_index);
721       if (is_start)
722         session_lookup_add_session_endpoint (sti, &sep, s->session_index);
723       else
724         session_lookup_del_session_endpoint (sti, &sep);
725     }
726
727   return 0;
728 }
729
730 static void
731 application_start_stop_proxy_local_scope (application_t * app,
732                                           u8 transport_proto, u8 is_start)
733 {
734   session_endpoint_t sep = SESSION_ENDPOINT_NULL;
735   app_namespace_t *app_ns;
736   app_ns = app_namespace_get (app->ns_index);
737   sep.is_ip4 = 1;
738   sep.transport_proto = transport_proto;
739   sep.port = 0;
740
741   if (is_start)
742     {
743       session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
744                                            app->index);
745       sep.is_ip4 = 0;
746       session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
747                                            app->index);
748     }
749   else
750     {
751       session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
752       sep.is_ip4 = 0;
753       session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
754     }
755 }
756
757 void
758 application_start_stop_proxy (application_t * app,
759                               transport_proto_t transport_proto, u8 is_start)
760 {
761   if (application_has_local_scope (app))
762     application_start_stop_proxy_local_scope (app, transport_proto, is_start);
763
764   if (application_has_global_scope (app))
765     {
766       application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP4,
767                                               transport_proto, is_start);
768       application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP6,
769                                               transport_proto, is_start);
770     }
771 }
772
773 void
774 application_setup_proxy (application_t * app)
775 {
776   u16 transports = app->proxied_transports;
777   transport_proto_t tp;
778
779   ASSERT (application_is_proxy (app));
780
781   /* *INDENT-OFF* */
782   transport_proto_foreach (tp, ({
783     if (transports & (1 << tp))
784       application_start_stop_proxy (app, tp, 1);
785   }));
786   /* *INDENT-ON* */
787 }
788
789 void
790 application_remove_proxy (application_t * app)
791 {
792   u16 transports = app->proxied_transports;
793   transport_proto_t tp;
794
795   ASSERT (application_is_proxy (app));
796
797   /* *INDENT-OFF* */
798   transport_proto_foreach (tp, ({
799     if (transports & (1 << tp))
800       application_start_stop_proxy (app, tp, 0);
801   }));
802   /* *INDENT-ON* */
803 }
804
805 segment_manager_properties_t *
806 application_segment_manager_properties (application_t * app)
807 {
808   return &app->sm_properties;
809 }
810
811 segment_manager_properties_t *
812 application_get_segment_manager_properties (u32 app_index)
813 {
814   application_t *app = application_get (app_index);
815   return &app->sm_properties;
816 }
817
818 static inline int
819 app_enqueue_evt (svm_msg_q_t * mq, svm_msg_q_msg_t * msg, u8 lock)
820 {
821   if (PREDICT_FALSE (svm_msg_q_is_full (mq)))
822     {
823       clib_warning ("evt q full");
824       svm_msg_q_free_msg (mq, msg);
825       if (lock)
826         svm_msg_q_unlock (mq);
827       return -1;
828     }
829
830   if (lock)
831     {
832       svm_msg_q_add_and_unlock (mq, msg);
833       return 0;
834     }
835
836   /* Even when not locking the ring, we must wait for queue mutex */
837   if (svm_msg_q_add (mq, msg, SVM_Q_WAIT))
838     {
839       clib_warning ("msg q add returned");
840       return -1;
841     }
842   return 0;
843 }
844
845 static inline int
846 app_send_io_evt_rx (application_t * app, stream_session_t * s, u8 lock)
847 {
848   session_event_t *evt;
849   svm_msg_q_msg_t msg;
850   svm_msg_q_t *mq;
851
852   if (PREDICT_FALSE (s->session_state != SESSION_STATE_READY
853                      && s->session_state != SESSION_STATE_LISTENING))
854     {
855       /* Session is closed so app will never clean up. Flush rx fifo */
856       if (s->session_state == SESSION_STATE_CLOSED)
857         svm_fifo_dequeue_drop_all (s->server_rx_fifo);
858       return 0;
859     }
860
861   if (app->cb_fns.builtin_app_rx_callback)
862     return app->cb_fns.builtin_app_rx_callback (s);
863
864   if (svm_fifo_has_event (s->server_rx_fifo)
865       || svm_fifo_is_empty (s->server_rx_fifo))
866     return 0;
867
868   mq = app->event_queue;
869   if (lock)
870     svm_msg_q_lock (mq);
871
872   if (PREDICT_FALSE (svm_msg_q_ring_is_full (mq, SESSION_MQ_IO_EVT_RING)))
873     {
874       clib_warning ("evt q rings full");
875       if (lock)
876         svm_msg_q_unlock (mq);
877       return -1;
878     }
879
880   msg = svm_msg_q_alloc_msg_w_ring (mq, SESSION_MQ_IO_EVT_RING);
881   ASSERT (!svm_msg_q_msg_is_invalid (&msg));
882
883   evt = (session_event_t *) svm_msg_q_msg_data (mq, &msg);
884   evt->fifo = s->server_rx_fifo;
885   evt->event_type = FIFO_EVENT_APP_RX;
886
887   if (app_enqueue_evt (mq, &msg, lock))
888     return -1;
889   (void) svm_fifo_set_event (s->server_rx_fifo);
890   return 0;
891 }
892
893 static inline int
894 app_send_io_evt_tx (application_t * app, stream_session_t * s, u8 lock)
895 {
896   svm_msg_q_t *mq;
897   session_event_t *evt;
898   svm_msg_q_msg_t msg;
899
900   if (application_is_builtin (app))
901     return 0;
902
903   mq = app->event_queue;
904   if (lock)
905     svm_msg_q_lock (mq);
906
907   if (PREDICT_FALSE (svm_msg_q_ring_is_full (mq, SESSION_MQ_IO_EVT_RING)))
908     {
909       clib_warning ("evt q rings full");
910       if (lock)
911         svm_msg_q_unlock (mq);
912       return -1;
913     }
914
915   msg = svm_msg_q_alloc_msg_w_ring (mq, SESSION_MQ_IO_EVT_RING);
916   ASSERT (!svm_msg_q_msg_is_invalid (&msg));
917
918   evt = (session_event_t *) svm_msg_q_msg_data (mq, &msg);
919   evt->event_type = FIFO_EVENT_APP_TX;
920   evt->fifo = s->server_tx_fifo;
921
922   return app_enqueue_evt (mq, &msg, lock);
923 }
924
925 /* *INDENT-OFF* */
926 typedef int (app_send_evt_handler_fn) (application_t *app,
927                                        stream_session_t *s,
928                                        u8 lock);
929 static app_send_evt_handler_fn * const app_send_evt_handler_fns[3] = {
930     app_send_io_evt_rx,
931     0,
932     app_send_io_evt_tx,
933 };
934 /* *INDENT-ON* */
935
936 /**
937  * Send event to application
938  *
939  * Logic from queue perspective is non-blocking. That is, if there's
940  * not enough space to enqueue a message, we return. However, if the lock
941  * flag is set, we do wait for queue mutex.
942  */
943 int
944 application_send_event (application_t * app, stream_session_t * s,
945                         u8 evt_type)
946 {
947   ASSERT (app && evt_type <= FIFO_EVENT_APP_TX);
948   return app_send_evt_handler_fns[evt_type] (app, s, 0 /* lock */ );
949 }
950
951 int
952 application_lock_and_send_event (application_t * app, stream_session_t * s,
953                                  u8 evt_type)
954 {
955   return app_send_evt_handler_fns[evt_type] (app, s, 1 /* lock */ );
956 }
957
958 local_session_t *
959 application_alloc_local_session (application_t * app)
960 {
961   local_session_t *s;
962   pool_get (app->local_sessions, s);
963   memset (s, 0, sizeof (*s));
964   s->app_index = app->index;
965   s->session_index = s - app->local_sessions;
966   s->session_type = session_type_from_proto_and_ip (TRANSPORT_PROTO_NONE, 0);
967   return s;
968 }
969
970 void
971 application_free_local_session (application_t * app, local_session_t * s)
972 {
973   pool_put (app->local_sessions, s);
974   if (CLIB_DEBUG)
975     memset (s, 0xfc, sizeof (*s));
976 }
977
978 local_session_t *
979 application_get_local_session (application_t * app, u32 session_index)
980 {
981   if (pool_is_free_index (app->local_sessions, session_index))
982     return 0;
983   return pool_elt_at_index (app->local_sessions, session_index);
984 }
985
986 local_session_t *
987 application_get_local_session_from_handle (session_handle_t handle)
988 {
989   application_t *server;
990   u32 session_index, server_index;
991   local_session_parse_handle (handle, &server_index, &session_index);
992   server = application_get (server_index);
993   return application_get_local_session (server, session_index);
994 }
995
996 always_inline void
997 application_local_listener_session_endpoint (local_session_t * ll,
998                                              session_endpoint_t * sep)
999 {
1000   sep->transport_proto =
1001     session_type_transport_proto (ll->listener_session_type);
1002   sep->port = ll->port;
1003   sep->is_ip4 = ll->listener_session_type & 1;
1004 }
1005
1006 int
1007 application_start_local_listen (application_t * server,
1008                                 session_endpoint_t * sep,
1009                                 session_handle_t * handle)
1010 {
1011   session_handle_t lh;
1012   local_session_t *ll;
1013   u32 table_index;
1014
1015   table_index = application_local_session_table (server);
1016
1017   /* An exact sep match, as opposed to session_lookup_local_listener */
1018   lh = session_lookup_endpoint_listener (table_index, sep, 1);
1019   if (lh != SESSION_INVALID_HANDLE)
1020     return VNET_API_ERROR_ADDRESS_IN_USE;
1021
1022   pool_get (server->local_listen_sessions, ll);
1023   memset (ll, 0, sizeof (*ll));
1024   ll->session_type = session_type_from_proto_and_ip (TRANSPORT_PROTO_NONE, 0);
1025   ll->app_index = server->index;
1026   ll->session_index = ll - server->local_listen_sessions;
1027   ll->port = sep->port;
1028   /* Store the original session type for the unbind */
1029   ll->listener_session_type =
1030     session_type_from_proto_and_ip (sep->transport_proto, sep->is_ip4);
1031   ll->transport_listener_index = ~0;
1032
1033   *handle = application_local_session_handle (ll);
1034   session_lookup_add_session_endpoint (table_index, sep, *handle);
1035
1036   return 0;
1037 }
1038
1039 /**
1040  * Clean up local session table. If we have a listener session use it to
1041  * find the port and proto. If not, the handle must be a local table handle
1042  * so parse it.
1043  */
1044 int
1045 application_stop_local_listen (application_t * server, session_handle_t lh)
1046 {
1047   session_endpoint_t sep = SESSION_ENDPOINT_NULL;
1048   u32 table_index, ll_index, server_index;
1049   stream_session_t *sl = 0;
1050   local_session_t *ll, *ls;
1051
1052   table_index = application_local_session_table (server);
1053
1054   /* We have both local and global table binds. Figure from global what
1055    * the sep we should be cleaning up is.
1056    */
1057   if (!session_handle_is_local (lh))
1058     {
1059       sl = listen_session_get_from_handle (lh);
1060       if (!sl || listen_session_get_local_session_endpoint (sl, &sep))
1061         {
1062           clib_warning ("broken listener");
1063           return -1;
1064         }
1065       lh = session_lookup_endpoint_listener (table_index, &sep, 0);
1066       if (lh == SESSION_INVALID_HANDLE)
1067         return -1;
1068     }
1069
1070   local_session_parse_handle (lh, &server_index, &ll_index);
1071   ASSERT (server->index == server_index);
1072   if (!(ll = application_get_local_listen_session (server, ll_index)))
1073     {
1074       clib_warning ("no local listener");
1075       return -1;
1076     }
1077   application_local_listener_session_endpoint (ll, &sep);
1078   session_lookup_del_session_endpoint (table_index, &sep);
1079
1080   /* *INDENT-OFF* */
1081   pool_foreach (ls, server->local_sessions, ({
1082     if (ls->listener_index == ll->session_index)
1083       application_local_session_disconnect (server->index, ls);
1084   }));
1085   /* *INDENT-ON* */
1086   pool_put_index (server->local_listen_sessions, ll->session_index);
1087
1088   return 0;
1089 }
1090
1091 static void
1092 application_local_session_fix_eventds (svm_msg_q_t * sq, svm_msg_q_t * cq)
1093 {
1094   int fd;
1095
1096   /*
1097    * segment manager initializes only the producer eventds, since vpp is
1098    * typically the producer. But for local sessions, we also pass to the
1099    * apps the mqs they listen on for events from peer apps, so they are also
1100    * consumer fds.
1101    */
1102   fd = svm_msg_q_get_producer_eventfd (sq);
1103   svm_msg_q_set_consumer_eventfd (sq, fd);
1104   fd = svm_msg_q_get_producer_eventfd (cq);
1105   svm_msg_q_set_consumer_eventfd (cq, fd);
1106 }
1107
1108 int
1109 application_local_session_connect (u32 table_index, application_t * client,
1110                                    application_t * server,
1111                                    local_session_t * ll, u32 opaque)
1112 {
1113   u32 seg_size, evt_q_sz, evt_q_elts, margin = 16 << 10;
1114   segment_manager_properties_t *props, *cprops;
1115   u32 round_rx_fifo_sz, round_tx_fifo_sz;
1116   int rv, has_transport, seg_index;
1117   svm_fifo_segment_private_t *seg;
1118   segment_manager_t *sm;
1119   local_session_t *ls;
1120   svm_msg_q_t *sq, *cq;
1121
1122   ls = application_alloc_local_session (server);
1123
1124   props = application_segment_manager_properties (server);
1125   cprops = application_segment_manager_properties (client);
1126   evt_q_elts = props->evt_q_size + cprops->evt_q_size;
1127   evt_q_sz = segment_manager_evt_q_expected_size (evt_q_elts);
1128   round_rx_fifo_sz = 1 << max_log2 (props->rx_fifo_size);
1129   round_tx_fifo_sz = 1 << max_log2 (props->tx_fifo_size);
1130   seg_size = round_rx_fifo_sz + round_tx_fifo_sz + evt_q_sz + margin;
1131
1132   has_transport = session_has_transport ((stream_session_t *) ll);
1133   if (!has_transport)
1134     {
1135       /* Local sessions don't have backing transport */
1136       ls->port = ll->port;
1137       sm = application_get_local_segment_manager (server);
1138     }
1139   else
1140     {
1141       stream_session_t *sl = (stream_session_t *) ll;
1142       transport_connection_t *tc;
1143       tc = listen_session_get_transport (sl);
1144       ls->port = tc->lcl_port;
1145       sm = application_get_listen_segment_manager (server, sl);
1146     }
1147
1148   seg_index = segment_manager_add_segment (sm, seg_size);
1149   if (seg_index < 0)
1150     {
1151       clib_warning ("failed to add new cut-through segment");
1152       return seg_index;
1153     }
1154   seg = segment_manager_get_segment_w_lock (sm, seg_index);
1155   sq = segment_manager_alloc_queue (seg, props);
1156   cq = segment_manager_alloc_queue (seg, cprops);
1157
1158   if (props->use_mq_eventfd)
1159     application_local_session_fix_eventds (sq, cq);
1160
1161   ls->server_evt_q = pointer_to_uword (sq);
1162   ls->client_evt_q = pointer_to_uword (cq);
1163   rv = segment_manager_try_alloc_fifos (seg, props->rx_fifo_size,
1164                                         props->tx_fifo_size,
1165                                         &ls->server_rx_fifo,
1166                                         &ls->server_tx_fifo);
1167   if (rv)
1168     {
1169       clib_warning ("failed to add fifos in cut-through segment");
1170       segment_manager_segment_reader_unlock (sm);
1171       goto failed;
1172     }
1173   ls->server_rx_fifo->master_session_index = ls->session_index;
1174   ls->server_tx_fifo->master_session_index = ls->session_index;
1175   ls->server_rx_fifo->master_thread_index = ~0;
1176   ls->server_tx_fifo->master_thread_index = ~0;
1177   ls->svm_segment_index = seg_index;
1178   ls->listener_index = ll->session_index;
1179   ls->client_index = client->index;
1180   ls->client_opaque = opaque;
1181   ls->listener_session_type = ll->session_type;
1182
1183   if ((rv = server->cb_fns.add_segment_callback (server->api_client_index,
1184                                                  &seg->ssvm)))
1185     {
1186       clib_warning ("failed to notify server of new segment");
1187       segment_manager_segment_reader_unlock (sm);
1188       goto failed;
1189     }
1190   segment_manager_segment_reader_unlock (sm);
1191   if ((rv = server->cb_fns.session_accept_callback ((stream_session_t *) ls)))
1192     {
1193       clib_warning ("failed to send accept cut-through notify to server");
1194       goto failed;
1195     }
1196   if (server->flags & APP_OPTIONS_FLAGS_IS_BUILTIN)
1197     application_local_session_connect_notify (ls);
1198
1199   return 0;
1200
1201 failed:
1202   if (!has_transport)
1203     segment_manager_del_segment (sm, seg);
1204   return rv;
1205 }
1206
1207 static uword
1208 application_client_local_connect_key (local_session_t * ls)
1209 {
1210   return ((uword) ls->app_index << 32 | (uword) ls->session_index);
1211 }
1212
1213 static void
1214 application_client_local_connect_key_parse (uword key, u32 * app_index,
1215                                             u32 * session_index)
1216 {
1217   *app_index = key >> 32;
1218   *session_index = key & 0xFFFFFFFF;
1219 }
1220
1221 int
1222 application_local_session_connect_notify (local_session_t * ls)
1223 {
1224   svm_fifo_segment_private_t *seg;
1225   application_t *client, *server;
1226   segment_manager_t *sm;
1227   int rv, is_fail = 0;
1228   uword client_key;
1229
1230   client = application_get (ls->client_index);
1231   server = application_get (ls->app_index);
1232   sm = application_get_local_segment_manager_w_session (server, ls);
1233   seg = segment_manager_get_segment_w_lock (sm, ls->svm_segment_index);
1234   if ((rv = client->cb_fns.add_segment_callback (client->api_client_index,
1235                                                  &seg->ssvm)))
1236     {
1237       clib_warning ("failed to notify client %u of new segment",
1238                     ls->client_index);
1239       segment_manager_segment_reader_unlock (sm);
1240       application_local_session_disconnect (ls->client_index, ls);
1241       is_fail = 1;
1242     }
1243   else
1244     {
1245       segment_manager_segment_reader_unlock (sm);
1246     }
1247
1248   client->cb_fns.session_connected_callback (client->index, ls->client_opaque,
1249                                              (stream_session_t *) ls,
1250                                              is_fail);
1251
1252   client_key = application_client_local_connect_key (ls);
1253   hash_set (client->local_connects, client_key, client_key);
1254   return 0;
1255 }
1256
1257 int
1258 application_local_session_cleanup (application_t * client,
1259                                    application_t * server,
1260                                    local_session_t * ls)
1261 {
1262   svm_fifo_segment_private_t *seg;
1263   segment_manager_t *sm;
1264   uword client_key;
1265   u8 has_transport;
1266
1267   has_transport = session_has_transport ((stream_session_t *) ls);
1268   client_key = application_client_local_connect_key (ls);
1269   if (!has_transport)
1270     sm = application_get_local_segment_manager_w_session (server, ls);
1271   else
1272     sm = application_get_listen_segment_manager (server,
1273                                                  (stream_session_t *) ls);
1274
1275   seg = segment_manager_get_segment (sm, ls->svm_segment_index);
1276   if (client)
1277     hash_unset (client->local_connects, client_key);
1278
1279   if (!has_transport)
1280     {
1281       server->cb_fns.del_segment_callback (server->api_client_index,
1282                                            &seg->ssvm);
1283       if (client)
1284         client->cb_fns.del_segment_callback (client->api_client_index,
1285                                              &seg->ssvm);
1286       segment_manager_del_segment (sm, seg);
1287     }
1288
1289   application_free_local_session (server, ls);
1290
1291   return 0;
1292 }
1293
1294 int
1295 application_local_session_disconnect (u32 app_index, local_session_t * ls)
1296 {
1297   application_t *client, *server;
1298
1299   client = application_get_if_valid (ls->client_index);
1300   server = application_get (ls->app_index);
1301
1302   if (ls->session_state == SESSION_STATE_CLOSED)
1303     return application_local_session_cleanup (client, server, ls);
1304
1305   if (app_index == ls->client_index)
1306     {
1307       mq_send_local_session_disconnected_cb (ls->app_index, ls);
1308     }
1309   else
1310     {
1311       if (!client)
1312         {
1313           return application_local_session_cleanup (client, server, ls);
1314         }
1315       else if (ls->session_state < SESSION_STATE_READY)
1316         {
1317           client->cb_fns.session_connected_callback (client->index,
1318                                                      ls->client_opaque,
1319                                                      (stream_session_t *) ls,
1320                                                      1 /* is_fail */ );
1321           ls->session_state = SESSION_STATE_CLOSED;
1322           return application_local_session_cleanup (client, server, ls);
1323         }
1324       else
1325         {
1326           mq_send_local_session_disconnected_cb (client->index, ls);
1327         }
1328     }
1329
1330   ls->session_state = SESSION_STATE_CLOSED;
1331
1332   return 0;
1333 }
1334
1335 int
1336 application_local_session_disconnect_w_index (u32 app_index, u32 ls_index)
1337 {
1338   application_t *app;
1339   local_session_t *ls;
1340   app = application_get (app_index);
1341   ls = application_get_local_session (app, ls_index);
1342   return application_local_session_disconnect (app_index, ls);
1343 }
1344
1345 void
1346 application_local_sessions_del (application_t * app)
1347 {
1348   u32 index, server_index, session_index, table_index;
1349   segment_manager_t *sm;
1350   u64 handle, *handles = 0;
1351   local_session_t *ls, *ll;
1352   application_t *server;
1353   session_endpoint_t sep;
1354   int i;
1355
1356   /*
1357    * Local listens. Don't bother with local sessions, we clean them lower
1358    */
1359   table_index = application_local_session_table (app);
1360   /* *INDENT-OFF* */
1361   pool_foreach (ll, app->local_listen_sessions, ({
1362     application_local_listener_session_endpoint (ll, &sep);
1363     session_lookup_del_session_endpoint (table_index, &sep);
1364   }));
1365   /* *INDENT-ON* */
1366
1367   /*
1368    * Local sessions
1369    */
1370   if (app->local_sessions)
1371     {
1372       /* *INDENT-OFF* */
1373       pool_foreach (ls, app->local_sessions, ({
1374         application_local_session_disconnect (app->index, ls);
1375       }));
1376       /* *INDENT-ON* */
1377     }
1378
1379   /*
1380    * Local connects
1381    */
1382   vec_reset_length (handles);
1383   /* *INDENT-OFF* */
1384   hash_foreach (handle, index, app->local_connects, ({
1385     vec_add1 (handles, handle);
1386   }));
1387   /* *INDENT-ON* */
1388
1389   for (i = 0; i < vec_len (handles); i++)
1390     {
1391       application_client_local_connect_key_parse (handles[i], &server_index,
1392                                                   &session_index);
1393       server = application_get_if_valid (server_index);
1394       if (server)
1395         {
1396           ls = application_get_local_session (server, session_index);
1397           application_local_session_disconnect (app->index, ls);
1398         }
1399     }
1400
1401   sm = segment_manager_get (app->local_segment_manager);
1402   sm->app_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
1403   segment_manager_del (sm);
1404 }
1405
1406 clib_error_t *
1407 vnet_app_add_tls_cert (vnet_app_add_tls_cert_args_t * a)
1408 {
1409   application_t *app;
1410   app = application_get (a->app_index);
1411   if (!app)
1412     return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1413                                    0, "app %u doesn't exist", a->app_index);
1414   app->tls_cert = vec_dup (a->cert);
1415   return 0;
1416 }
1417
1418 clib_error_t *
1419 vnet_app_add_tls_key (vnet_app_add_tls_key_args_t * a)
1420 {
1421   application_t *app;
1422   app = application_get (a->app_index);
1423   if (!app)
1424     return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1425                                    0, "app %u doesn't exist", a->app_index);
1426   app->tls_key = vec_dup (a->key);
1427   return 0;
1428 }
1429
1430 u8 *
1431 format_application_listener (u8 * s, va_list * args)
1432 {
1433   application_t *app = va_arg (*args, application_t *);
1434   u64 handle = va_arg (*args, u64);
1435   u32 sm_index = va_arg (*args, u32);
1436   int verbose = va_arg (*args, int);
1437   stream_session_t *listener;
1438   u8 *app_name, *str;
1439
1440   if (app == 0)
1441     {
1442       if (verbose)
1443         s = format (s, "%-40s%-20s%-15s%-15s%-10s", "Connection", "App",
1444                     "API Client", "ListenerID", "SegManager");
1445       else
1446         s = format (s, "%-40s%-20s", "Connection", "App");
1447
1448       return s;
1449     }
1450
1451   app_name = app_get_name_from_reg_index (app);
1452   listener = listen_session_get_from_handle (handle);
1453   str = format (0, "%U", format_stream_session, listener, verbose);
1454
1455   if (verbose)
1456     {
1457       s = format (s, "%-40s%-20s%-15u%-15u%-10u", str, app_name,
1458                   app->api_client_index, handle, sm_index);
1459     }
1460   else
1461     s = format (s, "%-40s%-20s", str, app_name);
1462
1463   vec_free (app_name);
1464   return s;
1465 }
1466
1467 void
1468 application_format_connects (application_t * app, int verbose)
1469 {
1470   svm_fifo_segment_private_t *fifo_segment;
1471   vlib_main_t *vm = vlib_get_main ();
1472   segment_manager_t *sm;
1473   u8 *app_name, *s = 0;
1474
1475   /* Header */
1476   if (app == 0)
1477     {
1478       if (verbose)
1479         vlib_cli_output (vm, "%-40s%-20s%-15s%-10s", "Connection", "App",
1480                          "API Client", "SegManager");
1481       else
1482         vlib_cli_output (vm, "%-40s%-20s", "Connection", "App");
1483       return;
1484     }
1485
1486   /* make sure */
1487   if (app->connects_seg_manager == (u32) ~ 0)
1488     return;
1489
1490   app_name = app_get_name_from_reg_index (app);
1491
1492   /* Across all fifo segments */
1493   sm = segment_manager_get (app->connects_seg_manager);
1494
1495   /* *INDENT-OFF* */
1496   segment_manager_foreach_segment_w_lock (fifo_segment, sm, ({
1497     svm_fifo_t *fifo;
1498     u8 *str;
1499
1500     fifo = svm_fifo_segment_get_fifo_list (fifo_segment);
1501     while (fifo)
1502         {
1503           u32 session_index, thread_index;
1504           stream_session_t *session;
1505
1506           session_index = fifo->master_session_index;
1507           thread_index = fifo->master_thread_index;
1508
1509           session = session_get (session_index, thread_index);
1510           str = format (0, "%U", format_stream_session, session, verbose);
1511
1512           if (verbose)
1513             s = format (s, "%-40s%-20s%-15u%-10u", str, app_name,
1514                         app->api_client_index, app->connects_seg_manager);
1515           else
1516             s = format (s, "%-40s%-20s", str, app_name);
1517
1518           vlib_cli_output (vm, "%v", s);
1519           vec_reset_length (s);
1520           vec_free (str);
1521
1522           fifo = fifo->next;
1523         }
1524     vec_free (s);
1525   }));
1526   /* *INDENT-ON* */
1527
1528   vec_free (app_name);
1529 }
1530
1531 void
1532 application_format_local_sessions (application_t * app, int verbose)
1533 {
1534   vlib_main_t *vm = vlib_get_main ();
1535   local_session_t *ls;
1536   transport_proto_t tp;
1537   u8 *conn = 0;
1538
1539   /* Header */
1540   if (app == 0)
1541     {
1542       vlib_cli_output (vm, "%-40s%-15s%-20s", "Connection", "ServerApp",
1543                        "ClientApp");
1544       return;
1545     }
1546
1547   /* *INDENT-OFF* */
1548   pool_foreach (ls, app->local_listen_sessions, ({
1549     tp = session_type_transport_proto(ls->listener_session_type);
1550     conn = format (0, "[L][%U] *:%u", format_transport_proto_short, tp,
1551                    ls->port);
1552     vlib_cli_output (vm, "%-40v%-15u%-20s", conn, ls->app_index, "*");
1553     vec_reset_length (conn);
1554   }));
1555   pool_foreach (ls, app->local_sessions, ({
1556     tp = session_type_transport_proto(ls->listener_session_type);
1557     conn = format (0, "[L][%U] *:%u", format_transport_proto_short, tp,
1558                    ls->port);
1559     vlib_cli_output (vm, "%-40v%-15u%-20u", conn, ls->app_index,
1560                      ls->client_index);
1561     vec_reset_length (conn);
1562   }));
1563   /* *INDENT-ON* */
1564
1565   vec_free (conn);
1566 }
1567
1568 void
1569 application_format_local_connects (application_t * app, int verbose)
1570 {
1571   vlib_main_t *vm = vlib_get_main ();
1572   u32 app_index, session_index;
1573   application_t *server;
1574   local_session_t *ls;
1575   uword client_key;
1576   u64 value;
1577
1578   /* Header */
1579   if (app == 0)
1580     {
1581       if (verbose)
1582         vlib_cli_output (vm, "%-40s%-15s%-20s%-10s", "Connection", "App",
1583                          "Peer App", "SegManager");
1584       else
1585         vlib_cli_output (vm, "%-40s%-15s%-20s", "Connection", "App",
1586                          "Peer App");
1587       return;
1588     }
1589
1590   /* *INDENT-OFF* */
1591   hash_foreach (client_key, value, app->local_connects, ({
1592     application_client_local_connect_key_parse (client_key, &app_index,
1593                                                 &session_index);
1594     server = application_get (app_index);
1595     ls = application_get_local_session (server, session_index);
1596     vlib_cli_output (vm, "%-40s%-15s%-20s", "TODO", ls->app_index, ls->client_index);
1597   }));
1598   /* *INDENT-ON* */
1599 }
1600
1601 u8 *
1602 format_application (u8 * s, va_list * args)
1603 {
1604   application_t *app = va_arg (*args, application_t *);
1605   CLIB_UNUSED (int verbose) = va_arg (*args, int);
1606   segment_manager_properties_t *props;
1607   const u8 *app_ns_name;
1608   u8 *app_name;
1609
1610   if (app == 0)
1611     {
1612       if (verbose)
1613         s = format (s, "%-10s%-20s%-15s%-15s%-15s%-15s%-15s", "Index", "Name",
1614                     "API Client", "Namespace", "Add seg size", "Rx-f size",
1615                     "Tx-f size");
1616       else
1617         s = format (s, "%-10s%-20s%-15s%-40s", "Index", "Name", "API Client",
1618                     "Namespace");
1619       return s;
1620     }
1621
1622   app_name = app_get_name (app);
1623   app_ns_name = app_namespace_id_from_index (app->ns_index);
1624   props = application_segment_manager_properties (app);
1625   if (verbose)
1626     s = format (s, "%-10u%-20s%-15d%-15u%-15U%-15U%-15U", app->index,
1627                 app_name, app->api_client_index, app->ns_index,
1628                 format_memory_size, props->add_segment_size,
1629                 format_memory_size, props->rx_fifo_size, format_memory_size,
1630                 props->tx_fifo_size);
1631   else
1632     s = format (s, "%-10u%-20s%-15d%-40s", app->index, app_name,
1633                 app->api_client_index, app_ns_name);
1634   return s;
1635 }
1636
1637
1638 void
1639 application_format_all_listeners (vlib_main_t * vm, int do_local, int verbose)
1640 {
1641   application_t *app;
1642   u32 sm_index;
1643   u64 handle;
1644
1645   if (!pool_elts (app_pool))
1646     {
1647       vlib_cli_output (vm, "No active server bindings");
1648       return;
1649     }
1650
1651   if (do_local)
1652     {
1653       application_format_local_sessions (0, verbose);
1654       /* *INDENT-OFF* */
1655       pool_foreach (app, app_pool, ({
1656         if (!pool_elts (app->local_sessions)
1657             && !pool_elts(app->local_connects))
1658           continue;
1659         application_format_local_sessions (app, verbose);
1660       }));
1661       /* *INDENT-ON* */
1662     }
1663   else
1664     {
1665       vlib_cli_output (vm, "%U", format_application_listener, 0 /* header */ ,
1666                        0, 0, verbose);
1667
1668       /* *INDENT-OFF* */
1669       pool_foreach (app, app_pool, ({
1670         if (hash_elts (app->listeners_table) == 0)
1671           continue;
1672         hash_foreach (handle, sm_index, app->listeners_table, ({
1673           vlib_cli_output (vm, "%U", format_application_listener, app,
1674                            handle, sm_index, verbose);
1675         }));
1676       }));
1677       /* *INDENT-ON* */
1678     }
1679 }
1680
1681 void
1682 application_format_all_clients (vlib_main_t * vm, int do_local, int verbose)
1683 {
1684   application_t *app;
1685
1686   if (!pool_elts (app_pool))
1687     {
1688       vlib_cli_output (vm, "No active apps");
1689       return;
1690     }
1691
1692   if (do_local)
1693     {
1694       application_format_local_connects (0, verbose);
1695
1696       /* *INDENT-OFF* */
1697       pool_foreach (app, app_pool, ({
1698         if (app->local_connects)
1699           application_format_local_connects (app, verbose);
1700       }));
1701       /* *INDENT-ON* */
1702     }
1703   else
1704     {
1705       application_format_connects (0, verbose);
1706
1707       /* *INDENT-OFF* */
1708       pool_foreach (app, app_pool, ({
1709         if (app->connects_seg_manager == (u32)~0)
1710           continue;
1711         application_format_connects (app, verbose);
1712       }));
1713       /* *INDENT-ON* */
1714     }
1715 }
1716
1717 static clib_error_t *
1718 show_app_command_fn (vlib_main_t * vm, unformat_input_t * input,
1719                      vlib_cli_command_t * cmd)
1720 {
1721   int do_server = 0, do_client = 0, do_local = 0;
1722   application_t *app;
1723   int verbose = 0;
1724
1725   session_cli_return_if_not_enabled ();
1726
1727   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1728     {
1729       if (unformat (input, "server"))
1730         do_server = 1;
1731       else if (unformat (input, "client"))
1732         do_client = 1;
1733       else if (unformat (input, "local"))
1734         do_local = 1;
1735       else if (unformat (input, "verbose"))
1736         verbose = 1;
1737       else
1738         break;
1739     }
1740
1741   if (do_server)
1742     application_format_all_listeners (vm, do_local, verbose);
1743
1744   if (do_client)
1745     application_format_all_clients (vm, do_local, verbose);
1746
1747   /* Print app related info */
1748   if (!do_server && !do_client)
1749     {
1750       vlib_cli_output (vm, "%U", format_application, 0, verbose);
1751       /* *INDENT-OFF* */
1752       pool_foreach (app, app_pool, ({
1753         vlib_cli_output (vm, "%U", format_application, app, verbose);
1754       }));
1755       /* *INDENT-ON* */
1756     }
1757
1758   return 0;
1759 }
1760
1761 /* *INDENT-OFF* */
1762 VLIB_CLI_COMMAND (show_app_command, static) =
1763 {
1764   .path = "show app",
1765   .short_help = "show app [server|client] [verbose]",
1766   .function = show_app_command_fn,
1767 };
1768 /* *INDENT-ON* */
1769
1770 /*
1771  * fd.io coding-style-patch-verification: ON
1772  *
1773  * Local Variables:
1774  * eval: (c-set-style "gnu")
1775  * End:
1776  */