session: cleanup application interface
[vpp.git] / src / vnet / session / application.c
1 /*
2  * Copyright (c) 2017-2019 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 static app_main_t app_main;
22
23 #define app_interface_check_thread_and_barrier(_fn, _arg)               \
24   if (PREDICT_FALSE (!vlib_thread_is_main_w_barrier ()))                \
25     {                                                                   \
26       vlib_rpc_call_main_thread (_fn, (u8 *) _arg, sizeof(*_arg));      \
27       return 0;                                                         \
28     }
29
30 static void
31 application_local_listener_session_endpoint (local_session_t * ll,
32                                              session_endpoint_t * sep)
33 {
34   sep->transport_proto =
35     session_type_transport_proto (ll->listener_session_type);
36   sep->port = ll->port;
37   sep->is_ip4 = ll->listener_session_type & 1;
38 }
39
40 static app_listener_t *
41 app_listener_alloc (application_t * app)
42 {
43   app_listener_t *app_listener;
44   pool_get (app->listeners, app_listener);
45   clib_memset (app_listener, 0, sizeof (*app_listener));
46   app_listener->al_index = app_listener - app->listeners;
47   app_listener->app_index = app->app_index;
48   app_listener->session_index = SESSION_INVALID_INDEX;
49   app_listener->local_index = SESSION_INVALID_INDEX;
50   return app_listener;
51 }
52
53 app_listener_t *
54 app_listener_get (application_t * app, u32 app_listener_index)
55 {
56   return pool_elt_at_index (app->listeners, app_listener_index);
57 }
58
59 static void
60 app_listener_free (application_t * app, app_listener_t * app_listener)
61 {
62   clib_bitmap_free (app_listener->workers);
63   pool_put (app->listeners, app_listener);
64   if (CLIB_DEBUG)
65     clib_memset (app_listener, 0xfa, sizeof (*app_listener));
66 }
67
68 local_session_t *
69 application_local_listen_session_alloc (application_t * app)
70 {
71   local_session_t *ll;
72   pool_get_zero (app->local_listen_sessions, ll);
73   ll->session_index = ll - app->local_listen_sessions;
74   ll->session_type = session_type_from_proto_and_ip (TRANSPORT_PROTO_NONE, 0);
75   ll->app_index = app->app_index;
76   return ll;
77 }
78
79 void
80 application_local_listen_session_free (application_t * app,
81                                        local_session_t * ll)
82 {
83   pool_put (app->local_listen_sessions, ll);
84   if (CLIB_DEBUG)
85     clib_memset (ll, 0xfb, sizeof (*ll));
86 }
87
88 static u32
89 app_listener_id (app_listener_t * al)
90 {
91   ASSERT (al->app_index < 1 << 16 && al->al_index < 1 << 16);
92   return (al->app_index << 16 | al->al_index);
93 }
94
95 session_handle_t
96 app_listener_handle (app_listener_t * al)
97 {
98   return ((u64) SESSION_LISTENER_PREFIX << 32 | (u64) app_listener_id (al));
99 }
100
101 static void
102 app_listener_id_parse (u32 listener_id, u32 * app_index,
103                        u32 * app_listener_index)
104 {
105   *app_index = listener_id >> 16;
106   *app_listener_index = listener_id & 0xFFFF;
107 }
108
109 void
110 app_listener_handle_parse (session_handle_t handle, u32 * app_index,
111                            u32 * app_listener_index)
112 {
113   app_listener_id_parse (handle & 0xFFFFFFFF, app_index, app_listener_index);
114 }
115
116 static app_listener_t *
117 app_listener_get_w_id (u32 listener_id)
118 {
119   u32 app_index, app_listener_index;
120   application_t *app;
121
122   app_listener_id_parse (listener_id, &app_index, &app_listener_index);
123   app = application_get_if_valid (app_index);
124   if (!app)
125     return 0;
126   return app_listener_get (app, app_listener_index);
127 }
128
129 app_listener_t *
130 app_listener_get_w_session (session_t * ls)
131 {
132   application_t *app;
133
134   app = application_get_if_valid (ls->app_index);
135   if (!app)
136     return 0;
137   return app_listener_get (app, ls->al_index);
138 }
139
140 app_listener_t *
141 app_listener_get_w_handle (session_handle_t handle)
142 {
143
144   if (handle >> 32 != SESSION_LISTENER_PREFIX)
145     return 0;
146
147   return app_listener_get_w_id (handle & 0xFFFFFFFF);
148 }
149
150 app_listener_t *
151 app_listener_lookup (application_t * app, session_endpoint_cfg_t * sep_ext)
152 {
153   u32 table_index, fib_proto;
154   session_endpoint_t *sep;
155   session_handle_t handle;
156   local_session_t *ll;
157   session_t *ls;
158
159   sep = (session_endpoint_t *) sep_ext;
160   if (application_has_local_scope (app) && session_endpoint_is_local (sep))
161     {
162       table_index = application_local_session_table (app);
163       handle = session_lookup_endpoint_listener (table_index, sep, 1);
164       if (handle != SESSION_INVALID_HANDLE)
165         {
166           ll = application_get_local_listener_w_handle (handle);
167           return app_listener_get_w_session ((session_t *) ll);
168         }
169     }
170
171   fib_proto = session_endpoint_fib_proto (sep);
172   table_index = application_session_table (app, fib_proto);
173   handle = session_lookup_endpoint_listener (table_index, sep, 1);
174   if (handle != SESSION_INVALID_HANDLE)
175     {
176       ls = listen_session_get_from_handle (handle);
177       return app_listener_get_w_session ((session_t *) ls);
178     }
179
180   return 0;
181 }
182
183 int
184 app_listener_alloc_and_init (application_t * app,
185                              session_endpoint_cfg_t * sep,
186                              app_listener_t ** listener)
187 {
188   app_listener_t *app_listener;
189   local_session_t *ll = 0;
190   session_handle_t lh;
191   session_type_t st;
192   session_t *ls = 0;
193   int rv;
194
195   app_listener = app_listener_alloc (app);
196   st = session_type_from_proto_and_ip (sep->transport_proto, sep->is_ip4);
197
198   /*
199    * Add session endpoint to local session table. Only binds to "inaddr_any"
200    * (i.e., zero address) are added to local scope table.
201    */
202   if (application_has_local_scope (app)
203       && session_endpoint_is_local ((session_endpoint_t *) sep))
204     {
205       u32 table_index;
206
207       ll = application_local_listen_session_alloc (app);
208       ll->port = sep->port;
209       /* Store the original session type for the unbind */
210       ll->listener_session_type = st;
211       table_index = application_local_session_table (app);
212       lh = application_local_session_handle (ll);
213       session_lookup_add_session_endpoint (table_index,
214                                            (session_endpoint_t *) sep, lh);
215       app_listener->local_index = ll->session_index;
216       ll->al_index = app_listener->al_index;
217     }
218
219   if (application_has_global_scope (app))
220     {
221       /*
222        * Start listening on local endpoint for requested transport and scope.
223        * Creates a stream session with state LISTENING to be used in session
224        * lookups, prior to establishing connection. Requests transport to
225        * build it's own specific listening connection.
226        */
227       ls = listen_session_new (0, st);
228       ls->app_index = app->app_index;
229       ls->app_wrk_index = sep->app_wrk_index;
230
231       /* Listen pool can be reallocated if the transport is
232        * recursive (tls) */
233       lh = session_handle (ls);
234
235       if ((rv = session_listen (ls, sep)))
236         {
237           ls = session_get_from_handle (lh);
238           session_free (ls);
239           return rv;
240         }
241       app_listener->session_index = ls->session_index;
242       ls->al_index = app_listener->al_index;
243     }
244
245   if (!ll && !ls)
246     {
247       app_listener_free (app, app_listener);
248       return -1;
249     }
250
251   *listener = app_listener;
252   return 0;
253 }
254
255 void
256 app_listener_cleanup (app_listener_t * al)
257 {
258   application_t *app = application_get (al->app_index);
259
260   if (al->session_index != SESSION_INVALID_INDEX)
261     {
262       session_t *ls = session_get (al->session_index, 0);
263       session_stop_listen (ls);
264       listen_session_del (ls);
265     }
266   if (al->local_index != SESSION_INVALID_INDEX)
267     {
268       session_endpoint_t sep = SESSION_ENDPOINT_NULL;
269       local_session_t *ll;
270       u32 table_index;
271
272       table_index = application_local_session_table (app);
273       ll = application_get_local_listen_session (app, al->local_index);
274       application_local_listener_session_endpoint (ll, &sep);
275       session_lookup_del_session_endpoint (table_index, &sep);
276       application_local_listen_session_free (app, ll);
277     }
278   app_listener_free (app, al);
279 }
280
281 app_worker_t *
282 app_listener_select_worker (app_listener_t * al)
283 {
284   application_t *app;
285   u32 wrk_index;
286
287   app = application_get (al->app_index);
288   wrk_index = clib_bitmap_next_set (al->workers, al->accept_rotor + 1);
289   if (wrk_index == ~0)
290     wrk_index = clib_bitmap_first_set (al->workers);
291
292   ASSERT (wrk_index != ~0);
293   al->accept_rotor = wrk_index;
294   return application_get_worker (app, wrk_index);
295 }
296
297 session_t *
298 app_listener_get_session (app_listener_t * al)
299 {
300   if (al->session_index == SESSION_INVALID_INDEX)
301     return 0;
302
303   return listen_session_get (al->session_index);
304 }
305
306 static app_worker_map_t *
307 app_worker_map_alloc (application_t * app)
308 {
309   app_worker_map_t *map;
310   pool_get (app->worker_maps, map);
311   clib_memset (map, 0, sizeof (*map));
312   return map;
313 }
314
315 static u32
316 app_worker_map_index (application_t * app, app_worker_map_t * map)
317 {
318   return (map - app->worker_maps);
319 }
320
321 static void
322 app_worker_map_free (application_t * app, app_worker_map_t * map)
323 {
324   pool_put (app->worker_maps, map);
325 }
326
327 static app_worker_map_t *
328 app_worker_map_get (application_t * app, u32 map_index)
329 {
330   if (pool_is_free_index (app->worker_maps, map_index))
331     return 0;
332   return pool_elt_at_index (app->worker_maps, map_index);
333 }
334
335 static const u8 *
336 app_get_name (application_t * app)
337 {
338   return app->name;
339 }
340
341 u32
342 application_session_table (application_t * app, u8 fib_proto)
343 {
344   app_namespace_t *app_ns;
345   app_ns = app_namespace_get (app->ns_index);
346   if (!application_has_global_scope (app))
347     return APP_INVALID_INDEX;
348   if (fib_proto == FIB_PROTOCOL_IP4)
349     return session_lookup_get_index_for_fib (fib_proto,
350                                              app_ns->ip4_fib_index);
351   else
352     return session_lookup_get_index_for_fib (fib_proto,
353                                              app_ns->ip6_fib_index);
354 }
355
356 u32
357 application_local_session_table (application_t * app)
358 {
359   app_namespace_t *app_ns;
360   if (!application_has_local_scope (app))
361     return APP_INVALID_INDEX;
362   app_ns = app_namespace_get (app->ns_index);
363   return app_ns->local_table_index;
364 }
365
366 /**
367  * Returns app name for app-index
368  */
369 const u8 *
370 application_name_from_index (u32 app_index)
371 {
372   application_t *app = application_get (app_index);
373   if (!app)
374     return 0;
375   return app_get_name (app);
376 }
377
378 static void
379 application_api_table_add (u32 app_index, u32 api_client_index)
380 {
381   if (api_client_index != APP_INVALID_INDEX)
382     hash_set (app_main.app_by_api_client_index, api_client_index, app_index);
383 }
384
385 static void
386 application_api_table_del (u32 api_client_index)
387 {
388   hash_unset (app_main.app_by_api_client_index, api_client_index);
389 }
390
391 static void
392 application_name_table_add (application_t * app)
393 {
394   hash_set_mem (app_main.app_by_name, app->name, app->app_index);
395 }
396
397 static void
398 application_name_table_del (application_t * app)
399 {
400   hash_unset_mem (app_main.app_by_name, app->name);
401 }
402
403 application_t *
404 application_lookup (u32 api_client_index)
405 {
406   uword *p;
407   p = hash_get (app_main.app_by_api_client_index, api_client_index);
408   if (p)
409     return application_get_if_valid (p[0]);
410
411   return 0;
412 }
413
414 application_t *
415 application_lookup_name (const u8 * name)
416 {
417   uword *p;
418   p = hash_get_mem (app_main.app_by_name, name);
419   if (p)
420     return application_get (p[0]);
421
422   return 0;
423 }
424
425 static application_t *
426 application_alloc (void)
427 {
428   application_t *app;
429   pool_get (app_main.app_pool, app);
430   clib_memset (app, 0, sizeof (*app));
431   app->app_index = app - app_main.app_pool;
432   return app;
433 }
434
435 application_t *
436 application_get (u32 app_index)
437 {
438   if (app_index == APP_INVALID_INDEX)
439     return 0;
440   return pool_elt_at_index (app_main.app_pool, app_index);
441 }
442
443 application_t *
444 application_get_if_valid (u32 app_index)
445 {
446   if (pool_is_free_index (app_main.app_pool, app_index))
447     return 0;
448
449   return pool_elt_at_index (app_main.app_pool, app_index);
450 }
451
452 static void
453 application_verify_cb_fns (session_cb_vft_t * cb_fns)
454 {
455   if (cb_fns->session_accept_callback == 0)
456     clib_warning ("No accept callback function provided");
457   if (cb_fns->session_connected_callback == 0)
458     clib_warning ("No session connected callback function provided");
459   if (cb_fns->session_disconnect_callback == 0)
460     clib_warning ("No session disconnect callback function provided");
461   if (cb_fns->session_reset_callback == 0)
462     clib_warning ("No session reset callback function provided");
463 }
464
465 /**
466  * Check app config for given segment type
467  *
468  * Returns 1 on success and 0 otherwise
469  */
470 static u8
471 application_verify_cfg (ssvm_segment_type_t st)
472 {
473   u8 is_valid;
474   if (st == SSVM_SEGMENT_MEMFD)
475     {
476       is_valid = (session_manager_get_evt_q_segment () != 0);
477       if (!is_valid)
478         clib_warning ("memfd seg: vpp's event qs IN binary api svm region");
479       return is_valid;
480     }
481   else if (st == SSVM_SEGMENT_SHM)
482     {
483       is_valid = (session_manager_get_evt_q_segment () == 0);
484       if (!is_valid)
485         clib_warning ("shm seg: vpp's event qs NOT IN binary api svm region");
486       return is_valid;
487     }
488   else
489     return 1;
490 }
491
492 static int
493 application_alloc_and_init (app_init_args_t * a)
494 {
495   ssvm_segment_type_t seg_type = SSVM_SEGMENT_MEMFD;
496   segment_manager_properties_t *props;
497   vl_api_registration_t *reg;
498   application_t *app;
499   u64 *options;
500
501   app = application_alloc ();
502   options = a->options;
503   /*
504    * Make sure we support the requested configuration
505    */
506   if (!(options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_IS_BUILTIN))
507     {
508       reg = vl_api_client_index_to_registration (a->api_client_index);
509       if (!reg)
510         return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
511       if (vl_api_registration_file_index (reg) == VL_API_INVALID_FI)
512         seg_type = SSVM_SEGMENT_SHM;
513     }
514   else
515     {
516       if (options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_EVT_MQ_USE_EVENTFD)
517         {
518           clib_warning ("mq eventfds can only be used if socket transport is "
519                         "used for api");
520           return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
521         }
522       seg_type = SSVM_SEGMENT_PRIVATE;
523     }
524
525   if (!application_verify_cfg (seg_type))
526     return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
527
528   /* Check that the obvious things are properly set up */
529   application_verify_cb_fns (a->session_cb_vft);
530
531   app->flags = options[APP_OPTIONS_FLAGS];
532   app->cb_fns = *a->session_cb_vft;
533   app->ns_index = options[APP_OPTIONS_NAMESPACE];
534   app->proxied_transports = options[APP_OPTIONS_PROXY_TRANSPORT];
535   app->name = vec_dup (a->name);
536
537   /* If no scope enabled, default to global */
538   if (!application_has_global_scope (app)
539       && !application_has_local_scope (app))
540     app->flags |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
541
542   props = application_segment_manager_properties (app);
543   segment_manager_properties_init (props);
544   props->segment_size = options[APP_OPTIONS_ADD_SEGMENT_SIZE];
545   props->prealloc_fifos = options[APP_OPTIONS_PREALLOC_FIFO_PAIRS];
546   if (options[APP_OPTIONS_ADD_SEGMENT_SIZE])
547     {
548       props->add_segment_size = options[APP_OPTIONS_ADD_SEGMENT_SIZE];
549       props->add_segment = 1;
550     }
551   if (options[APP_OPTIONS_RX_FIFO_SIZE])
552     props->rx_fifo_size = options[APP_OPTIONS_RX_FIFO_SIZE];
553   if (options[APP_OPTIONS_TX_FIFO_SIZE])
554     props->tx_fifo_size = options[APP_OPTIONS_TX_FIFO_SIZE];
555   if (options[APP_OPTIONS_EVT_QUEUE_SIZE])
556     props->evt_q_size = options[APP_OPTIONS_EVT_QUEUE_SIZE];
557   if (options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_EVT_MQ_USE_EVENTFD)
558     props->use_mq_eventfd = 1;
559   if (options[APP_OPTIONS_TLS_ENGINE])
560     app->tls_engine = options[APP_OPTIONS_TLS_ENGINE];
561   props->segment_type = seg_type;
562
563   /* Add app to lookup by api_client_index table */
564   if (!application_is_builtin (app))
565     application_api_table_add (app->app_index, a->api_client_index);
566   else
567     application_name_table_add (app);
568
569   a->app_index = app->app_index;
570
571   APP_DBG ("New app name: %v api index: %u index %u", app->name,
572            app->api_client_index, app->app_index);
573
574   return 0;
575 }
576
577 static void
578 application_free (application_t * app)
579 {
580   app_worker_map_t *wrk_map;
581   app_worker_t *app_wrk;
582   u32 table_index;
583   local_session_t *ll;
584   session_endpoint_t sep;
585
586   /*
587    * The app event queue allocated in first segment is cleared with
588    * the segment manager. No need to explicitly free it.
589    */
590   APP_DBG ("Delete app name %v api index: %d index: %d", app->name,
591            app->api_client_index, app->app_index);
592
593   if (application_is_proxy (app))
594     application_remove_proxy (app);
595
596   /*
597    * Free workers
598    */
599
600   /* *INDENT-OFF* */
601   pool_flush (wrk_map, app->worker_maps, ({
602     app_wrk = app_worker_get (wrk_map->wrk_index);
603     app_worker_free (app_wrk);
604   }));
605   /* *INDENT-ON* */
606   pool_free (app->worker_maps);
607
608   /*
609    * Free local listeners. Global table unbinds stop local listeners
610    * as well, but if we have only local binds, these won't be cleaned up.
611    * Don't bother with local accepted sessions, we clean them when
612    * cleaning up the worker.
613    */
614   table_index = application_local_session_table (app);
615   /* *INDENT-OFF* */
616   pool_foreach (ll, app->local_listen_sessions, ({
617     application_local_listener_session_endpoint (ll, &sep);
618     session_lookup_del_session_endpoint (table_index, &sep);
619   }));
620   /* *INDENT-ON* */
621   pool_free (app->local_listen_sessions);
622
623   /*
624    * Cleanup remaining state
625    */
626   if (application_is_builtin (app))
627     application_name_table_del (app);
628   vec_free (app->name);
629   vec_free (app->tls_cert);
630   vec_free (app->tls_key);
631   pool_put (app_main.app_pool, app);
632 }
633
634 static void
635 application_detach_process (application_t * app, u32 api_client_index)
636 {
637   vnet_app_worker_add_del_args_t _args = { 0 }, *args = &_args;
638   app_worker_map_t *wrk_map;
639   u32 *wrks = 0, *wrk_index;
640   app_worker_t *app_wrk;
641
642   if (api_client_index == ~0)
643     {
644       application_free (app);
645       return;
646     }
647
648   APP_DBG ("Detaching for app %v index %u api client index %u", app->name,
649            app->app_index, app->api_client_index);
650
651   /* *INDENT-OFF* */
652   pool_foreach (wrk_map, app->worker_maps, ({
653     app_wrk = app_worker_get (wrk_map->wrk_index);
654     if (app_wrk->api_client_index == api_client_index)
655       vec_add1 (wrks, app_wrk->wrk_index);
656   }));
657   /* *INDENT-ON* */
658
659   if (!vec_len (wrks))
660     {
661       clib_warning ("no workers for app %u api_index %u", app->app_index,
662                     api_client_index);
663       return;
664     }
665
666   args->app_index = app->app_index;
667   args->api_client_index = api_client_index;
668   vec_foreach (wrk_index, wrks)
669   {
670     app_wrk = app_worker_get (wrk_index[0]);
671     args->wrk_map_index = app_wrk->wrk_map_index;
672     args->is_add = 0;
673     vnet_app_worker_add_del (args);
674   }
675   vec_free (wrks);
676 }
677
678 app_worker_t *
679 application_get_worker (application_t * app, u32 wrk_map_index)
680 {
681   app_worker_map_t *map;
682   map = app_worker_map_get (app, wrk_map_index);
683   if (!map)
684     return 0;
685   return app_worker_get (map->wrk_index);
686 }
687
688 app_worker_t *
689 application_get_default_worker (application_t * app)
690 {
691   return application_get_worker (app, 0);
692 }
693
694 u32
695 application_n_workers (application_t * app)
696 {
697   return pool_elts (app->worker_maps);
698 }
699
700 app_worker_t *
701 application_listener_select_worker (session_t * ls)
702 {
703   app_listener_t *al;
704
705   al = app_listener_get_w_session (ls);
706   return app_listener_select_worker (al);
707 }
708
709 int
710 application_alloc_worker_and_init (application_t * app, app_worker_t ** wrk)
711 {
712   app_worker_map_t *wrk_map;
713   app_worker_t *app_wrk;
714   segment_manager_t *sm;
715   int rv;
716
717   app_wrk = app_worker_alloc (app);
718   wrk_map = app_worker_map_alloc (app);
719   wrk_map->wrk_index = app_wrk->wrk_index;
720   app_wrk->wrk_map_index = app_worker_map_index (app, wrk_map);
721
722   /*
723    * Setup first segment manager
724    */
725   sm = segment_manager_new ();
726   sm->app_wrk_index = app_wrk->wrk_index;
727
728   if ((rv = segment_manager_init (sm, app->sm_properties.segment_size,
729                                   app->sm_properties.prealloc_fifos)))
730     {
731       app_worker_free (app_wrk);
732       return rv;
733     }
734   sm->first_is_protected = 1;
735
736   /*
737    * Setup app worker
738    */
739   app_wrk->first_segment_manager = segment_manager_index (sm);
740   app_wrk->listeners_table = hash_create (0, sizeof (u64));
741   app_wrk->event_queue = segment_manager_event_queue (sm);
742   app_wrk->app_is_builtin = application_is_builtin (app);
743
744   /*
745    * Segment manager for local sessions
746    */
747   sm = segment_manager_new ();
748   sm->app_wrk_index = app_wrk->wrk_index;
749   app_wrk->local_segment_manager = segment_manager_index (sm);
750   app_wrk->local_connects = hash_create (0, sizeof (u64));
751
752   *wrk = app_wrk;
753
754   return 0;
755 }
756
757 int
758 vnet_app_worker_add_del (vnet_app_worker_add_del_args_t * a)
759 {
760   svm_fifo_segment_private_t *fs;
761   app_worker_map_t *wrk_map;
762   app_worker_t *app_wrk;
763   segment_manager_t *sm;
764   application_t *app;
765   int rv;
766
767   app = application_get (a->app_index);
768   if (!app)
769     return VNET_API_ERROR_INVALID_VALUE;
770
771   if (a->is_add)
772     {
773       if ((rv = application_alloc_worker_and_init (app, &app_wrk)))
774         return rv;
775
776       /* Map worker api index to the app */
777       app_wrk->api_client_index = a->api_client_index;
778       application_api_table_add (app->app_index, a->api_client_index);
779
780       sm = segment_manager_get (app_wrk->first_segment_manager);
781       fs = segment_manager_get_segment_w_lock (sm, 0);
782       a->segment = &fs->ssvm;
783       a->segment_handle = segment_manager_segment_handle (sm, fs);
784       segment_manager_segment_reader_unlock (sm);
785       a->evt_q = app_wrk->event_queue;
786       a->wrk_map_index = app_wrk->wrk_map_index;
787     }
788   else
789     {
790       wrk_map = app_worker_map_get (app, a->wrk_map_index);
791       if (!wrk_map)
792         return VNET_API_ERROR_INVALID_VALUE;
793
794       app_wrk = app_worker_get (wrk_map->wrk_index);
795       if (!app_wrk)
796         return VNET_API_ERROR_INVALID_VALUE;
797
798       application_api_table_del (app_wrk->api_client_index);
799       app_worker_free (app_wrk);
800       app_worker_map_free (app, wrk_map);
801       if (application_n_workers (app) == 0)
802         application_free (app);
803     }
804   return 0;
805 }
806
807 static int
808 app_validate_namespace (u8 * namespace_id, u64 secret, u32 * app_ns_index)
809 {
810   app_namespace_t *app_ns;
811   if (vec_len (namespace_id) == 0)
812     {
813       /* Use default namespace */
814       *app_ns_index = 0;
815       return 0;
816     }
817
818   *app_ns_index = app_namespace_index_from_id (namespace_id);
819   if (*app_ns_index == APP_NAMESPACE_INVALID_INDEX)
820     return VNET_API_ERROR_APP_INVALID_NS;
821   app_ns = app_namespace_get (*app_ns_index);
822   if (!app_ns)
823     return VNET_API_ERROR_APP_INVALID_NS;
824   if (app_ns->ns_secret != secret)
825     return VNET_API_ERROR_APP_WRONG_NS_SECRET;
826   return 0;
827 }
828
829 static u8 *
830 app_name_from_api_index (u32 api_client_index)
831 {
832   vl_api_registration_t *regp;
833   regp = vl_api_client_index_to_registration (api_client_index);
834   if (regp)
835     return format (0, "%s%c", regp->name, 0);
836
837   clib_warning ("api client index %u does not have an api registration!",
838                 api_client_index);
839   return format (0, "unknown%c", 0);
840 }
841
842 /**
843  * Attach application to vpp
844  *
845  * Allocates a vpp app, i.e., a structure that keeps back pointers
846  * to external app and a segment manager for shared memory fifo based
847  * communication with the external app.
848  */
849 int
850 vnet_application_attach (vnet_app_attach_args_t * a)
851 {
852   svm_fifo_segment_private_t *fs;
853   application_t *app = 0;
854   app_worker_t *app_wrk;
855   segment_manager_t *sm;
856   u32 app_ns_index = 0;
857   u8 *app_name = 0;
858   u64 secret;
859   int rv;
860
861   if (a->api_client_index != APP_INVALID_INDEX)
862     app = application_lookup (a->api_client_index);
863   else if (a->name)
864     app = application_lookup_name (a->name);
865   else
866     return VNET_API_ERROR_INVALID_VALUE;
867
868   if (app)
869     return VNET_API_ERROR_APP_ALREADY_ATTACHED;
870
871   if (a->api_client_index != APP_INVALID_INDEX)
872     {
873       app_name = app_name_from_api_index (a->api_client_index);
874       a->name = app_name;
875     }
876
877   secret = a->options[APP_OPTIONS_NAMESPACE_SECRET];
878   if ((rv = app_validate_namespace (a->namespace_id, secret, &app_ns_index)))
879     return rv;
880   a->options[APP_OPTIONS_NAMESPACE] = app_ns_index;
881
882   if ((rv = application_alloc_and_init ((app_init_args_t *) a)))
883     return rv;
884
885   app = application_get (a->app_index);
886   if ((rv = application_alloc_worker_and_init (app, &app_wrk)))
887     return rv;
888
889   a->app_evt_q = app_wrk->event_queue;
890   app_wrk->api_client_index = a->api_client_index;
891   sm = segment_manager_get (app_wrk->first_segment_manager);
892   fs = segment_manager_get_segment_w_lock (sm, 0);
893
894   if (application_is_proxy (app))
895     application_setup_proxy (app);
896
897   ASSERT (vec_len (fs->ssvm.name) <= 128);
898   a->segment = &fs->ssvm;
899   a->segment_handle = segment_manager_segment_handle (sm, fs);
900
901   segment_manager_segment_reader_unlock (sm);
902   vec_free (app_name);
903   return 0;
904 }
905
906 /**
907  * Detach application from vpp
908  */
909 int
910 vnet_application_detach (vnet_app_detach_args_t * a)
911 {
912   application_t *app;
913
914   app = application_get_if_valid (a->app_index);
915   if (!app)
916     {
917       clib_warning ("app not attached");
918       return VNET_API_ERROR_APPLICATION_NOT_ATTACHED;
919     }
920
921   app_interface_check_thread_and_barrier (vnet_application_detach, a);
922   application_detach_process (app, a->api_client_index);
923   return 0;
924 }
925
926
927 static u8
928 session_endpoint_in_ns (session_endpoint_t * sep)
929 {
930   u8 is_lep = session_endpoint_is_local (sep);
931   if (!is_lep && sep->sw_if_index != ENDPOINT_INVALID_INDEX
932       && !ip_interface_has_address (sep->sw_if_index, &sep->ip, sep->is_ip4))
933     {
934       clib_warning ("sw_if_index %u not configured with ip %U",
935                     sep->sw_if_index, format_ip46_address, &sep->ip,
936                     sep->is_ip4);
937       return 0;
938     }
939   return (is_lep || ip_is_local (sep->fib_index, &sep->ip, sep->is_ip4));
940 }
941
942 static void
943 session_endpoint_update_for_app (session_endpoint_cfg_t * sep,
944                                  application_t * app, u8 is_connect)
945 {
946   app_namespace_t *app_ns;
947   u32 ns_index, fib_index;
948
949   ns_index = app->ns_index;
950
951   /* App is a transport proto, so fetch the calling app's ns */
952   if (app->flags & APP_OPTIONS_FLAGS_IS_TRANSPORT_APP)
953     {
954       app_worker_t *owner_wrk;
955       application_t *owner_app;
956
957       owner_wrk = app_worker_get (sep->app_wrk_index);
958       owner_app = application_get (owner_wrk->app_index);
959       ns_index = owner_app->ns_index;
960     }
961   app_ns = app_namespace_get (ns_index);
962   if (!app_ns)
963     return;
964
965   /* Ask transport and network to bind to/connect using local interface
966    * that "supports" app's namespace. This will fix our local connection
967    * endpoint.
968    */
969
970   /* If in default namespace and user requested a fib index use it */
971   if (ns_index == 0 && sep->fib_index != ENDPOINT_INVALID_INDEX)
972     fib_index = sep->fib_index;
973   else
974     fib_index = sep->is_ip4 ? app_ns->ip4_fib_index : app_ns->ip6_fib_index;
975   sep->peer.fib_index = fib_index;
976   sep->fib_index = fib_index;
977
978   if (!is_connect)
979     {
980       sep->sw_if_index = app_ns->sw_if_index;
981     }
982   else
983     {
984       if (app_ns->sw_if_index != APP_NAMESPACE_INVALID_INDEX
985           && sep->peer.sw_if_index != ENDPOINT_INVALID_INDEX
986           && sep->peer.sw_if_index != app_ns->sw_if_index)
987         clib_warning ("Local sw_if_index different from app ns sw_if_index");
988
989       sep->peer.sw_if_index = app_ns->sw_if_index;
990     }
991 }
992
993 int
994 vnet_listen (vnet_listen_args_t * a)
995 {
996   app_listener_t *app_listener;
997   app_worker_t *app_wrk;
998   application_t *app;
999   int rv;
1000
1001   app = application_get_if_valid (a->app_index);
1002   if (!app)
1003     return VNET_API_ERROR_APPLICATION_NOT_ATTACHED;
1004
1005   app_wrk = application_get_worker (app, a->wrk_map_index);
1006   if (!app_wrk)
1007     return VNET_API_ERROR_INVALID_VALUE;
1008
1009   a->sep_ext.app_wrk_index = app_wrk->wrk_index;
1010
1011   session_endpoint_update_for_app (&a->sep_ext, app, 0 /* is_connect */ );
1012   if (!session_endpoint_in_ns (&a->sep))
1013     return VNET_API_ERROR_INVALID_VALUE_2;
1014
1015   /*
1016    * Check if we already have an app listener
1017    */
1018   app_listener = app_listener_lookup (app, &a->sep_ext);
1019   if (app_listener)
1020     {
1021       if (app_listener->app_index != app->app_index)
1022         return VNET_API_ERROR_ADDRESS_IN_USE;
1023       if (app_worker_start_listen (app_wrk, app_listener))
1024         return -1;
1025       a->handle = app_listener_handle (app_listener);
1026       return 0;
1027     }
1028
1029   /*
1030    * Create new app listener
1031    */
1032   if ((rv = app_listener_alloc_and_init (app, &a->sep_ext, &app_listener)))
1033     return rv;
1034
1035   if ((rv = app_worker_start_listen (app_wrk, app_listener)))
1036     {
1037       app_listener_cleanup (app_listener);
1038       return rv;
1039     }
1040
1041   a->handle = app_listener_handle (app_listener);
1042   return 0;
1043 }
1044
1045 int
1046 vnet_connect (vnet_connect_args_t * a)
1047 {
1048   app_worker_t *server_wrk, *client_wrk;
1049   application_t *client;
1050   local_session_t *ll;
1051   app_listener_t *al;
1052   u32 table_index;
1053   session_t *ls;
1054   u8 fib_proto;
1055   u64 lh;
1056
1057   if (session_endpoint_is_zero (&a->sep))
1058     return VNET_API_ERROR_INVALID_VALUE;
1059
1060   client = application_get (a->app_index);
1061   session_endpoint_update_for_app (&a->sep_ext, client, 1 /* is_connect */ );
1062   client_wrk = application_get_worker (client, a->wrk_map_index);
1063
1064   /*
1065    * First check the local scope for locally attached destinations.
1066    * If we have local scope, we pass *all* connects through it since we may
1067    * have special policy rules even for non-local destinations, think proxy.
1068    */
1069   if (application_has_local_scope (client))
1070     {
1071       table_index = application_local_session_table (client);
1072       lh = session_lookup_local_endpoint (table_index, &a->sep);
1073       if (lh == SESSION_DROP_HANDLE)
1074         return VNET_API_ERROR_APP_CONNECT_FILTERED;
1075
1076       if (lh == SESSION_INVALID_HANDLE)
1077         goto global_scope;
1078
1079       ll = application_get_local_listener_w_handle (lh);
1080       al = app_listener_get_w_session ((session_t *) ll);
1081
1082       /*
1083        * Break loop if rule in local table points to connecting app. This
1084        * can happen if client is a generic proxy. Route connect through
1085        * global table instead.
1086        */
1087       if (al->app_index == a->app_index)
1088         goto global_scope;
1089
1090       server_wrk = app_listener_select_worker (al);
1091       return app_worker_local_session_connect (client_wrk, server_wrk, ll,
1092                                                a->api_context);
1093     }
1094
1095   /*
1096    * If nothing found, check the global scope for locally attached
1097    * destinations. Make sure first that we're allowed to.
1098    */
1099
1100 global_scope:
1101   if (session_endpoint_is_local (&a->sep))
1102     return VNET_API_ERROR_SESSION_CONNECT;
1103
1104   if (!application_has_global_scope (client))
1105     return VNET_API_ERROR_APP_CONNECT_SCOPE;
1106
1107   fib_proto = session_endpoint_fib_proto (&a->sep);
1108   table_index = application_session_table (client, fib_proto);
1109   ls = session_lookup_listener (table_index, &a->sep);
1110   if (ls)
1111     {
1112       al = app_listener_get_w_session (ls);
1113       server_wrk = app_listener_select_worker (al);
1114       ll = (local_session_t *) ls;
1115       return app_worker_local_session_connect (client_wrk, server_wrk, ll,
1116                                                a->api_context);
1117     }
1118
1119   /*
1120    * Not connecting to a local server, propagate to transport
1121    */
1122   if (app_worker_connect_session (client_wrk, &a->sep, a->api_context))
1123     return VNET_API_ERROR_SESSION_CONNECT;
1124   return 0;
1125 }
1126
1127 int
1128 vnet_unlisten (vnet_unlisten_args_t * a)
1129 {
1130   app_worker_t *app_wrk;
1131   app_listener_t *al;
1132   application_t *app;
1133
1134   if (!(app = application_get_if_valid (a->app_index)))
1135     return VNET_API_ERROR_APPLICATION_NOT_ATTACHED;
1136
1137   al = app_listener_get_w_handle (a->handle);
1138   if (al->app_index != app->app_index)
1139     {
1140       clib_warning ("app doesn't own handle %llu!", a->handle);
1141       return -1;
1142     }
1143
1144   app_wrk = application_get_worker (app, a->wrk_map_index);
1145   if (!app_wrk)
1146     {
1147       clib_warning ("no app %u worker %u", app->app_index, a->wrk_map_index);
1148       return -1;
1149     }
1150
1151   return app_worker_stop_listen (app_wrk, al);
1152 }
1153
1154 int
1155 vnet_disconnect_session (vnet_disconnect_args_t * a)
1156 {
1157   if (session_handle_is_local (a->handle))
1158     {
1159       local_session_t *ls;
1160
1161       /* Disconnect reply came to worker 1 not main thread */
1162       app_interface_check_thread_and_barrier (vnet_disconnect_session, a);
1163
1164       if (!(ls = app_worker_get_local_session_from_handle (a->handle)))
1165         return 0;
1166
1167       return app_worker_local_session_disconnect (a->app_index, ls);
1168     }
1169   else
1170     {
1171       app_worker_t *app_wrk;
1172       session_t *s;
1173
1174       s = session_get_from_handle_if_valid (a->handle);
1175       if (!s)
1176         return VNET_API_ERROR_INVALID_VALUE;
1177       app_wrk = app_worker_get (s->app_wrk_index);
1178       if (app_wrk->app_index != a->app_index)
1179         return VNET_API_ERROR_INVALID_VALUE;
1180
1181       /* We're peeking into another's thread pool. Make sure */
1182       ASSERT (s->session_index == session_index_from_handle (a->handle));
1183
1184       session_close (s);
1185     }
1186   return 0;
1187 }
1188
1189 int
1190 application_change_listener_owner (session_t * s, app_worker_t * app_wrk)
1191 {
1192   app_worker_t *old_wrk = app_worker_get (s->app_wrk_index);
1193   app_listener_t *app_listener;
1194   application_t *app;
1195
1196   if (!old_wrk)
1197     return -1;
1198
1199   hash_unset (old_wrk->listeners_table, listen_session_get_handle (s));
1200   if (session_transport_service_type (s) == TRANSPORT_SERVICE_CL
1201       && s->rx_fifo)
1202     segment_manager_dealloc_fifos (s->rx_fifo->segment_index, s->rx_fifo,
1203                                    s->tx_fifo);
1204
1205   app = application_get (old_wrk->app_index);
1206   if (!app)
1207     return -1;
1208
1209   app_listener = app_listener_get (app, s->al_index);
1210
1211   /* Only remove from lb for now */
1212   app_listener->workers = clib_bitmap_set (app_listener->workers,
1213                                            old_wrk->wrk_map_index, 0);
1214
1215   if (app_worker_start_listen (app_wrk, app_listener))
1216     return -1;
1217
1218   s->app_wrk_index = app_wrk->wrk_index;
1219
1220   return 0;
1221 }
1222
1223 int
1224 application_is_proxy (application_t * app)
1225 {
1226   return (app->flags & APP_OPTIONS_FLAGS_IS_PROXY);
1227 }
1228
1229 int
1230 application_is_builtin (application_t * app)
1231 {
1232   return (app->flags & APP_OPTIONS_FLAGS_IS_BUILTIN);
1233 }
1234
1235 int
1236 application_is_builtin_proxy (application_t * app)
1237 {
1238   return (application_is_proxy (app) && application_is_builtin (app));
1239 }
1240
1241 u8
1242 application_has_local_scope (application_t * app)
1243 {
1244   return app->flags & APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
1245 }
1246
1247 u8
1248 application_has_global_scope (application_t * app)
1249 {
1250   return app->flags & APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
1251 }
1252
1253 u8
1254 application_use_mq_for_ctrl (application_t * app)
1255 {
1256   return app->flags & APP_OPTIONS_FLAGS_USE_MQ_FOR_CTRL_MSGS;
1257 }
1258
1259 static clib_error_t *
1260 application_start_stop_proxy_fib_proto (application_t * app, u8 fib_proto,
1261                                         u8 transport_proto, u8 is_start)
1262 {
1263   app_namespace_t *app_ns = app_namespace_get (app->ns_index);
1264   u8 is_ip4 = (fib_proto == FIB_PROTOCOL_IP4);
1265   session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
1266   transport_connection_t *tc;
1267   app_worker_t *app_wrk;
1268   app_listener_t *al;
1269   session_t *s;
1270   u32 flags;
1271
1272   /* TODO decide if we want proxy to be enabled for all workers */
1273   app_wrk = application_get_default_worker (app);
1274   if (is_start)
1275     {
1276       s = app_worker_first_listener (app_wrk, fib_proto, transport_proto);
1277       if (!s)
1278         {
1279           sep.is_ip4 = is_ip4;
1280           sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
1281           sep.sw_if_index = app_ns->sw_if_index;
1282           sep.transport_proto = transport_proto;
1283           sep.app_wrk_index = app_wrk->wrk_index;       /* only default */
1284
1285           /* force global scope listener */
1286           flags = app->flags;
1287           app->flags &= ~APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
1288           app_listener_alloc_and_init (app, &sep, &al);
1289           app->flags = flags;
1290
1291           app_worker_start_listen (app_wrk, al);
1292           s = listen_session_get (al->session_index);
1293           s->enqueue_epoch = SESSION_PROXY_LISTENER_INDEX;
1294         }
1295     }
1296   else
1297     {
1298       s = app_worker_proxy_listener (app_wrk, fib_proto, transport_proto);
1299       ASSERT (s);
1300     }
1301
1302   tc = listen_session_get_transport (s);
1303
1304   if (!ip_is_zero (&tc->lcl_ip, 1))
1305     {
1306       u32 sti;
1307       sep.is_ip4 = is_ip4;
1308       sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
1309       sep.transport_proto = transport_proto;
1310       sep.port = 0;
1311       sti = session_lookup_get_index_for_fib (fib_proto, sep.fib_index);
1312       if (is_start)
1313         session_lookup_add_session_endpoint (sti,
1314                                              (session_endpoint_t *) & sep,
1315                                              s->session_index);
1316       else
1317         session_lookup_del_session_endpoint (sti,
1318                                              (session_endpoint_t *) & sep);
1319     }
1320
1321   return 0;
1322 }
1323
1324 static void
1325 application_start_stop_proxy_local_scope (application_t * app,
1326                                           u8 transport_proto, u8 is_start)
1327 {
1328   session_endpoint_t sep = SESSION_ENDPOINT_NULL;
1329   app_namespace_t *app_ns;
1330   app_ns = app_namespace_get (app->ns_index);
1331   sep.is_ip4 = 1;
1332   sep.transport_proto = transport_proto;
1333   sep.port = 0;
1334
1335   if (is_start)
1336     {
1337       session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
1338                                            app->app_index);
1339       sep.is_ip4 = 0;
1340       session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
1341                                            app->app_index);
1342     }
1343   else
1344     {
1345       session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
1346       sep.is_ip4 = 0;
1347       session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
1348     }
1349 }
1350
1351 void
1352 application_start_stop_proxy (application_t * app,
1353                               transport_proto_t transport_proto, u8 is_start)
1354 {
1355   if (application_has_local_scope (app))
1356     application_start_stop_proxy_local_scope (app, transport_proto, is_start);
1357
1358   if (application_has_global_scope (app))
1359     {
1360       application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP4,
1361                                               transport_proto, is_start);
1362       application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP6,
1363                                               transport_proto, is_start);
1364     }
1365 }
1366
1367 void
1368 application_setup_proxy (application_t * app)
1369 {
1370   u16 transports = app->proxied_transports;
1371   transport_proto_t tp;
1372
1373   ASSERT (application_is_proxy (app));
1374
1375   /* *INDENT-OFF* */
1376   transport_proto_foreach (tp, ({
1377     if (transports & (1 << tp))
1378       application_start_stop_proxy (app, tp, 1);
1379   }));
1380   /* *INDENT-ON* */
1381 }
1382
1383 void
1384 application_remove_proxy (application_t * app)
1385 {
1386   u16 transports = app->proxied_transports;
1387   transport_proto_t tp;
1388
1389   ASSERT (application_is_proxy (app));
1390
1391   /* *INDENT-OFF* */
1392   transport_proto_foreach (tp, ({
1393     if (transports & (1 << tp))
1394       application_start_stop_proxy (app, tp, 0);
1395   }));
1396   /* *INDENT-ON* */
1397 }
1398
1399 segment_manager_properties_t *
1400 application_segment_manager_properties (application_t * app)
1401 {
1402   return &app->sm_properties;
1403 }
1404
1405 segment_manager_properties_t *
1406 application_get_segment_manager_properties (u32 app_index)
1407 {
1408   application_t *app = application_get (app_index);
1409   return &app->sm_properties;
1410 }
1411
1412 clib_error_t *
1413 vnet_app_add_tls_cert (vnet_app_add_tls_cert_args_t * a)
1414 {
1415   application_t *app;
1416   app = application_get (a->app_index);
1417   if (!app)
1418     return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1419                                    0, "app %u doesn't exist", a->app_index);
1420   app->tls_cert = vec_dup (a->cert);
1421   return 0;
1422 }
1423
1424 clib_error_t *
1425 vnet_app_add_tls_key (vnet_app_add_tls_key_args_t * a)
1426 {
1427   application_t *app;
1428   app = application_get (a->app_index);
1429   if (!app)
1430     return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1431                                    0, "app %u doesn't exist", a->app_index);
1432   app->tls_key = vec_dup (a->key);
1433   return 0;
1434 }
1435
1436 static void
1437 application_format_listeners (application_t * app, int verbose)
1438 {
1439   vlib_main_t *vm = vlib_get_main ();
1440   app_worker_map_t *wrk_map;
1441   app_worker_t *app_wrk;
1442   u32 sm_index;
1443   u64 handle;
1444
1445   if (!app)
1446     {
1447       vlib_cli_output (vm, "%U", format_app_worker_listener, 0 /* header */ ,
1448                        0, 0, verbose);
1449       return;
1450     }
1451
1452   /* *INDENT-OFF* */
1453   pool_foreach (wrk_map, app->worker_maps, ({
1454     app_wrk = app_worker_get (wrk_map->wrk_index);
1455     if (hash_elts (app_wrk->listeners_table) == 0)
1456       continue;
1457     hash_foreach (handle, sm_index, app_wrk->listeners_table, ({
1458       vlib_cli_output (vm, "%U", format_app_worker_listener, app_wrk,
1459                        handle, sm_index, verbose);
1460     }));
1461   }));
1462   /* *INDENT-ON* */
1463 }
1464
1465 static void
1466 application_format_connects (application_t * app, int verbose)
1467 {
1468   app_worker_map_t *wrk_map;
1469   app_worker_t *app_wrk;
1470
1471   if (!app)
1472     {
1473       app_worker_format_connects (0, verbose);
1474       return;
1475     }
1476
1477   /* *INDENT-OFF* */
1478   pool_foreach (wrk_map, app->worker_maps, ({
1479     app_wrk = app_worker_get (wrk_map->wrk_index);
1480     app_worker_format_connects (app_wrk, verbose);
1481   }));
1482   /* *INDENT-ON* */
1483 }
1484
1485 static void
1486 application_format_local_sessions (application_t * app, int verbose)
1487 {
1488   vlib_main_t *vm = vlib_get_main ();
1489   app_worker_map_t *wrk_map;
1490   app_worker_t *app_wrk;
1491   transport_proto_t tp;
1492   local_session_t *ls;
1493   u8 *conn = 0;
1494
1495   if (!app)
1496     {
1497       app_worker_format_local_sessions (0, verbose);
1498       return;
1499     }
1500
1501   /*
1502    * Format local listeners
1503    */
1504
1505   /* *INDENT-OFF* */
1506   pool_foreach (ls, app->local_listen_sessions, ({
1507     tp = session_type_transport_proto (ls->listener_session_type);
1508     conn = format (0, "[L][%U] *:%u", format_transport_proto_short, tp,
1509                    ls->port);
1510     vlib_cli_output (vm, "%-40v%-15u%-20s", conn, ls->app_wrk_index, "*");
1511     vec_reset_length (conn);
1512   }));
1513   /* *INDENT-ON* */
1514
1515   /*
1516    * Format local accepted/connected sessions
1517    */
1518   /* *INDENT-OFF* */
1519   pool_foreach (wrk_map, app->worker_maps, ({
1520     app_wrk = app_worker_get (wrk_map->wrk_index);
1521     app_worker_format_local_sessions (app_wrk, verbose);
1522   }));
1523   /* *INDENT-ON* */
1524 }
1525
1526 static void
1527 application_format_local_connects (application_t * app, int verbose)
1528 {
1529   app_worker_map_t *wrk_map;
1530   app_worker_t *app_wrk;
1531
1532   if (!app)
1533     {
1534       app_worker_format_local_connects (0, verbose);
1535       return;
1536     }
1537
1538   /* *INDENT-OFF* */
1539   pool_foreach (wrk_map, app->worker_maps, ({
1540     app_wrk = app_worker_get (wrk_map->wrk_index);
1541     app_worker_format_local_connects (app_wrk, verbose);
1542   }));
1543   /* *INDENT-ON* */
1544 }
1545
1546 u8 *
1547 format_application (u8 * s, va_list * args)
1548 {
1549   application_t *app = va_arg (*args, application_t *);
1550   CLIB_UNUSED (int verbose) = va_arg (*args, int);
1551   segment_manager_properties_t *props;
1552   const u8 *app_ns_name, *app_name;
1553   app_worker_map_t *wrk_map;
1554   app_worker_t *app_wrk;
1555
1556   if (app == 0)
1557     {
1558       if (!verbose)
1559         s = format (s, "%-10s%-20s%-40s", "Index", "Name", "Namespace");
1560       return s;
1561     }
1562
1563   app_name = app_get_name (app);
1564   app_ns_name = app_namespace_id_from_index (app->ns_index);
1565   props = application_segment_manager_properties (app);
1566   if (!verbose)
1567     {
1568       s = format (s, "%-10u%-20s%-40s", app->app_index, app_name,
1569                   app_ns_name);
1570       return s;
1571     }
1572
1573   s = format (s, "app-name %s app-index %u ns-index %u seg-size %U\n",
1574               app_name, app->app_index, app->ns_index,
1575               format_memory_size, props->add_segment_size);
1576   s = format (s, "rx-fifo-size %U tx-fifo-size %U workers:\n",
1577               format_memory_size, props->rx_fifo_size,
1578               format_memory_size, props->tx_fifo_size);
1579
1580   /* *INDENT-OFF* */
1581   pool_foreach (wrk_map, app->worker_maps, ({
1582       app_wrk = app_worker_get (wrk_map->wrk_index);
1583       s = format (s, "%U", format_app_worker, app_wrk);
1584   }));
1585   /* *INDENT-ON* */
1586
1587   return s;
1588 }
1589
1590 void
1591 application_format_all_listeners (vlib_main_t * vm, int do_local, int verbose)
1592 {
1593   application_t *app;
1594
1595   if (!pool_elts (app_main.app_pool))
1596     {
1597       vlib_cli_output (vm, "No active server bindings");
1598       return;
1599     }
1600
1601   if (do_local)
1602     {
1603       application_format_local_sessions (0, verbose);
1604       /* *INDENT-OFF* */
1605       pool_foreach (app, app_main.app_pool, ({
1606         application_format_local_sessions (app, verbose);
1607       }));
1608       /* *INDENT-ON* */
1609     }
1610   else
1611     {
1612       application_format_listeners (0, verbose);
1613
1614       /* *INDENT-OFF* */
1615       pool_foreach (app, app_main.app_pool, ({
1616         application_format_listeners (app, verbose);
1617       }));
1618       /* *INDENT-ON* */
1619     }
1620 }
1621
1622 void
1623 application_format_all_clients (vlib_main_t * vm, int do_local, int verbose)
1624 {
1625   application_t *app;
1626
1627   if (!pool_elts (app_main.app_pool))
1628     {
1629       vlib_cli_output (vm, "No active apps");
1630       return;
1631     }
1632
1633   if (do_local)
1634     {
1635       application_format_local_connects (0, verbose);
1636
1637       /* *INDENT-OFF* */
1638       pool_foreach (app, app_main.app_pool, ({
1639         application_format_local_connects (app, verbose);
1640       }));
1641       /* *INDENT-ON* */
1642     }
1643   else
1644     {
1645       application_format_connects (0, verbose);
1646
1647       /* *INDENT-OFF* */
1648       pool_foreach (app, app_main.app_pool, ({
1649         application_format_connects (app, verbose);
1650       }));
1651       /* *INDENT-ON* */
1652     }
1653 }
1654
1655 static clib_error_t *
1656 show_app_command_fn (vlib_main_t * vm, unformat_input_t * input,
1657                      vlib_cli_command_t * cmd)
1658 {
1659   int do_server = 0, do_client = 0, do_local = 0;
1660   application_t *app;
1661   u32 app_index = ~0;
1662   int verbose = 0;
1663
1664   session_cli_return_if_not_enabled ();
1665
1666   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1667     {
1668       if (unformat (input, "server"))
1669         do_server = 1;
1670       else if (unformat (input, "client"))
1671         do_client = 1;
1672       else if (unformat (input, "local"))
1673         do_local = 1;
1674       else if (unformat (input, "%u", &app_index))
1675         ;
1676       else if (unformat (input, "verbose"))
1677         verbose = 1;
1678       else
1679         return clib_error_return (0, "unknown input `%U'",
1680                                   format_unformat_error, input);
1681     }
1682
1683   if (do_server)
1684     {
1685       application_format_all_listeners (vm, do_local, verbose);
1686       return 0;
1687     }
1688
1689   if (do_client)
1690     {
1691       application_format_all_clients (vm, do_local, verbose);
1692       return 0;
1693     }
1694
1695   if (app_index != ~0)
1696     {
1697       app = application_get_if_valid (app_index);
1698       if (!app)
1699         return clib_error_return (0, "No app with index %u", app_index);
1700
1701       vlib_cli_output (vm, "%U", format_application, app, /* verbose */ 1);
1702       return 0;
1703     }
1704
1705   /* Print app related info */
1706   if (!do_server && !do_client)
1707     {
1708       vlib_cli_output (vm, "%U", format_application, 0, 0);
1709       /* *INDENT-OFF* */
1710       pool_foreach (app, app_main.app_pool, ({
1711         vlib_cli_output (vm, "%U", format_application, app, 0);
1712       }));
1713       /* *INDENT-ON* */
1714     }
1715
1716   return 0;
1717 }
1718
1719 /* *INDENT-OFF* */
1720 VLIB_CLI_COMMAND (show_app_command, static) =
1721 {
1722   .path = "show app",
1723   .short_help = "show app [server|client] [verbose]",
1724   .function = show_app_command_fn,
1725 };
1726 /* *INDENT-ON* */
1727
1728 /*
1729  * fd.io coding-style-patch-verification: ON
1730  *
1731  * Local Variables:
1732  * eval: (c-set-style "gnu")
1733  * End:
1734  */