session: fix retrieval of local sessions if app is detached
[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_if_valid (server_index);
993   if (!server)
994     return 0;
995   return application_get_local_session (server, session_index);
996 }
997
998 always_inline void
999 application_local_listener_session_endpoint (local_session_t * ll,
1000                                              session_endpoint_t * sep)
1001 {
1002   sep->transport_proto =
1003     session_type_transport_proto (ll->listener_session_type);
1004   sep->port = ll->port;
1005   sep->is_ip4 = ll->listener_session_type & 1;
1006 }
1007
1008 int
1009 application_start_local_listen (application_t * server,
1010                                 session_endpoint_t * sep,
1011                                 session_handle_t * handle)
1012 {
1013   session_handle_t lh;
1014   local_session_t *ll;
1015   u32 table_index;
1016
1017   table_index = application_local_session_table (server);
1018
1019   /* An exact sep match, as opposed to session_lookup_local_listener */
1020   lh = session_lookup_endpoint_listener (table_index, sep, 1);
1021   if (lh != SESSION_INVALID_HANDLE)
1022     return VNET_API_ERROR_ADDRESS_IN_USE;
1023
1024   pool_get (server->local_listen_sessions, ll);
1025   memset (ll, 0, sizeof (*ll));
1026   ll->session_type = session_type_from_proto_and_ip (TRANSPORT_PROTO_NONE, 0);
1027   ll->app_index = server->index;
1028   ll->session_index = ll - server->local_listen_sessions;
1029   ll->port = sep->port;
1030   /* Store the original session type for the unbind */
1031   ll->listener_session_type =
1032     session_type_from_proto_and_ip (sep->transport_proto, sep->is_ip4);
1033   ll->transport_listener_index = ~0;
1034
1035   *handle = application_local_session_handle (ll);
1036   session_lookup_add_session_endpoint (table_index, sep, *handle);
1037
1038   return 0;
1039 }
1040
1041 /**
1042  * Clean up local session table. If we have a listener session use it to
1043  * find the port and proto. If not, the handle must be a local table handle
1044  * so parse it.
1045  */
1046 int
1047 application_stop_local_listen (application_t * server, session_handle_t lh)
1048 {
1049   session_endpoint_t sep = SESSION_ENDPOINT_NULL;
1050   u32 table_index, ll_index, server_index;
1051   stream_session_t *sl = 0;
1052   local_session_t *ll, *ls;
1053
1054   table_index = application_local_session_table (server);
1055
1056   /* We have both local and global table binds. Figure from global what
1057    * the sep we should be cleaning up is.
1058    */
1059   if (!session_handle_is_local (lh))
1060     {
1061       sl = listen_session_get_from_handle (lh);
1062       if (!sl || listen_session_get_local_session_endpoint (sl, &sep))
1063         {
1064           clib_warning ("broken listener");
1065           return -1;
1066         }
1067       lh = session_lookup_endpoint_listener (table_index, &sep, 0);
1068       if (lh == SESSION_INVALID_HANDLE)
1069         return -1;
1070     }
1071
1072   local_session_parse_handle (lh, &server_index, &ll_index);
1073   ASSERT (server->index == server_index);
1074   if (!(ll = application_get_local_listen_session (server, ll_index)))
1075     {
1076       clib_warning ("no local listener");
1077       return -1;
1078     }
1079   application_local_listener_session_endpoint (ll, &sep);
1080   session_lookup_del_session_endpoint (table_index, &sep);
1081
1082   /* *INDENT-OFF* */
1083   pool_foreach (ls, server->local_sessions, ({
1084     if (ls->listener_index == ll->session_index)
1085       application_local_session_disconnect (server->index, ls);
1086   }));
1087   /* *INDENT-ON* */
1088   pool_put_index (server->local_listen_sessions, ll->session_index);
1089
1090   return 0;
1091 }
1092
1093 static void
1094 application_local_session_fix_eventds (svm_msg_q_t * sq, svm_msg_q_t * cq)
1095 {
1096   int fd;
1097
1098   /*
1099    * segment manager initializes only the producer eventds, since vpp is
1100    * typically the producer. But for local sessions, we also pass to the
1101    * apps the mqs they listen on for events from peer apps, so they are also
1102    * consumer fds.
1103    */
1104   fd = svm_msg_q_get_producer_eventfd (sq);
1105   svm_msg_q_set_consumer_eventfd (sq, fd);
1106   fd = svm_msg_q_get_producer_eventfd (cq);
1107   svm_msg_q_set_consumer_eventfd (cq, fd);
1108 }
1109
1110 int
1111 application_local_session_connect (u32 table_index, application_t * client,
1112                                    application_t * server,
1113                                    local_session_t * ll, u32 opaque)
1114 {
1115   u32 seg_size, evt_q_sz, evt_q_elts, margin = 16 << 10;
1116   segment_manager_properties_t *props, *cprops;
1117   u32 round_rx_fifo_sz, round_tx_fifo_sz;
1118   int rv, has_transport, seg_index;
1119   svm_fifo_segment_private_t *seg;
1120   segment_manager_t *sm;
1121   local_session_t *ls;
1122   svm_msg_q_t *sq, *cq;
1123
1124   ls = application_alloc_local_session (server);
1125
1126   props = application_segment_manager_properties (server);
1127   cprops = application_segment_manager_properties (client);
1128   evt_q_elts = props->evt_q_size + cprops->evt_q_size;
1129   evt_q_sz = segment_manager_evt_q_expected_size (evt_q_elts);
1130   round_rx_fifo_sz = 1 << max_log2 (props->rx_fifo_size);
1131   round_tx_fifo_sz = 1 << max_log2 (props->tx_fifo_size);
1132   seg_size = round_rx_fifo_sz + round_tx_fifo_sz + evt_q_sz + margin;
1133
1134   has_transport = session_has_transport ((stream_session_t *) ll);
1135   if (!has_transport)
1136     {
1137       /* Local sessions don't have backing transport */
1138       ls->port = ll->port;
1139       sm = application_get_local_segment_manager (server);
1140     }
1141   else
1142     {
1143       stream_session_t *sl = (stream_session_t *) ll;
1144       transport_connection_t *tc;
1145       tc = listen_session_get_transport (sl);
1146       ls->port = tc->lcl_port;
1147       sm = application_get_listen_segment_manager (server, sl);
1148     }
1149
1150   seg_index = segment_manager_add_segment (sm, seg_size);
1151   if (seg_index < 0)
1152     {
1153       clib_warning ("failed to add new cut-through segment");
1154       return seg_index;
1155     }
1156   seg = segment_manager_get_segment_w_lock (sm, seg_index);
1157   sq = segment_manager_alloc_queue (seg, props);
1158   cq = segment_manager_alloc_queue (seg, cprops);
1159
1160   if (props->use_mq_eventfd)
1161     application_local_session_fix_eventds (sq, cq);
1162
1163   ls->server_evt_q = pointer_to_uword (sq);
1164   ls->client_evt_q = pointer_to_uword (cq);
1165   rv = segment_manager_try_alloc_fifos (seg, props->rx_fifo_size,
1166                                         props->tx_fifo_size,
1167                                         &ls->server_rx_fifo,
1168                                         &ls->server_tx_fifo);
1169   if (rv)
1170     {
1171       clib_warning ("failed to add fifos in cut-through segment");
1172       segment_manager_segment_reader_unlock (sm);
1173       goto failed;
1174     }
1175   ls->server_rx_fifo->master_session_index = ls->session_index;
1176   ls->server_tx_fifo->master_session_index = ls->session_index;
1177   ls->server_rx_fifo->master_thread_index = ~0;
1178   ls->server_tx_fifo->master_thread_index = ~0;
1179   ls->svm_segment_index = seg_index;
1180   ls->listener_index = ll->session_index;
1181   ls->client_index = client->index;
1182   ls->client_opaque = opaque;
1183   ls->listener_session_type = ll->session_type;
1184
1185   if ((rv = server->cb_fns.add_segment_callback (server->api_client_index,
1186                                                  &seg->ssvm)))
1187     {
1188       clib_warning ("failed to notify server of new segment");
1189       segment_manager_segment_reader_unlock (sm);
1190       goto failed;
1191     }
1192   segment_manager_segment_reader_unlock (sm);
1193   if ((rv = server->cb_fns.session_accept_callback ((stream_session_t *) ls)))
1194     {
1195       clib_warning ("failed to send accept cut-through notify to server");
1196       goto failed;
1197     }
1198   if (server->flags & APP_OPTIONS_FLAGS_IS_BUILTIN)
1199     application_local_session_connect_notify (ls);
1200
1201   return 0;
1202
1203 failed:
1204   if (!has_transport)
1205     segment_manager_del_segment (sm, seg);
1206   return rv;
1207 }
1208
1209 static uword
1210 application_client_local_connect_key (local_session_t * ls)
1211 {
1212   return ((uword) ls->app_index << 32 | (uword) ls->session_index);
1213 }
1214
1215 static void
1216 application_client_local_connect_key_parse (uword key, u32 * app_index,
1217                                             u32 * session_index)
1218 {
1219   *app_index = key >> 32;
1220   *session_index = key & 0xFFFFFFFF;
1221 }
1222
1223 int
1224 application_local_session_connect_notify (local_session_t * ls)
1225 {
1226   svm_fifo_segment_private_t *seg;
1227   application_t *client, *server;
1228   segment_manager_t *sm;
1229   int rv, is_fail = 0;
1230   uword client_key;
1231
1232   client = application_get (ls->client_index);
1233   server = application_get (ls->app_index);
1234   sm = application_get_local_segment_manager_w_session (server, ls);
1235   seg = segment_manager_get_segment_w_lock (sm, ls->svm_segment_index);
1236   if ((rv = client->cb_fns.add_segment_callback (client->api_client_index,
1237                                                  &seg->ssvm)))
1238     {
1239       clib_warning ("failed to notify client %u of new segment",
1240                     ls->client_index);
1241       segment_manager_segment_reader_unlock (sm);
1242       application_local_session_disconnect (ls->client_index, ls);
1243       is_fail = 1;
1244     }
1245   else
1246     {
1247       segment_manager_segment_reader_unlock (sm);
1248     }
1249
1250   client->cb_fns.session_connected_callback (client->index, ls->client_opaque,
1251                                              (stream_session_t *) ls,
1252                                              is_fail);
1253
1254   client_key = application_client_local_connect_key (ls);
1255   hash_set (client->local_connects, client_key, client_key);
1256   return 0;
1257 }
1258
1259 int
1260 application_local_session_cleanup (application_t * client,
1261                                    application_t * server,
1262                                    local_session_t * ls)
1263 {
1264   svm_fifo_segment_private_t *seg;
1265   segment_manager_t *sm;
1266   uword client_key;
1267   u8 has_transport;
1268
1269   has_transport = session_has_transport ((stream_session_t *) ls);
1270   client_key = application_client_local_connect_key (ls);
1271   if (!has_transport)
1272     sm = application_get_local_segment_manager_w_session (server, ls);
1273   else
1274     sm = application_get_listen_segment_manager (server,
1275                                                  (stream_session_t *) ls);
1276
1277   seg = segment_manager_get_segment (sm, ls->svm_segment_index);
1278   if (client)
1279     hash_unset (client->local_connects, client_key);
1280
1281   if (!has_transport)
1282     {
1283       server->cb_fns.del_segment_callback (server->api_client_index,
1284                                            &seg->ssvm);
1285       if (client)
1286         client->cb_fns.del_segment_callback (client->api_client_index,
1287                                              &seg->ssvm);
1288       segment_manager_del_segment (sm, seg);
1289     }
1290
1291   application_free_local_session (server, ls);
1292
1293   return 0;
1294 }
1295
1296 int
1297 application_local_session_disconnect (u32 app_index, local_session_t * ls)
1298 {
1299   application_t *client, *server;
1300
1301   client = application_get_if_valid (ls->client_index);
1302   server = application_get (ls->app_index);
1303
1304   if (ls->session_state == SESSION_STATE_CLOSED)
1305     return application_local_session_cleanup (client, server, ls);
1306
1307   if (app_index == ls->client_index)
1308     {
1309       mq_send_local_session_disconnected_cb (ls->app_index, ls);
1310     }
1311   else
1312     {
1313       if (!client)
1314         {
1315           return application_local_session_cleanup (client, server, ls);
1316         }
1317       else if (ls->session_state < SESSION_STATE_READY)
1318         {
1319           client->cb_fns.session_connected_callback (client->index,
1320                                                      ls->client_opaque,
1321                                                      (stream_session_t *) ls,
1322                                                      1 /* is_fail */ );
1323           ls->session_state = SESSION_STATE_CLOSED;
1324           return application_local_session_cleanup (client, server, ls);
1325         }
1326       else
1327         {
1328           mq_send_local_session_disconnected_cb (client->index, ls);
1329         }
1330     }
1331
1332   ls->session_state = SESSION_STATE_CLOSED;
1333
1334   return 0;
1335 }
1336
1337 int
1338 application_local_session_disconnect_w_index (u32 app_index, u32 ls_index)
1339 {
1340   application_t *app;
1341   local_session_t *ls;
1342   app = application_get (app_index);
1343   ls = application_get_local_session (app, ls_index);
1344   return application_local_session_disconnect (app_index, ls);
1345 }
1346
1347 void
1348 application_local_sessions_del (application_t * app)
1349 {
1350   u32 index, server_index, session_index, table_index;
1351   segment_manager_t *sm;
1352   u64 handle, *handles = 0;
1353   local_session_t *ls, *ll;
1354   application_t *server;
1355   session_endpoint_t sep;
1356   int i;
1357
1358   /*
1359    * Local listens. Don't bother with local sessions, we clean them lower
1360    */
1361   table_index = application_local_session_table (app);
1362   /* *INDENT-OFF* */
1363   pool_foreach (ll, app->local_listen_sessions, ({
1364     application_local_listener_session_endpoint (ll, &sep);
1365     session_lookup_del_session_endpoint (table_index, &sep);
1366   }));
1367   /* *INDENT-ON* */
1368
1369   /*
1370    * Local sessions
1371    */
1372   if (app->local_sessions)
1373     {
1374       /* *INDENT-OFF* */
1375       pool_foreach (ls, app->local_sessions, ({
1376         application_local_session_disconnect (app->index, ls);
1377       }));
1378       /* *INDENT-ON* */
1379     }
1380
1381   /*
1382    * Local connects
1383    */
1384   vec_reset_length (handles);
1385   /* *INDENT-OFF* */
1386   hash_foreach (handle, index, app->local_connects, ({
1387     vec_add1 (handles, handle);
1388   }));
1389   /* *INDENT-ON* */
1390
1391   for (i = 0; i < vec_len (handles); i++)
1392     {
1393       application_client_local_connect_key_parse (handles[i], &server_index,
1394                                                   &session_index);
1395       server = application_get_if_valid (server_index);
1396       if (server)
1397         {
1398           ls = application_get_local_session (server, session_index);
1399           application_local_session_disconnect (app->index, ls);
1400         }
1401     }
1402
1403   sm = segment_manager_get (app->local_segment_manager);
1404   sm->app_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
1405   segment_manager_del (sm);
1406 }
1407
1408 clib_error_t *
1409 vnet_app_add_tls_cert (vnet_app_add_tls_cert_args_t * a)
1410 {
1411   application_t *app;
1412   app = application_get (a->app_index);
1413   if (!app)
1414     return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1415                                    0, "app %u doesn't exist", a->app_index);
1416   app->tls_cert = vec_dup (a->cert);
1417   return 0;
1418 }
1419
1420 clib_error_t *
1421 vnet_app_add_tls_key (vnet_app_add_tls_key_args_t * a)
1422 {
1423   application_t *app;
1424   app = application_get (a->app_index);
1425   if (!app)
1426     return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1427                                    0, "app %u doesn't exist", a->app_index);
1428   app->tls_key = vec_dup (a->key);
1429   return 0;
1430 }
1431
1432 u8 *
1433 format_application_listener (u8 * s, va_list * args)
1434 {
1435   application_t *app = va_arg (*args, application_t *);
1436   u64 handle = va_arg (*args, u64);
1437   u32 sm_index = va_arg (*args, u32);
1438   int verbose = va_arg (*args, int);
1439   stream_session_t *listener;
1440   u8 *app_name, *str;
1441
1442   if (app == 0)
1443     {
1444       if (verbose)
1445         s = format (s, "%-40s%-20s%-15s%-15s%-10s", "Connection", "App",
1446                     "API Client", "ListenerID", "SegManager");
1447       else
1448         s = format (s, "%-40s%-20s", "Connection", "App");
1449
1450       return s;
1451     }
1452
1453   app_name = app_get_name_from_reg_index (app);
1454   listener = listen_session_get_from_handle (handle);
1455   str = format (0, "%U", format_stream_session, listener, verbose);
1456
1457   if (verbose)
1458     {
1459       s = format (s, "%-40s%-20s%-15u%-15u%-10u", str, app_name,
1460                   app->api_client_index, handle, sm_index);
1461     }
1462   else
1463     s = format (s, "%-40s%-20s", str, app_name);
1464
1465   vec_free (app_name);
1466   return s;
1467 }
1468
1469 void
1470 application_format_connects (application_t * app, int verbose)
1471 {
1472   svm_fifo_segment_private_t *fifo_segment;
1473   vlib_main_t *vm = vlib_get_main ();
1474   segment_manager_t *sm;
1475   u8 *app_name, *s = 0;
1476
1477   /* Header */
1478   if (app == 0)
1479     {
1480       if (verbose)
1481         vlib_cli_output (vm, "%-40s%-20s%-15s%-10s", "Connection", "App",
1482                          "API Client", "SegManager");
1483       else
1484         vlib_cli_output (vm, "%-40s%-20s", "Connection", "App");
1485       return;
1486     }
1487
1488   /* make sure */
1489   if (app->connects_seg_manager == (u32) ~ 0)
1490     return;
1491
1492   app_name = app_get_name_from_reg_index (app);
1493
1494   /* Across all fifo segments */
1495   sm = segment_manager_get (app->connects_seg_manager);
1496
1497   /* *INDENT-OFF* */
1498   segment_manager_foreach_segment_w_lock (fifo_segment, sm, ({
1499     svm_fifo_t *fifo;
1500     u8 *str;
1501
1502     fifo = svm_fifo_segment_get_fifo_list (fifo_segment);
1503     while (fifo)
1504         {
1505           u32 session_index, thread_index;
1506           stream_session_t *session;
1507
1508           session_index = fifo->master_session_index;
1509           thread_index = fifo->master_thread_index;
1510
1511           session = session_get (session_index, thread_index);
1512           str = format (0, "%U", format_stream_session, session, verbose);
1513
1514           if (verbose)
1515             s = format (s, "%-40s%-20s%-15u%-10u", str, app_name,
1516                         app->api_client_index, app->connects_seg_manager);
1517           else
1518             s = format (s, "%-40s%-20s", str, app_name);
1519
1520           vlib_cli_output (vm, "%v", s);
1521           vec_reset_length (s);
1522           vec_free (str);
1523
1524           fifo = fifo->next;
1525         }
1526     vec_free (s);
1527   }));
1528   /* *INDENT-ON* */
1529
1530   vec_free (app_name);
1531 }
1532
1533 void
1534 application_format_local_sessions (application_t * app, int verbose)
1535 {
1536   vlib_main_t *vm = vlib_get_main ();
1537   local_session_t *ls;
1538   transport_proto_t tp;
1539   u8 *conn = 0;
1540
1541   /* Header */
1542   if (app == 0)
1543     {
1544       vlib_cli_output (vm, "%-40s%-15s%-20s", "Connection", "ServerApp",
1545                        "ClientApp");
1546       return;
1547     }
1548
1549   /* *INDENT-OFF* */
1550   pool_foreach (ls, app->local_listen_sessions, ({
1551     tp = session_type_transport_proto(ls->listener_session_type);
1552     conn = format (0, "[L][%U] *:%u", format_transport_proto_short, tp,
1553                    ls->port);
1554     vlib_cli_output (vm, "%-40v%-15u%-20s", conn, ls->app_index, "*");
1555     vec_reset_length (conn);
1556   }));
1557   pool_foreach (ls, app->local_sessions, ({
1558     tp = session_type_transport_proto(ls->listener_session_type);
1559     conn = format (0, "[L][%U] *:%u", format_transport_proto_short, tp,
1560                    ls->port);
1561     vlib_cli_output (vm, "%-40v%-15u%-20u", conn, ls->app_index,
1562                      ls->client_index);
1563     vec_reset_length (conn);
1564   }));
1565   /* *INDENT-ON* */
1566
1567   vec_free (conn);
1568 }
1569
1570 void
1571 application_format_local_connects (application_t * app, int verbose)
1572 {
1573   vlib_main_t *vm = vlib_get_main ();
1574   u32 app_index, session_index;
1575   application_t *server;
1576   local_session_t *ls;
1577   uword client_key;
1578   u64 value;
1579
1580   /* Header */
1581   if (app == 0)
1582     {
1583       if (verbose)
1584         vlib_cli_output (vm, "%-40s%-15s%-20s%-10s", "Connection", "App",
1585                          "Peer App", "SegManager");
1586       else
1587         vlib_cli_output (vm, "%-40s%-15s%-20s", "Connection", "App",
1588                          "Peer App");
1589       return;
1590     }
1591
1592   /* *INDENT-OFF* */
1593   hash_foreach (client_key, value, app->local_connects, ({
1594     application_client_local_connect_key_parse (client_key, &app_index,
1595                                                 &session_index);
1596     server = application_get (app_index);
1597     ls = application_get_local_session (server, session_index);
1598     vlib_cli_output (vm, "%-40s%-15s%-20s", "TODO", ls->app_index, ls->client_index);
1599   }));
1600   /* *INDENT-ON* */
1601 }
1602
1603 u8 *
1604 format_application (u8 * s, va_list * args)
1605 {
1606   application_t *app = va_arg (*args, application_t *);
1607   CLIB_UNUSED (int verbose) = va_arg (*args, int);
1608   segment_manager_properties_t *props;
1609   const u8 *app_ns_name;
1610   u8 *app_name;
1611
1612   if (app == 0)
1613     {
1614       if (verbose)
1615         s = format (s, "%-10s%-20s%-15s%-15s%-15s%-15s%-15s", "Index", "Name",
1616                     "API Client", "Namespace", "Add seg size", "Rx-f size",
1617                     "Tx-f size");
1618       else
1619         s = format (s, "%-10s%-20s%-15s%-40s", "Index", "Name", "API Client",
1620                     "Namespace");
1621       return s;
1622     }
1623
1624   app_name = app_get_name (app);
1625   app_ns_name = app_namespace_id_from_index (app->ns_index);
1626   props = application_segment_manager_properties (app);
1627   if (verbose)
1628     s = format (s, "%-10u%-20s%-15d%-15u%-15U%-15U%-15U", app->index,
1629                 app_name, app->api_client_index, app->ns_index,
1630                 format_memory_size, props->add_segment_size,
1631                 format_memory_size, props->rx_fifo_size, format_memory_size,
1632                 props->tx_fifo_size);
1633   else
1634     s = format (s, "%-10u%-20s%-15d%-40s", app->index, app_name,
1635                 app->api_client_index, app_ns_name);
1636   return s;
1637 }
1638
1639
1640 void
1641 application_format_all_listeners (vlib_main_t * vm, int do_local, int verbose)
1642 {
1643   application_t *app;
1644   u32 sm_index;
1645   u64 handle;
1646
1647   if (!pool_elts (app_pool))
1648     {
1649       vlib_cli_output (vm, "No active server bindings");
1650       return;
1651     }
1652
1653   if (do_local)
1654     {
1655       application_format_local_sessions (0, verbose);
1656       /* *INDENT-OFF* */
1657       pool_foreach (app, app_pool, ({
1658         if (!pool_elts (app->local_sessions)
1659             && !pool_elts(app->local_connects))
1660           continue;
1661         application_format_local_sessions (app, verbose);
1662       }));
1663       /* *INDENT-ON* */
1664     }
1665   else
1666     {
1667       vlib_cli_output (vm, "%U", format_application_listener, 0 /* header */ ,
1668                        0, 0, verbose);
1669
1670       /* *INDENT-OFF* */
1671       pool_foreach (app, app_pool, ({
1672         if (hash_elts (app->listeners_table) == 0)
1673           continue;
1674         hash_foreach (handle, sm_index, app->listeners_table, ({
1675           vlib_cli_output (vm, "%U", format_application_listener, app,
1676                            handle, sm_index, verbose);
1677         }));
1678       }));
1679       /* *INDENT-ON* */
1680     }
1681 }
1682
1683 void
1684 application_format_all_clients (vlib_main_t * vm, int do_local, int verbose)
1685 {
1686   application_t *app;
1687
1688   if (!pool_elts (app_pool))
1689     {
1690       vlib_cli_output (vm, "No active apps");
1691       return;
1692     }
1693
1694   if (do_local)
1695     {
1696       application_format_local_connects (0, verbose);
1697
1698       /* *INDENT-OFF* */
1699       pool_foreach (app, app_pool, ({
1700         if (app->local_connects)
1701           application_format_local_connects (app, verbose);
1702       }));
1703       /* *INDENT-ON* */
1704     }
1705   else
1706     {
1707       application_format_connects (0, verbose);
1708
1709       /* *INDENT-OFF* */
1710       pool_foreach (app, app_pool, ({
1711         if (app->connects_seg_manager == (u32)~0)
1712           continue;
1713         application_format_connects (app, verbose);
1714       }));
1715       /* *INDENT-ON* */
1716     }
1717 }
1718
1719 static clib_error_t *
1720 show_app_command_fn (vlib_main_t * vm, unformat_input_t * input,
1721                      vlib_cli_command_t * cmd)
1722 {
1723   int do_server = 0, do_client = 0, do_local = 0;
1724   application_t *app;
1725   int verbose = 0;
1726
1727   session_cli_return_if_not_enabled ();
1728
1729   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1730     {
1731       if (unformat (input, "server"))
1732         do_server = 1;
1733       else if (unformat (input, "client"))
1734         do_client = 1;
1735       else if (unformat (input, "local"))
1736         do_local = 1;
1737       else if (unformat (input, "verbose"))
1738         verbose = 1;
1739       else
1740         break;
1741     }
1742
1743   if (do_server)
1744     application_format_all_listeners (vm, do_local, verbose);
1745
1746   if (do_client)
1747     application_format_all_clients (vm, do_local, verbose);
1748
1749   /* Print app related info */
1750   if (!do_server && !do_client)
1751     {
1752       vlib_cli_output (vm, "%U", format_application, 0, verbose);
1753       /* *INDENT-OFF* */
1754       pool_foreach (app, app_pool, ({
1755         vlib_cli_output (vm, "%U", format_application, app, verbose);
1756       }));
1757       /* *INDENT-ON* */
1758     }
1759
1760   return 0;
1761 }
1762
1763 /* *INDENT-OFF* */
1764 VLIB_CLI_COMMAND (show_app_command, static) =
1765 {
1766   .path = "show app",
1767   .short_help = "show app [server|client] [verbose]",
1768   .function = show_app_command_fn,
1769 };
1770 /* *INDENT-ON* */
1771
1772 /*
1773  * fd.io coding-style-patch-verification: ON
1774  *
1775  * Local Variables:
1776  * eval: (c-set-style "gnu")
1777  * End:
1778  */