session: use endpt fib index if app in default ns
[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 static app_main_t app_main;
22
23 static app_listener_t *
24 app_listener_alloc (application_t * app)
25 {
26   app_listener_t *app_listener;
27   pool_get (app->listeners, app_listener);
28   clib_memset (app_listener, 0, sizeof (*app_listener));
29   app_listener->al_index = app_listener - app->listeners;
30   return app_listener;
31 }
32
33 static app_listener_t *
34 app_listener_get (application_t * app, u32 app_listener_index)
35 {
36   return pool_elt_at_index (app->listeners, app_listener_index);
37 }
38
39 static void
40 app_listener_free (application_t * app, app_listener_t * app_listener)
41 {
42   clib_bitmap_free (app_listener->workers);
43   pool_put (app->listeners, app_listener);
44   if (CLIB_DEBUG)
45     clib_memset (app_listener, 0xfa, sizeof (*app_listener));
46 }
47
48 static app_listener_t *
49 app_local_listener_alloc (application_t * app)
50 {
51   app_listener_t *app_listener;
52   pool_get (app->local_listeners, app_listener);
53   clib_memset (app_listener, 0, sizeof (*app_listener));
54   app_listener->al_index = app_listener - app->local_listeners;
55   return app_listener;
56 }
57
58 static app_listener_t *
59 app_local_listener_get (application_t * app, u32 app_listener_index)
60 {
61   return pool_elt_at_index (app->local_listeners, app_listener_index);
62 }
63
64 static void
65 app_local_listener_free (application_t * app, app_listener_t * app_listener)
66 {
67   clib_bitmap_free (app_listener->workers);
68   pool_put (app->local_listeners, app_listener);
69   if (CLIB_DEBUG)
70     clib_memset (app_listener, 0xfa, sizeof (*app_listener));
71 }
72
73 static app_worker_map_t *
74 app_worker_map_alloc (application_t * app)
75 {
76   app_worker_map_t *map;
77   pool_get (app->worker_maps, map);
78   clib_memset (map, 0, sizeof (*map));
79   return map;
80 }
81
82 static u32
83 app_worker_map_index (application_t * app, app_worker_map_t * map)
84 {
85   return (map - app->worker_maps);
86 }
87
88 static void
89 app_worker_map_free (application_t * app, app_worker_map_t * map)
90 {
91   pool_put (app->worker_maps, map);
92 }
93
94 static app_worker_map_t *
95 app_worker_map_get (application_t * app, u32 map_index)
96 {
97   return pool_elt_at_index (app->worker_maps, map_index);
98 }
99
100 static const u8 *
101 app_get_name (application_t * app)
102 {
103   return app->name;
104 }
105
106 u32
107 application_session_table (application_t * app, u8 fib_proto)
108 {
109   app_namespace_t *app_ns;
110   app_ns = app_namespace_get (app->ns_index);
111   if (!application_has_global_scope (app))
112     return APP_INVALID_INDEX;
113   if (fib_proto == FIB_PROTOCOL_IP4)
114     return session_lookup_get_index_for_fib (fib_proto,
115                                              app_ns->ip4_fib_index);
116   else
117     return session_lookup_get_index_for_fib (fib_proto,
118                                              app_ns->ip6_fib_index);
119 }
120
121 u32
122 application_local_session_table (application_t * app)
123 {
124   app_namespace_t *app_ns;
125   if (!application_has_local_scope (app))
126     return APP_INVALID_INDEX;
127   app_ns = app_namespace_get (app->ns_index);
128   return app_ns->local_table_index;
129 }
130
131 static void
132 application_local_listener_session_endpoint (local_session_t * ll,
133                                              session_endpoint_t * sep)
134 {
135   sep->transport_proto =
136     session_type_transport_proto (ll->listener_session_type);
137   sep->port = ll->port;
138   sep->is_ip4 = ll->listener_session_type & 1;
139 }
140
141 /**
142  * Returns app name for app-index
143  */
144 const u8 *
145 application_name_from_index (u32 app_index)
146 {
147   application_t *app = application_get (app_index);
148   if (!app)
149     return 0;
150   return app_get_name (app);
151 }
152
153 static void
154 application_api_table_add (u32 app_index, u32 api_client_index)
155 {
156   if (api_client_index != APP_INVALID_INDEX)
157     hash_set (app_main.app_by_api_client_index, api_client_index, app_index);
158 }
159
160 static void
161 application_api_table_del (u32 api_client_index)
162 {
163   hash_unset (app_main.app_by_api_client_index, api_client_index);
164 }
165
166 static void
167 application_name_table_add (application_t * app)
168 {
169   hash_set_mem (app_main.app_by_name, app->name, app->app_index);
170 }
171
172 static void
173 application_name_table_del (application_t * app)
174 {
175   hash_unset_mem (app_main.app_by_name, app->name);
176 }
177
178 application_t *
179 application_lookup (u32 api_client_index)
180 {
181   uword *p;
182   p = hash_get (app_main.app_by_api_client_index, api_client_index);
183   if (p)
184     return application_get_if_valid (p[0]);
185
186   return 0;
187 }
188
189 application_t *
190 application_lookup_name (const u8 * name)
191 {
192   uword *p;
193   p = hash_get_mem (app_main.app_by_name, name);
194   if (p)
195     return application_get (p[0]);
196
197   return 0;
198 }
199
200 application_t *
201 application_alloc (void)
202 {
203   application_t *app;
204   pool_get (app_main.app_pool, app);
205   clib_memset (app, 0, sizeof (*app));
206   app->app_index = app - app_main.app_pool;
207   return app;
208 }
209
210 application_t *
211 application_get (u32 app_index)
212 {
213   if (app_index == APP_INVALID_INDEX)
214     return 0;
215   return pool_elt_at_index (app_main.app_pool, app_index);
216 }
217
218 application_t *
219 application_get_if_valid (u32 app_index)
220 {
221   if (pool_is_free_index (app_main.app_pool, app_index))
222     return 0;
223
224   return pool_elt_at_index (app_main.app_pool, app_index);
225 }
226
227 static void
228 application_verify_cb_fns (session_cb_vft_t * cb_fns)
229 {
230   if (cb_fns->session_accept_callback == 0)
231     clib_warning ("No accept callback function provided");
232   if (cb_fns->session_connected_callback == 0)
233     clib_warning ("No session connected callback function provided");
234   if (cb_fns->session_disconnect_callback == 0)
235     clib_warning ("No session disconnect callback function provided");
236   if (cb_fns->session_reset_callback == 0)
237     clib_warning ("No session reset callback function provided");
238 }
239
240 /**
241  * Check app config for given segment type
242  *
243  * Returns 1 on success and 0 otherwise
244  */
245 static u8
246 application_verify_cfg (ssvm_segment_type_t st)
247 {
248   u8 is_valid;
249   if (st == SSVM_SEGMENT_MEMFD)
250     {
251       is_valid = (session_manager_get_evt_q_segment () != 0);
252       if (!is_valid)
253         clib_warning ("memfd seg: vpp's event qs IN binary api svm region");
254       return is_valid;
255     }
256   else if (st == SSVM_SEGMENT_SHM)
257     {
258       is_valid = (session_manager_get_evt_q_segment () == 0);
259       if (!is_valid)
260         clib_warning ("shm seg: vpp's event qs NOT IN binary api svm region");
261       return is_valid;
262     }
263   else
264     return 1;
265 }
266
267 int
268 application_alloc_and_init (app_init_args_t * a)
269 {
270   ssvm_segment_type_t seg_type = SSVM_SEGMENT_MEMFD;
271   segment_manager_properties_t *props;
272   vl_api_registration_t *reg;
273   application_t *app;
274   u64 *options;
275
276   app = application_alloc ();
277   options = a->options;
278   /*
279    * Make sure we support the requested configuration
280    */
281   if (!(options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_IS_BUILTIN))
282     {
283       reg = vl_api_client_index_to_registration (a->api_client_index);
284       if (!reg)
285         return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
286       if (vl_api_registration_file_index (reg) == VL_API_INVALID_FI)
287         seg_type = SSVM_SEGMENT_SHM;
288     }
289   else
290     {
291       if (options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_EVT_MQ_USE_EVENTFD)
292         {
293           clib_warning ("mq eventfds can only be used if socket transport is "
294                         "used for api");
295           return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
296         }
297       seg_type = SSVM_SEGMENT_PRIVATE;
298     }
299
300   if (!application_verify_cfg (seg_type))
301     return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
302
303   /* Check that the obvious things are properly set up */
304   application_verify_cb_fns (a->session_cb_vft);
305
306   app->flags = options[APP_OPTIONS_FLAGS];
307   app->cb_fns = *a->session_cb_vft;
308   app->ns_index = options[APP_OPTIONS_NAMESPACE];
309   app->proxied_transports = options[APP_OPTIONS_PROXY_TRANSPORT];
310   app->name = vec_dup (a->name);
311
312   /* If no scope enabled, default to global */
313   if (!application_has_global_scope (app)
314       && !application_has_local_scope (app))
315     app->flags |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
316
317   props = application_segment_manager_properties (app);
318   segment_manager_properties_init (props);
319   props->segment_size = options[APP_OPTIONS_ADD_SEGMENT_SIZE];
320   props->prealloc_fifos = options[APP_OPTIONS_PREALLOC_FIFO_PAIRS];
321   if (options[APP_OPTIONS_ADD_SEGMENT_SIZE])
322     {
323       props->add_segment_size = options[APP_OPTIONS_ADD_SEGMENT_SIZE];
324       props->add_segment = 1;
325     }
326   if (options[APP_OPTIONS_RX_FIFO_SIZE])
327     props->rx_fifo_size = options[APP_OPTIONS_RX_FIFO_SIZE];
328   if (options[APP_OPTIONS_TX_FIFO_SIZE])
329     props->tx_fifo_size = options[APP_OPTIONS_TX_FIFO_SIZE];
330   if (options[APP_OPTIONS_EVT_QUEUE_SIZE])
331     props->evt_q_size = options[APP_OPTIONS_EVT_QUEUE_SIZE];
332   if (options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_EVT_MQ_USE_EVENTFD)
333     props->use_mq_eventfd = 1;
334   if (options[APP_OPTIONS_TLS_ENGINE])
335     app->tls_engine = options[APP_OPTIONS_TLS_ENGINE];
336   props->segment_type = seg_type;
337
338   /* Add app to lookup by api_client_index table */
339   if (!application_is_builtin (app))
340     application_api_table_add (app->app_index, a->api_client_index);
341   else
342     application_name_table_add (app);
343
344   a->app_index = app->app_index;
345
346   APP_DBG ("New app name: %v api index: %u index %u", app->name,
347            app->api_client_index, app->app_index);
348
349   return 0;
350 }
351
352 void
353 application_free (application_t * app)
354 {
355   app_worker_map_t *wrk_map;
356   app_worker_t *app_wrk;
357   u32 table_index;
358   local_session_t *ll;
359   session_endpoint_t sep;
360
361   /*
362    * The app event queue allocated in first segment is cleared with
363    * the segment manager. No need to explicitly free it.
364    */
365   APP_DBG ("Delete app name %v api index: %d index: %d", app->name,
366            app->api_client_index, app->app_index);
367
368   if (application_is_proxy (app))
369     application_remove_proxy (app);
370
371   /*
372    * Free workers
373    */
374
375   /* *INDENT-OFF* */
376   pool_flush (wrk_map, app->worker_maps, ({
377     app_wrk = app_worker_get (wrk_map->wrk_index);
378     app_worker_free (app_wrk);
379   }));
380   /* *INDENT-ON* */
381   pool_free (app->worker_maps);
382
383   /*
384    * Free local listeners. Global table unbinds stop local listeners
385    * as well, but if we have only local binds, these won't be cleaned up.
386    * Don't bother with local accepted sessions, we clean them when
387    * cleaning up the worker.
388    */
389   table_index = application_local_session_table (app);
390   /* *INDENT-OFF* */
391   pool_foreach (ll, app->local_listen_sessions, ({
392     application_local_listener_session_endpoint (ll, &sep);
393     session_lookup_del_session_endpoint (table_index, &sep);
394   }));
395   /* *INDENT-ON* */
396   pool_free (app->local_listen_sessions);
397
398   /*
399    * Cleanup remaining state
400    */
401   if (application_is_builtin (app))
402     application_name_table_del (app);
403   vec_free (app->name);
404   vec_free (app->tls_cert);
405   vec_free (app->tls_key);
406   pool_put (app_main.app_pool, app);
407 }
408
409 void
410 application_detach_process (application_t * app, u32 api_client_index)
411 {
412   vnet_app_worker_add_del_args_t _args = { 0 }, *args = &_args;
413   app_worker_map_t *wrk_map;
414   u32 *wrks = 0, *wrk_index;
415   app_worker_t *app_wrk;
416
417   if (api_client_index == ~0)
418     {
419       application_free (app);
420       return;
421     }
422
423   APP_DBG ("Detaching for app %v index %u api client index %u", app->name,
424            app->app_index, app->api_client_index);
425
426   /* *INDENT-OFF* */
427   pool_foreach (wrk_map, app->worker_maps, ({
428     app_wrk = app_worker_get (wrk_map->wrk_index);
429     if (app_wrk->api_client_index == api_client_index)
430       vec_add1 (wrks, app_wrk->wrk_index);
431   }));
432   /* *INDENT-ON* */
433
434   if (!vec_len (wrks))
435     {
436       clib_warning ("no workers for app %u api_index %u", app->app_index,
437                     api_client_index);
438       return;
439     }
440
441   args->app_index = app->app_index;
442   args->api_client_index = api_client_index;
443   vec_foreach (wrk_index, wrks)
444   {
445     app_wrk = app_worker_get (wrk_index[0]);
446     args->wrk_map_index = app_wrk->wrk_map_index;
447     args->is_add = 0;
448     vnet_app_worker_add_del (args);
449   }
450   vec_free (wrks);
451 }
452
453 app_worker_t *
454 application_get_worker (application_t * app, u32 wrk_map_index)
455 {
456   app_worker_map_t *map;
457   map = app_worker_map_get (app, wrk_map_index);
458   if (!map)
459     return 0;
460   return app_worker_get (map->wrk_index);
461 }
462
463 app_worker_t *
464 application_get_default_worker (application_t * app)
465 {
466   return application_get_worker (app, 0);
467 }
468
469 u32
470 application_n_workers (application_t * app)
471 {
472   return pool_elts (app->worker_maps);
473 }
474
475 app_worker_t *
476 application_listener_select_worker (stream_session_t * ls, u8 is_local)
477 {
478   app_listener_t *app_listener;
479   application_t *app;
480   u32 wrk_index;
481
482   app = application_get (ls->app_index);
483   if (!is_local)
484     app_listener = app_listener_get (app, ls->listener_db_index);
485   else
486     app_listener = app_local_listener_get (app, ls->listener_db_index);
487
488   wrk_index = clib_bitmap_next_set (app_listener->workers,
489                                     app_listener->accept_rotor + 1);
490   if (wrk_index == ~0)
491     wrk_index = clib_bitmap_first_set (app_listener->workers);
492
493   ASSERT (wrk_index != ~0);
494   app_listener->accept_rotor = wrk_index;
495   return application_get_worker (app, wrk_index);
496 }
497
498 app_worker_t *
499 app_worker_alloc (application_t * app)
500 {
501   app_worker_t *app_wrk;
502   pool_get (app_main.workers, app_wrk);
503   clib_memset (app_wrk, 0, sizeof (*app_wrk));
504   app_wrk->wrk_index = app_wrk - app_main.workers;
505   app_wrk->app_index = app->app_index;
506   app_wrk->wrk_map_index = ~0;
507   app_wrk->connects_seg_manager = APP_INVALID_SEGMENT_MANAGER_INDEX;
508   app_wrk->first_segment_manager = APP_INVALID_SEGMENT_MANAGER_INDEX;
509   app_wrk->local_segment_manager = APP_INVALID_SEGMENT_MANAGER_INDEX;
510   APP_DBG ("New app %v worker %u", app_get_name (app), app_wrk->wrk_index);
511   return app_wrk;
512 }
513
514 app_worker_t *
515 app_worker_get (u32 wrk_index)
516 {
517   return pool_elt_at_index (app_main.workers, wrk_index);
518 }
519
520 app_worker_t *
521 app_worker_get_if_valid (u32 wrk_index)
522 {
523   if (pool_is_free_index (app_main.workers, wrk_index))
524     return 0;
525   return pool_elt_at_index (app_main.workers, wrk_index);
526 }
527
528 void
529 app_worker_free (app_worker_t * app_wrk)
530 {
531   application_t *app = application_get (app_wrk->app_index);
532   vnet_unbind_args_t _a, *a = &_a;
533   u64 handle, *handles = 0;
534   segment_manager_t *sm;
535   u32 sm_index;
536   int i;
537
538   /*
539    *  Listener cleanup
540    */
541
542   /* *INDENT-OFF* */
543   hash_foreach (handle, sm_index, app_wrk->listeners_table,
544   ({
545     vec_add1 (handles, handle);
546     sm = segment_manager_get (sm_index);
547     sm->app_wrk_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
548   }));
549   /* *INDENT-ON* */
550
551   for (i = 0; i < vec_len (handles); i++)
552     {
553       a->app_index = app->app_index;
554       a->wrk_map_index = app_wrk->wrk_map_index;
555       a->handle = handles[i];
556       /* seg manager is removed when unbind completes */
557       vnet_unbind (a);
558     }
559
560   /*
561    * Connects segment manager cleanup
562    */
563
564   if (app_wrk->connects_seg_manager != APP_INVALID_SEGMENT_MANAGER_INDEX)
565     {
566       sm = segment_manager_get (app_wrk->connects_seg_manager);
567       sm->app_wrk_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
568       segment_manager_init_del (sm);
569     }
570
571   /* If first segment manager is used by a listener */
572   if (app_wrk->first_segment_manager != APP_INVALID_SEGMENT_MANAGER_INDEX
573       && app_wrk->first_segment_manager != app_wrk->connects_seg_manager)
574     {
575       sm = segment_manager_get (app_wrk->first_segment_manager);
576       sm->first_is_protected = 0;
577       sm->app_wrk_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
578       /* .. and has no fifos, e.g. it might be used for redirected sessions,
579        * remove it */
580       if (!segment_manager_has_fifos (sm))
581         segment_manager_del (sm);
582     }
583
584   /*
585    * Local sessions
586    */
587   app_worker_local_sessions_free (app_wrk);
588
589   pool_put (app_main.workers, app_wrk);
590   if (CLIB_DEBUG)
591     clib_memset (app_wrk, 0xfe, sizeof (*app_wrk));
592 }
593
594 int
595 app_worker_alloc_and_init (application_t * app, app_worker_t ** wrk)
596 {
597   app_worker_map_t *wrk_map;
598   app_worker_t *app_wrk;
599   segment_manager_t *sm;
600   int rv;
601
602   app_wrk = app_worker_alloc (app);
603   wrk_map = app_worker_map_alloc (app);
604   wrk_map->wrk_index = app_wrk->wrk_index;
605   app_wrk->wrk_map_index = app_worker_map_index (app, wrk_map);
606
607   /*
608    * Setup first segment manager
609    */
610   sm = segment_manager_new ();
611   sm->app_wrk_index = app_wrk->wrk_index;
612
613   if ((rv = segment_manager_init (sm, app->sm_properties.segment_size,
614                                   app->sm_properties.prealloc_fifos)))
615     {
616       app_worker_free (app_wrk);
617       return rv;
618     }
619   sm->first_is_protected = 1;
620
621   /*
622    * Setup app worker
623    */
624   app_wrk->first_segment_manager = segment_manager_index (sm);
625   app_wrk->listeners_table = hash_create (0, sizeof (u64));
626   app_wrk->event_queue = segment_manager_event_queue (sm);
627   app_wrk->app_is_builtin = application_is_builtin (app);
628
629   /*
630    * Segment manager for local sessions
631    */
632   sm = segment_manager_new ();
633   sm->app_wrk_index = app_wrk->wrk_index;
634   app_wrk->local_segment_manager = segment_manager_index (sm);
635   app_wrk->local_connects = hash_create (0, sizeof (u64));
636
637   *wrk = app_wrk;
638
639   return 0;
640 }
641
642 application_t *
643 app_worker_get_app (u32 wrk_index)
644 {
645   app_worker_t *app_wrk;
646   app_wrk = app_worker_get_if_valid (wrk_index);
647   if (!app_wrk)
648     return 0;
649   return application_get_if_valid (app_wrk->app_index);
650 }
651
652 static segment_manager_t *
653 app_worker_alloc_segment_manager (app_worker_t * app_wrk)
654 {
655   segment_manager_t *sm = 0;
656
657   /* If the first segment manager is not in use, don't allocate a new one */
658   if (app_wrk->first_segment_manager != APP_INVALID_SEGMENT_MANAGER_INDEX
659       && app_wrk->first_segment_manager_in_use == 0)
660     {
661       sm = segment_manager_get (app_wrk->first_segment_manager);
662       app_wrk->first_segment_manager_in_use = 1;
663       return sm;
664     }
665
666   sm = segment_manager_new ();
667   sm->app_wrk_index = app_wrk->wrk_index;
668
669   return sm;
670 }
671
672 int
673 app_worker_start_listen (app_worker_t * app_wrk, stream_session_t * ls)
674 {
675   segment_manager_t *sm;
676
677   /* Allocate segment manager. All sessions derived out of a listen session
678    * have fifos allocated by the same segment manager. */
679   if (!(sm = app_worker_alloc_segment_manager (app_wrk)))
680     return -1;
681
682   /* Add to app's listener table. Useful to find all child listeners
683    * when app goes down, although, just for unbinding this is not needed */
684   hash_set (app_wrk->listeners_table, listen_session_get_handle (ls),
685             segment_manager_index (sm));
686
687   if (!ls->server_rx_fifo
688       && session_transport_service_type (ls) == TRANSPORT_SERVICE_CL)
689     {
690       if (session_alloc_fifos (sm, ls))
691         return -1;
692     }
693   return 0;
694 }
695
696 int
697 app_worker_stop_listen (app_worker_t * app_wrk, session_handle_t handle)
698 {
699   segment_manager_t *sm;
700   uword *sm_indexp;
701
702   sm_indexp = hash_get (app_wrk->listeners_table, handle);
703   if (PREDICT_FALSE (!sm_indexp))
704     {
705       clib_warning ("listener handle was removed %llu!", handle);
706       return -1;
707     }
708
709   sm = segment_manager_get (*sm_indexp);
710   if (app_wrk->first_segment_manager == *sm_indexp)
711     {
712       /* Delete sessions but don't remove segment manager */
713       app_wrk->first_segment_manager_in_use = 0;
714       segment_manager_del_sessions (sm);
715     }
716   else
717     {
718       segment_manager_init_del (sm);
719     }
720   hash_unset (app_wrk->listeners_table, handle);
721
722   return 0;
723 }
724
725 /**
726  * Start listening local transport endpoint for requested transport.
727  *
728  * Creates a 'dummy' stream session with state LISTENING to be used in session
729  * lookups, prior to establishing connection. Requests transport to build
730  * it's own specific listening connection.
731  */
732 int
733 application_start_listen (application_t * app,
734                           session_endpoint_cfg_t * sep_ext,
735                           session_handle_t * res)
736 {
737   app_listener_t *app_listener;
738   u32 table_index, fib_proto;
739   session_endpoint_t *sep;
740   app_worker_t *app_wrk;
741   stream_session_t *ls;
742   session_handle_t lh;
743   session_type_t sst;
744
745   /*
746    * Check if sep is already listened on
747    */
748   sep = (session_endpoint_t *) sep_ext;
749   fib_proto = session_endpoint_fib_proto (sep);
750   table_index = application_session_table (app, fib_proto);
751   lh = session_lookup_endpoint_listener (table_index, sep, 1);
752   if (lh != SESSION_INVALID_HANDLE)
753     {
754       ls = listen_session_get_from_handle (lh);
755       if (ls->app_index != app->app_index)
756         return VNET_API_ERROR_ADDRESS_IN_USE;
757
758       app_wrk = app_worker_get (sep_ext->app_wrk_index);
759       if (ls->app_wrk_index == app_wrk->wrk_index)
760         return VNET_API_ERROR_ADDRESS_IN_USE;
761
762       if (app_worker_start_listen (app_wrk, ls))
763         return -1;
764
765       app_listener = app_listener_get (app, ls->listener_db_index);
766       app_listener->workers = clib_bitmap_set (app_listener->workers,
767                                                app_wrk->wrk_map_index, 1);
768
769       *res = listen_session_get_handle (ls);
770       return 0;
771     }
772
773   /*
774    * Allocate new listener for application
775    */
776   sst = session_type_from_proto_and_ip (sep_ext->transport_proto,
777                                         sep_ext->is_ip4);
778   ls = listen_session_new (0, sst);
779   ls->app_index = app->app_index;
780   lh = listen_session_get_handle (ls);
781   if (session_listen (ls, sep_ext))
782     goto err;
783
784
785   ls = listen_session_get_from_handle (lh);
786   app_listener = app_listener_alloc (app);
787   ls->listener_db_index = app_listener->al_index;
788
789   /*
790    * Setup app worker as a listener
791    */
792   app_wrk = app_worker_get (sep_ext->app_wrk_index);
793   ls->app_wrk_index = app_wrk->wrk_index;
794   if (app_worker_start_listen (app_wrk, ls))
795     goto err;
796   app_listener->workers = clib_bitmap_set (app_listener->workers,
797                                            app_wrk->wrk_map_index, 1);
798
799   *res = lh;
800   return 0;
801
802 err:
803   listen_session_del (ls);
804   return -1;
805 }
806
807 /**
808  * Stop listening on session associated to handle
809  *
810  * @param handle        listener handle
811  * @param app_index     index of the app owning the handle.
812  * @param app_wrk_index index of the worker requesting the stop
813  */
814 int
815 application_stop_listen (u32 app_index, u32 app_wrk_index,
816                          session_handle_t handle)
817 {
818   app_listener_t *app_listener;
819   stream_session_t *listener;
820   app_worker_t *app_wrk;
821   application_t *app;
822
823   listener = listen_session_get_from_handle (handle);
824   app = application_get (app_index);
825   if (PREDICT_FALSE (!app || app->app_index != listener->app_index))
826     {
827       clib_warning ("app doesn't own handle %llu!", handle);
828       return -1;
829     }
830
831   app_listener = app_listener_get (app, listener->listener_db_index);
832   if (!clib_bitmap_get (app_listener->workers, app_wrk_index))
833     {
834       clib_warning ("worker %u not listening on handle %lu", app_wrk_index,
835                     handle);
836       return 0;
837     }
838
839   app_wrk = application_get_worker (app, app_wrk_index);
840   app_worker_stop_listen (app_wrk, handle);
841   clib_bitmap_set_no_check (app_listener->workers, app_wrk_index, 0);
842
843   if (clib_bitmap_is_zero (app_listener->workers))
844     {
845       session_stop_listen (listener);
846       app_listener_free (app, app_listener);
847       listen_session_del (listener);
848     }
849
850   return 0;
851 }
852
853 int
854 app_worker_open_session (app_worker_t * app, session_endpoint_t * sep,
855                          u32 api_context)
856 {
857   int rv;
858
859   /* Make sure we have a segment manager for connects */
860   app_worker_alloc_connects_segment_manager (app);
861
862   if ((rv = session_open (app->wrk_index, sep, api_context)))
863     return rv;
864
865   return 0;
866 }
867
868 int
869 app_worker_alloc_connects_segment_manager (app_worker_t * app_wrk)
870 {
871   segment_manager_t *sm;
872
873   if (app_wrk->connects_seg_manager == APP_INVALID_SEGMENT_MANAGER_INDEX)
874     {
875       sm = app_worker_alloc_segment_manager (app_wrk);
876       if (sm == 0)
877         return -1;
878       app_wrk->connects_seg_manager = segment_manager_index (sm);
879     }
880   return 0;
881 }
882
883 segment_manager_t *
884 app_worker_get_connect_segment_manager (app_worker_t * app)
885 {
886   ASSERT (app->connects_seg_manager != (u32) ~ 0);
887   return segment_manager_get (app->connects_seg_manager);
888 }
889
890 segment_manager_t *
891 app_worker_get_listen_segment_manager (app_worker_t * app,
892                                        stream_session_t * listener)
893 {
894   uword *smp;
895   smp = hash_get (app->listeners_table, listen_session_get_handle (listener));
896   ASSERT (smp != 0);
897   return segment_manager_get (*smp);
898 }
899
900 clib_error_t *
901 vnet_app_worker_add_del (vnet_app_worker_add_del_args_t * a)
902 {
903   svm_fifo_segment_private_t *fs;
904   app_worker_map_t *wrk_map;
905   app_worker_t *app_wrk;
906   segment_manager_t *sm;
907   application_t *app;
908   int rv;
909
910   app = application_get (a->app_index);
911   if (!app)
912     return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
913                                    "App %u does not exist", a->app_index);
914
915   if (a->is_add)
916     {
917       if ((rv = app_worker_alloc_and_init (app, &app_wrk)))
918         return clib_error_return_code (0, rv, 0, "app wrk init: %d", rv);
919
920       /* Map worker api index to the app */
921       app_wrk->api_client_index = a->api_client_index;
922       application_api_table_add (app->app_index, a->api_client_index);
923
924       sm = segment_manager_get (app_wrk->first_segment_manager);
925       fs = segment_manager_get_segment_w_lock (sm, 0);
926       a->segment = &fs->ssvm;
927       segment_manager_segment_reader_unlock (sm);
928       a->evt_q = app_wrk->event_queue;
929       a->wrk_map_index = app_wrk->wrk_map_index;
930     }
931   else
932     {
933       wrk_map = app_worker_map_get (app, a->wrk_map_index);
934       if (!wrk_map)
935         return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
936                                        "App %u does not have worker %u",
937                                        app->app_index, a->wrk_map_index);
938       app_wrk = app_worker_get (wrk_map->wrk_index);
939       if (!app_wrk)
940         return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
941                                        "No worker %u", a->wrk_map_index);
942       application_api_table_del (app_wrk->api_client_index);
943       app_worker_free (app_wrk);
944       app_worker_map_free (app, wrk_map);
945       if (application_n_workers (app) == 0)
946         application_free (app);
947     }
948   return 0;
949 }
950
951 segment_manager_t *
952 application_get_local_segment_manager (app_worker_t * app)
953 {
954   return segment_manager_get (app->local_segment_manager);
955 }
956
957 segment_manager_t *
958 application_get_local_segment_manager_w_session (app_worker_t * app,
959                                                  local_session_t * ls)
960 {
961   stream_session_t *listener;
962   if (application_local_session_listener_has_transport (ls))
963     {
964       listener = listen_session_get (ls->listener_index);
965       return app_worker_get_listen_segment_manager (app, listener);
966     }
967   return segment_manager_get (app->local_segment_manager);
968 }
969
970 int
971 application_is_proxy (application_t * app)
972 {
973   return (app->flags & APP_OPTIONS_FLAGS_IS_PROXY);
974 }
975
976 int
977 application_is_builtin (application_t * app)
978 {
979   return (app->flags & APP_OPTIONS_FLAGS_IS_BUILTIN);
980 }
981
982 int
983 application_is_builtin_proxy (application_t * app)
984 {
985   return (application_is_proxy (app) && application_is_builtin (app));
986 }
987
988 u8
989 application_has_local_scope (application_t * app)
990 {
991   return app->flags & APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
992 }
993
994 u8
995 application_has_global_scope (application_t * app)
996 {
997   return app->flags & APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
998 }
999
1000 u8
1001 application_use_mq_for_ctrl (application_t * app)
1002 {
1003   return app->flags & APP_OPTIONS_FLAGS_USE_MQ_FOR_CTRL_MSGS;
1004 }
1005
1006 /**
1007  * Send an API message to the external app, to map new segment
1008  */
1009 int
1010 app_worker_add_segment_notify (u32 app_wrk_index, ssvm_private_t * fs)
1011 {
1012   app_worker_t *app_wrk = app_worker_get (app_wrk_index);
1013   application_t *app = application_get (app_wrk->app_index);
1014   return app->cb_fns.add_segment_callback (app_wrk->api_client_index, fs);
1015 }
1016
1017 u32
1018 application_n_listeners (app_worker_t * app)
1019 {
1020   return hash_elts (app->listeners_table);
1021 }
1022
1023 stream_session_t *
1024 app_worker_first_listener (app_worker_t * app, u8 fib_proto,
1025                            u8 transport_proto)
1026 {
1027   stream_session_t *listener;
1028   u64 handle;
1029   u32 sm_index;
1030   u8 sst;
1031
1032   sst = session_type_from_proto_and_ip (transport_proto,
1033                                         fib_proto == FIB_PROTOCOL_IP4);
1034
1035   /* *INDENT-OFF* */
1036    hash_foreach (handle, sm_index, app->listeners_table, ({
1037      listener = listen_session_get_from_handle (handle);
1038      if (listener->session_type == sst
1039          && listener->enqueue_epoch != SESSION_PROXY_LISTENER_INDEX)
1040        return listener;
1041    }));
1042   /* *INDENT-ON* */
1043
1044   return 0;
1045 }
1046
1047 u8
1048 app_worker_application_is_builtin (app_worker_t * app_wrk)
1049 {
1050   return app_wrk->app_is_builtin;
1051 }
1052
1053 stream_session_t *
1054 application_proxy_listener (app_worker_t * app, u8 fib_proto,
1055                             u8 transport_proto)
1056 {
1057   stream_session_t *listener;
1058   u64 handle;
1059   u32 sm_index;
1060   u8 sst;
1061
1062   sst = session_type_from_proto_and_ip (transport_proto,
1063                                         fib_proto == FIB_PROTOCOL_IP4);
1064
1065   /* *INDENT-OFF* */
1066    hash_foreach (handle, sm_index, app->listeners_table, ({
1067      listener = listen_session_get_from_handle (handle);
1068      if (listener->session_type == sst
1069          && listener->enqueue_epoch == SESSION_PROXY_LISTENER_INDEX)
1070        return listener;
1071    }));
1072   /* *INDENT-ON* */
1073
1074   return 0;
1075 }
1076
1077 static clib_error_t *
1078 application_start_stop_proxy_fib_proto (application_t * app, u8 fib_proto,
1079                                         u8 transport_proto, u8 is_start)
1080 {
1081   app_namespace_t *app_ns = app_namespace_get (app->ns_index);
1082   u8 is_ip4 = (fib_proto == FIB_PROTOCOL_IP4);
1083   session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
1084   transport_connection_t *tc;
1085   app_worker_t *app_wrk;
1086   stream_session_t *s;
1087   u64 handle;
1088
1089   /* TODO decide if we want proxy to be enabled for all workers */
1090   app_wrk = application_get_default_worker (app);
1091   if (is_start)
1092     {
1093       s = app_worker_first_listener (app_wrk, fib_proto, transport_proto);
1094       if (!s)
1095         {
1096           sep.is_ip4 = is_ip4;
1097           sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
1098           sep.sw_if_index = app_ns->sw_if_index;
1099           sep.transport_proto = transport_proto;
1100           sep.app_wrk_index = app_wrk->wrk_index;       /* only default */
1101           application_start_listen (app, &sep, &handle);
1102           s = listen_session_get_from_handle (handle);
1103           s->enqueue_epoch = SESSION_PROXY_LISTENER_INDEX;
1104         }
1105     }
1106   else
1107     {
1108       s = application_proxy_listener (app_wrk, fib_proto, transport_proto);
1109       ASSERT (s);
1110     }
1111
1112   tc = listen_session_get_transport (s);
1113
1114   if (!ip_is_zero (&tc->lcl_ip, 1))
1115     {
1116       u32 sti;
1117       sep.is_ip4 = is_ip4;
1118       sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
1119       sep.transport_proto = transport_proto;
1120       sep.port = 0;
1121       sti = session_lookup_get_index_for_fib (fib_proto, sep.fib_index);
1122       if (is_start)
1123         session_lookup_add_session_endpoint (sti,
1124                                              (session_endpoint_t *) & sep,
1125                                              s->session_index);
1126       else
1127         session_lookup_del_session_endpoint (sti,
1128                                              (session_endpoint_t *) & sep);
1129     }
1130
1131   return 0;
1132 }
1133
1134 static void
1135 application_start_stop_proxy_local_scope (application_t * app,
1136                                           u8 transport_proto, u8 is_start)
1137 {
1138   session_endpoint_t sep = SESSION_ENDPOINT_NULL;
1139   app_namespace_t *app_ns;
1140   app_ns = app_namespace_get (app->ns_index);
1141   sep.is_ip4 = 1;
1142   sep.transport_proto = transport_proto;
1143   sep.port = 0;
1144
1145   if (is_start)
1146     {
1147       session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
1148                                            app->app_index);
1149       sep.is_ip4 = 0;
1150       session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
1151                                            app->app_index);
1152     }
1153   else
1154     {
1155       session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
1156       sep.is_ip4 = 0;
1157       session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
1158     }
1159 }
1160
1161 void
1162 application_start_stop_proxy (application_t * app,
1163                               transport_proto_t transport_proto, u8 is_start)
1164 {
1165   if (application_has_local_scope (app))
1166     application_start_stop_proxy_local_scope (app, transport_proto, is_start);
1167
1168   if (application_has_global_scope (app))
1169     {
1170       application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP4,
1171                                               transport_proto, is_start);
1172       application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP6,
1173                                               transport_proto, is_start);
1174     }
1175 }
1176
1177 void
1178 application_setup_proxy (application_t * app)
1179 {
1180   u16 transports = app->proxied_transports;
1181   transport_proto_t tp;
1182
1183   ASSERT (application_is_proxy (app));
1184
1185   /* *INDENT-OFF* */
1186   transport_proto_foreach (tp, ({
1187     if (transports & (1 << tp))
1188       application_start_stop_proxy (app, tp, 1);
1189   }));
1190   /* *INDENT-ON* */
1191 }
1192
1193 void
1194 application_remove_proxy (application_t * app)
1195 {
1196   u16 transports = app->proxied_transports;
1197   transport_proto_t tp;
1198
1199   ASSERT (application_is_proxy (app));
1200
1201   /* *INDENT-OFF* */
1202   transport_proto_foreach (tp, ({
1203     if (transports & (1 << tp))
1204       application_start_stop_proxy (app, tp, 0);
1205   }));
1206   /* *INDENT-ON* */
1207 }
1208
1209 segment_manager_properties_t *
1210 application_segment_manager_properties (application_t * app)
1211 {
1212   return &app->sm_properties;
1213 }
1214
1215 segment_manager_properties_t *
1216 application_get_segment_manager_properties (u32 app_index)
1217 {
1218   application_t *app = application_get (app_index);
1219   return &app->sm_properties;
1220 }
1221
1222 static inline int
1223 app_enqueue_evt (svm_msg_q_t * mq, svm_msg_q_msg_t * msg, u8 lock)
1224 {
1225   if (PREDICT_FALSE (svm_msg_q_is_full (mq)))
1226     {
1227       clib_warning ("evt q full");
1228       svm_msg_q_free_msg (mq, msg);
1229       if (lock)
1230         svm_msg_q_unlock (mq);
1231       return -1;
1232     }
1233
1234   if (lock)
1235     {
1236       svm_msg_q_add_and_unlock (mq, msg);
1237       return 0;
1238     }
1239
1240   /* Even when not locking the ring, we must wait for queue mutex */
1241   if (svm_msg_q_add (mq, msg, SVM_Q_WAIT))
1242     {
1243       clib_warning ("msg q add returned");
1244       return -1;
1245     }
1246   return 0;
1247 }
1248
1249 static inline int
1250 app_send_io_evt_rx (app_worker_t * app_wrk, stream_session_t * s, u8 lock)
1251 {
1252   session_event_t *evt;
1253   svm_msg_q_msg_t msg;
1254   svm_msg_q_t *mq;
1255
1256   if (PREDICT_FALSE (s->session_state != SESSION_STATE_READY
1257                      && s->session_state != SESSION_STATE_LISTENING))
1258     {
1259       /* Session is closed so app will never clean up. Flush rx fifo */
1260       if (s->session_state == SESSION_STATE_CLOSED)
1261         svm_fifo_dequeue_drop_all (s->server_rx_fifo);
1262       return 0;
1263     }
1264
1265   if (app_worker_application_is_builtin (app_wrk))
1266     {
1267       application_t *app = application_get (app_wrk->app_index);
1268       return app->cb_fns.builtin_app_rx_callback (s);
1269     }
1270
1271   if (svm_fifo_has_event (s->server_rx_fifo)
1272       || svm_fifo_is_empty (s->server_rx_fifo))
1273     return 0;
1274
1275   mq = app_wrk->event_queue;
1276   if (lock)
1277     svm_msg_q_lock (mq);
1278
1279   if (PREDICT_FALSE (svm_msg_q_ring_is_full (mq, SESSION_MQ_IO_EVT_RING)))
1280     {
1281       clib_warning ("evt q rings full");
1282       if (lock)
1283         svm_msg_q_unlock (mq);
1284       return -1;
1285     }
1286
1287   msg = svm_msg_q_alloc_msg_w_ring (mq, SESSION_MQ_IO_EVT_RING);
1288   ASSERT (!svm_msg_q_msg_is_invalid (&msg));
1289
1290   evt = (session_event_t *) svm_msg_q_msg_data (mq, &msg);
1291   evt->fifo = s->server_rx_fifo;
1292   evt->event_type = FIFO_EVENT_APP_RX;
1293
1294   (void) svm_fifo_set_event (s->server_rx_fifo);
1295
1296   if (app_enqueue_evt (mq, &msg, lock))
1297     return -1;
1298   return 0;
1299 }
1300
1301 static inline int
1302 app_send_io_evt_tx (app_worker_t * app_wrk, stream_session_t * s, u8 lock)
1303 {
1304   svm_msg_q_t *mq;
1305   session_event_t *evt;
1306   svm_msg_q_msg_t msg;
1307
1308   if (app_worker_application_is_builtin (app_wrk))
1309     return 0;
1310
1311   mq = app_wrk->event_queue;
1312   if (lock)
1313     svm_msg_q_lock (mq);
1314
1315   if (PREDICT_FALSE (svm_msg_q_ring_is_full (mq, SESSION_MQ_IO_EVT_RING)))
1316     {
1317       clib_warning ("evt q rings full");
1318       if (lock)
1319         svm_msg_q_unlock (mq);
1320       return -1;
1321     }
1322
1323   msg = svm_msg_q_alloc_msg_w_ring (mq, SESSION_MQ_IO_EVT_RING);
1324   ASSERT (!svm_msg_q_msg_is_invalid (&msg));
1325
1326   evt = (session_event_t *) svm_msg_q_msg_data (mq, &msg);
1327   evt->event_type = FIFO_EVENT_APP_TX;
1328   evt->fifo = s->server_tx_fifo;
1329
1330   return app_enqueue_evt (mq, &msg, lock);
1331 }
1332
1333 /* *INDENT-OFF* */
1334 typedef int (app_send_evt_handler_fn) (app_worker_t *app,
1335                                        stream_session_t *s,
1336                                        u8 lock);
1337 static app_send_evt_handler_fn * const app_send_evt_handler_fns[3] = {
1338     app_send_io_evt_rx,
1339     0,
1340     app_send_io_evt_tx,
1341 };
1342 /* *INDENT-ON* */
1343
1344 /**
1345  * Send event to application
1346  *
1347  * Logic from queue perspective is non-blocking. If there's
1348  * not enough space to enqueue a message, we return.
1349  */
1350 int
1351 app_worker_send_event (app_worker_t * app, stream_session_t * s, u8 evt_type)
1352 {
1353   ASSERT (app && evt_type <= FIFO_EVENT_APP_TX);
1354   return app_send_evt_handler_fns[evt_type] (app, s, 0 /* lock */ );
1355 }
1356
1357 /**
1358  * Send event to application
1359  *
1360  * Logic from queue perspective is blocking. However, if queue is full,
1361  * we return.
1362  */
1363 int
1364 app_worker_lock_and_send_event (app_worker_t * app, stream_session_t * s,
1365                                 u8 evt_type)
1366 {
1367   return app_send_evt_handler_fns[evt_type] (app, s, 1 /* lock */ );
1368 }
1369
1370 local_session_t *
1371 application_local_session_alloc (app_worker_t * app_wrk)
1372 {
1373   local_session_t *s;
1374   pool_get (app_wrk->local_sessions, s);
1375   clib_memset (s, 0, sizeof (*s));
1376   s->app_wrk_index = app_wrk->wrk_index;
1377   s->session_index = s - app_wrk->local_sessions;
1378   s->session_type = session_type_from_proto_and_ip (TRANSPORT_PROTO_NONE, 0);
1379   return s;
1380 }
1381
1382 void
1383 application_local_session_free (app_worker_t * app, local_session_t * s)
1384 {
1385   pool_put (app->local_sessions, s);
1386   if (CLIB_DEBUG)
1387     clib_memset (s, 0xfc, sizeof (*s));
1388 }
1389
1390 local_session_t *
1391 application_get_local_session (app_worker_t * app_wrk, u32 session_index)
1392 {
1393   if (pool_is_free_index (app_wrk->local_sessions, session_index))
1394     return 0;
1395   return pool_elt_at_index (app_wrk->local_sessions, session_index);
1396 }
1397
1398 local_session_t *
1399 application_get_local_session_from_handle (session_handle_t handle)
1400 {
1401   app_worker_t *server_wrk;
1402   u32 session_index, server_wrk_index;
1403   local_session_parse_handle (handle, &server_wrk_index, &session_index);
1404   server_wrk = app_worker_get_if_valid (server_wrk_index);
1405   if (!server_wrk)
1406     return 0;
1407   return application_get_local_session (server_wrk, session_index);
1408 }
1409
1410 local_session_t *
1411 application_local_listen_session_alloc (application_t * app)
1412 {
1413   local_session_t *ll;
1414   pool_get (app->local_listen_sessions, ll);
1415   clib_memset (ll, 0, sizeof (*ll));
1416   return ll;
1417 }
1418
1419 u32
1420 application_local_listener_index (application_t * app, local_session_t * ll)
1421 {
1422   return (ll - app->local_listen_sessions);
1423 }
1424
1425 void
1426 application_local_listen_session_free (application_t * app,
1427                                        local_session_t * ll)
1428 {
1429   pool_put (app->local_listen_sessions, ll);
1430   if (CLIB_DEBUG)
1431     clib_memset (ll, 0xfb, sizeof (*ll));
1432 }
1433
1434 int
1435 application_start_local_listen (application_t * app,
1436                                 session_endpoint_cfg_t * sep_ext,
1437                                 session_handle_t * handle)
1438 {
1439   app_listener_t *app_listener;
1440   session_endpoint_t *sep;
1441   app_worker_t *app_wrk;
1442   session_handle_t lh;
1443   local_session_t *ll;
1444   u32 table_index;
1445
1446   sep = (session_endpoint_t *) sep_ext;
1447   table_index = application_local_session_table (app);
1448   app_wrk = app_worker_get (sep_ext->app_wrk_index);
1449
1450   /* An exact sep match, as opposed to session_lookup_local_listener */
1451   lh = session_lookup_endpoint_listener (table_index, sep, 1);
1452   if (lh != SESSION_INVALID_HANDLE)
1453     {
1454       ll = application_get_local_listener_w_handle (lh);
1455       if (ll->app_index != app->app_index)
1456         return VNET_API_ERROR_ADDRESS_IN_USE;
1457
1458       if (ll->app_wrk_index == app_wrk->wrk_index)
1459         return VNET_API_ERROR_ADDRESS_IN_USE;
1460
1461       app_listener = app_local_listener_get (app, ll->listener_db_index);
1462       app_listener->workers = clib_bitmap_set (app_listener->workers,
1463                                                app_wrk->wrk_map_index, 1);
1464       *handle = application_local_session_handle (ll);
1465       return 0;
1466     }
1467
1468   ll = application_local_listen_session_alloc (app);
1469   ll->session_type = session_type_from_proto_and_ip (TRANSPORT_PROTO_NONE, 0);
1470   ll->app_wrk_index = app_wrk->app_index;
1471   ll->session_index = application_local_listener_index (app, ll);
1472   ll->port = sep_ext->port;
1473   /* Store the original session type for the unbind */
1474   ll->listener_session_type =
1475     session_type_from_proto_and_ip (sep_ext->transport_proto,
1476                                     sep_ext->is_ip4);
1477   ll->transport_listener_index = ~0;
1478   ll->app_index = app->app_index;
1479
1480   app_listener = app_local_listener_alloc (app);
1481   ll->listener_db_index = app_listener->al_index;
1482   app_listener->workers = clib_bitmap_set (app_listener->workers,
1483                                            app_wrk->wrk_map_index, 1);
1484
1485   *handle = application_local_session_handle (ll);
1486   session_lookup_add_session_endpoint (table_index, sep, *handle);
1487
1488   return 0;
1489 }
1490
1491 /**
1492  * Clean up local session table. If we have a listener session use it to
1493  * find the port and proto. If not, the handle must be a local table handle
1494  * so parse it.
1495  */
1496 int
1497 application_stop_local_listen (u32 app_index, u32 wrk_map_index,
1498                                session_handle_t lh)
1499 {
1500   session_endpoint_t sep = SESSION_ENDPOINT_NULL;
1501   u32 table_index, ll_index, server_index;
1502   app_listener_t *app_listener;
1503   app_worker_t *server_wrk;
1504   stream_session_t *sl = 0;
1505   local_session_t *ll, *ls;
1506   application_t *server;
1507
1508   server = application_get (app_index);
1509   table_index = application_local_session_table (server);
1510
1511   /* We have both local and global table binds. Figure from global what
1512    * the sep we should be cleaning up is.
1513    */
1514   if (!session_handle_is_local (lh))
1515     {
1516       sl = listen_session_get_from_handle (lh);
1517       if (!sl || listen_session_get_local_session_endpoint (sl, &sep))
1518         {
1519           clib_warning ("broken listener");
1520           return -1;
1521         }
1522       lh = session_lookup_endpoint_listener (table_index, &sep, 0);
1523       if (lh == SESSION_INVALID_HANDLE)
1524         return -1;
1525     }
1526
1527   local_session_parse_handle (lh, &server_index, &ll_index);
1528   if (PREDICT_FALSE (server_index != app_index))
1529     {
1530       clib_warning ("app %u does not own local handle 0x%lx", app_index, lh);
1531       return -1;
1532     }
1533
1534   ll = application_get_local_listen_session (server, ll_index);
1535   if (PREDICT_FALSE (!ll))
1536     {
1537       clib_warning ("no local listener");
1538       return -1;
1539     }
1540
1541   app_listener = app_local_listener_get (server, ll->listener_db_index);
1542   if (!clib_bitmap_get (app_listener->workers, wrk_map_index))
1543     {
1544       clib_warning ("app wrk %u not listening on handle %lu", wrk_map_index,
1545                     lh);
1546       return -1;
1547     }
1548
1549   server_wrk = application_get_worker (server, wrk_map_index);
1550   /* *INDENT-OFF* */
1551   pool_foreach (ls, server_wrk->local_sessions, ({
1552     if (ls->listener_index == ll->session_index)
1553       application_local_session_disconnect (server_wrk->app_index, ls);
1554   }));
1555   /* *INDENT-ON* */
1556
1557   clib_bitmap_set_no_check (app_listener->workers, wrk_map_index, 0);
1558   if (clib_bitmap_is_zero (app_listener->workers))
1559     {
1560       app_local_listener_free (server, app_listener);
1561       application_local_listener_session_endpoint (ll, &sep);
1562       session_lookup_del_session_endpoint (table_index, &sep);
1563       application_local_listen_session_free (server, ll);
1564     }
1565
1566   return 0;
1567 }
1568
1569 static void
1570 application_local_session_fix_eventds (svm_msg_q_t * sq, svm_msg_q_t * cq)
1571 {
1572   int fd;
1573
1574   /*
1575    * segment manager initializes only the producer eventds, since vpp is
1576    * typically the producer. But for local sessions, we also pass to the
1577    * apps the mqs they listen on for events from peer apps, so they are also
1578    * consumer fds.
1579    */
1580   fd = svm_msg_q_get_producer_eventfd (sq);
1581   svm_msg_q_set_consumer_eventfd (sq, fd);
1582   fd = svm_msg_q_get_producer_eventfd (cq);
1583   svm_msg_q_set_consumer_eventfd (cq, fd);
1584 }
1585
1586 int
1587 application_local_session_connect (app_worker_t * client_wrk,
1588                                    app_worker_t * server_wrk,
1589                                    local_session_t * ll, u32 opaque)
1590 {
1591   u32 seg_size, evt_q_sz, evt_q_elts, margin = 16 << 10;
1592   segment_manager_properties_t *props, *cprops;
1593   u32 round_rx_fifo_sz, round_tx_fifo_sz;
1594   int rv, has_transport, seg_index;
1595   svm_fifo_segment_private_t *seg;
1596   application_t *server, *client;
1597   segment_manager_t *sm;
1598   local_session_t *ls;
1599   svm_msg_q_t *sq, *cq;
1600
1601   ls = application_local_session_alloc (server_wrk);
1602   server = application_get (server_wrk->app_index);
1603   client = application_get (client_wrk->app_index);
1604
1605   props = application_segment_manager_properties (server);
1606   cprops = application_segment_manager_properties (client);
1607   evt_q_elts = props->evt_q_size + cprops->evt_q_size;
1608   evt_q_sz = segment_manager_evt_q_expected_size (evt_q_elts);
1609   round_rx_fifo_sz = 1 << max_log2 (props->rx_fifo_size);
1610   round_tx_fifo_sz = 1 << max_log2 (props->tx_fifo_size);
1611   seg_size = round_rx_fifo_sz + round_tx_fifo_sz + evt_q_sz + margin;
1612
1613   has_transport = session_has_transport ((stream_session_t *) ll);
1614   if (!has_transport)
1615     {
1616       /* Local sessions don't have backing transport */
1617       ls->port = ll->port;
1618       sm = application_get_local_segment_manager (server_wrk);
1619     }
1620   else
1621     {
1622       stream_session_t *sl = (stream_session_t *) ll;
1623       transport_connection_t *tc;
1624       tc = listen_session_get_transport (sl);
1625       ls->port = tc->lcl_port;
1626       sm = app_worker_get_listen_segment_manager (server_wrk, sl);
1627     }
1628
1629   seg_index = segment_manager_add_segment (sm, seg_size);
1630   if (seg_index < 0)
1631     {
1632       clib_warning ("failed to add new cut-through segment");
1633       return seg_index;
1634     }
1635   seg = segment_manager_get_segment_w_lock (sm, seg_index);
1636   sq = segment_manager_alloc_queue (seg, props);
1637   cq = segment_manager_alloc_queue (seg, cprops);
1638
1639   if (props->use_mq_eventfd)
1640     application_local_session_fix_eventds (sq, cq);
1641
1642   ls->server_evt_q = pointer_to_uword (sq);
1643   ls->client_evt_q = pointer_to_uword (cq);
1644   rv = segment_manager_try_alloc_fifos (seg, props->rx_fifo_size,
1645                                         props->tx_fifo_size,
1646                                         &ls->server_rx_fifo,
1647                                         &ls->server_tx_fifo);
1648   if (rv)
1649     {
1650       clib_warning ("failed to add fifos in cut-through segment");
1651       segment_manager_segment_reader_unlock (sm);
1652       goto failed;
1653     }
1654   ls->server_rx_fifo->ct_session_index = ls->session_index;
1655   ls->server_tx_fifo->ct_session_index = ls->session_index;
1656   ls->svm_segment_index = seg_index;
1657   ls->listener_index = ll->session_index;
1658   ls->client_wrk_index = client_wrk->wrk_index;
1659   ls->client_opaque = opaque;
1660   ls->listener_session_type = ll->session_type;
1661   ls->session_state = SESSION_STATE_READY;
1662
1663   if ((rv = server->cb_fns.add_segment_callback (server_wrk->api_client_index,
1664                                                  &seg->ssvm)))
1665     {
1666       clib_warning ("failed to notify server of new segment");
1667       segment_manager_segment_reader_unlock (sm);
1668       goto failed;
1669     }
1670   segment_manager_segment_reader_unlock (sm);
1671   if ((rv = server->cb_fns.session_accept_callback ((stream_session_t *) ls)))
1672     {
1673       clib_warning ("failed to send accept cut-through notify to server");
1674       goto failed;
1675     }
1676   if (server->flags & APP_OPTIONS_FLAGS_IS_BUILTIN)
1677     application_local_session_connect_notify (ls);
1678
1679   return 0;
1680
1681 failed:
1682   if (!has_transport)
1683     segment_manager_del_segment (sm, seg);
1684   return rv;
1685 }
1686
1687 static uword
1688 application_client_local_connect_key (local_session_t * ls)
1689 {
1690   return ((uword) ls->app_wrk_index << 32 | (uword) ls->session_index);
1691 }
1692
1693 static void
1694 application_client_local_connect_key_parse (uword key, u32 * app_wrk_index,
1695                                             u32 * session_index)
1696 {
1697   *app_wrk_index = key >> 32;
1698   *session_index = key & 0xFFFFFFFF;
1699 }
1700
1701 int
1702 application_local_session_connect_notify (local_session_t * ls)
1703 {
1704   svm_fifo_segment_private_t *seg;
1705   app_worker_t *client_wrk, *server_wrk;
1706   segment_manager_t *sm;
1707   application_t *client;
1708   int rv, is_fail = 0;
1709   uword client_key;
1710
1711   client_wrk = app_worker_get (ls->client_wrk_index);
1712   server_wrk = app_worker_get (ls->app_wrk_index);
1713   client = application_get (client_wrk->app_index);
1714
1715   sm = application_get_local_segment_manager_w_session (server_wrk, ls);
1716   seg = segment_manager_get_segment_w_lock (sm, ls->svm_segment_index);
1717   if ((rv = client->cb_fns.add_segment_callback (client_wrk->api_client_index,
1718                                                  &seg->ssvm)))
1719     {
1720       clib_warning ("failed to notify client %u of new segment",
1721                     ls->client_wrk_index);
1722       segment_manager_segment_reader_unlock (sm);
1723       application_local_session_disconnect (ls->client_wrk_index, ls);
1724       is_fail = 1;
1725     }
1726   else
1727     {
1728       segment_manager_segment_reader_unlock (sm);
1729     }
1730
1731   client->cb_fns.session_connected_callback (client_wrk->wrk_index,
1732                                              ls->client_opaque,
1733                                              (stream_session_t *) ls,
1734                                              is_fail);
1735
1736   client_key = application_client_local_connect_key (ls);
1737   hash_set (client_wrk->local_connects, client_key, client_key);
1738   return 0;
1739 }
1740
1741 int
1742 application_local_session_cleanup (app_worker_t * client_wrk,
1743                                    app_worker_t * server_wrk,
1744                                    local_session_t * ls)
1745 {
1746   svm_fifo_segment_private_t *seg;
1747   stream_session_t *listener;
1748   segment_manager_t *sm;
1749   uword client_key;
1750   u8 has_transport;
1751
1752   /* Retrieve listener transport type as it is the one that decides where
1753    * the fifos are allocated */
1754   has_transport = application_local_session_listener_has_transport (ls);
1755   if (!has_transport)
1756     sm = application_get_local_segment_manager_w_session (server_wrk, ls);
1757   else
1758     {
1759       listener = listen_session_get (ls->listener_index);
1760       sm = app_worker_get_listen_segment_manager (server_wrk, listener);
1761     }
1762
1763   seg = segment_manager_get_segment (sm, ls->svm_segment_index);
1764   if (client_wrk)
1765     {
1766       client_key = application_client_local_connect_key (ls);
1767       hash_unset (client_wrk->local_connects, client_key);
1768     }
1769
1770   if (!has_transport)
1771     {
1772       application_t *server = application_get (server_wrk->app_index);
1773       server->cb_fns.del_segment_callback (server_wrk->api_client_index,
1774                                            &seg->ssvm);
1775       if (client_wrk)
1776         {
1777           application_t *client = application_get (client_wrk->app_index);
1778           client->cb_fns.del_segment_callback (client_wrk->api_client_index,
1779                                                &seg->ssvm);
1780         }
1781       segment_manager_del_segment (sm, seg);
1782     }
1783
1784   application_local_session_free (server_wrk, ls);
1785
1786   return 0;
1787 }
1788
1789 int
1790 application_local_session_disconnect (u32 app_index, local_session_t * ls)
1791 {
1792   app_worker_t *client_wrk, *server_wrk;
1793   u8 is_server = 0, is_client = 0;
1794   application_t *app;
1795
1796   app = application_get_if_valid (app_index);
1797   if (!app)
1798     return 0;
1799
1800   client_wrk = app_worker_get_if_valid (ls->client_wrk_index);
1801   server_wrk = app_worker_get (ls->app_wrk_index);
1802
1803   if (server_wrk->app_index == app_index)
1804     is_server = 1;
1805   else if (client_wrk && client_wrk->app_index == app_index)
1806     is_client = 1;
1807
1808   if (!is_server && !is_client)
1809     {
1810       clib_warning ("app %u is neither client nor server for session 0x%lx",
1811                     app_index, application_local_session_handle (ls));
1812       return VNET_API_ERROR_INVALID_VALUE;
1813     }
1814
1815   if (ls->session_state == SESSION_STATE_CLOSED)
1816     return application_local_session_cleanup (client_wrk, server_wrk, ls);
1817
1818   if (app_index == ls->client_wrk_index)
1819     {
1820       mq_send_local_session_disconnected_cb (ls->app_wrk_index, ls);
1821     }
1822   else
1823     {
1824       if (!client_wrk)
1825         {
1826           return application_local_session_cleanup (client_wrk, server_wrk,
1827                                                     ls);
1828         }
1829       else if (ls->session_state < SESSION_STATE_READY)
1830         {
1831           application_t *client = application_get (client_wrk->app_index);
1832           client->cb_fns.session_connected_callback (client_wrk->wrk_index,
1833                                                      ls->client_opaque,
1834                                                      (stream_session_t *) ls,
1835                                                      1 /* is_fail */ );
1836           ls->session_state = SESSION_STATE_CLOSED;
1837           return application_local_session_cleanup (client_wrk, server_wrk,
1838                                                     ls);
1839         }
1840       else
1841         {
1842           mq_send_local_session_disconnected_cb (client_wrk->wrk_index, ls);
1843         }
1844     }
1845
1846   ls->session_state = SESSION_STATE_CLOSED;
1847
1848   return 0;
1849 }
1850
1851 int
1852 application_local_session_disconnect_w_index (u32 app_wrk_index, u32 ls_index)
1853 {
1854   app_worker_t *app_wrk;
1855   local_session_t *ls;
1856   app_wrk = app_worker_get (app_wrk_index);
1857   ls = application_get_local_session (app_wrk, ls_index);
1858   return application_local_session_disconnect (app_wrk_index, ls);
1859 }
1860
1861 void
1862 app_worker_local_sessions_free (app_worker_t * app_wrk)
1863 {
1864   u32 index, server_wrk_index, session_index;
1865   u64 handle, *handles = 0;
1866   app_worker_t *server_wrk;
1867   segment_manager_t *sm;
1868   local_session_t *ls;
1869   int i;
1870
1871   /*
1872    * Local sessions
1873    */
1874   if (app_wrk->local_sessions)
1875     {
1876       /* *INDENT-OFF* */
1877       pool_foreach (ls, app_wrk->local_sessions, ({
1878         application_local_session_disconnect (app_wrk->wrk_index, ls);
1879       }));
1880       /* *INDENT-ON* */
1881     }
1882
1883   /*
1884    * Local connects
1885    */
1886   vec_reset_length (handles);
1887   /* *INDENT-OFF* */
1888   hash_foreach (handle, index, app_wrk->local_connects, ({
1889     vec_add1 (handles, handle);
1890   }));
1891   /* *INDENT-ON* */
1892
1893   for (i = 0; i < vec_len (handles); i++)
1894     {
1895       application_client_local_connect_key_parse (handles[i],
1896                                                   &server_wrk_index,
1897                                                   &session_index);
1898       server_wrk = app_worker_get_if_valid (server_wrk_index);
1899       if (server_wrk)
1900         {
1901           ls = application_get_local_session (server_wrk, session_index);
1902           application_local_session_disconnect (app_wrk->wrk_index, ls);
1903         }
1904     }
1905
1906   sm = segment_manager_get (app_wrk->local_segment_manager);
1907   sm->app_wrk_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
1908   segment_manager_del (sm);
1909 }
1910
1911 clib_error_t *
1912 vnet_app_add_tls_cert (vnet_app_add_tls_cert_args_t * a)
1913 {
1914   application_t *app;
1915   app = application_get (a->app_index);
1916   if (!app)
1917     return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1918                                    0, "app %u doesn't exist", a->app_index);
1919   app->tls_cert = vec_dup (a->cert);
1920   return 0;
1921 }
1922
1923 clib_error_t *
1924 vnet_app_add_tls_key (vnet_app_add_tls_key_args_t * a)
1925 {
1926   application_t *app;
1927   app = application_get (a->app_index);
1928   if (!app)
1929     return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1930                                    0, "app %u doesn't exist", a->app_index);
1931   app->tls_key = vec_dup (a->key);
1932   return 0;
1933 }
1934
1935 u8 *
1936 format_app_worker_listener (u8 * s, va_list * args)
1937 {
1938   app_worker_t *app_wrk = va_arg (*args, app_worker_t *);
1939   u64 handle = va_arg (*args, u64);
1940   u32 sm_index = va_arg (*args, u32);
1941   int verbose = va_arg (*args, int);
1942   stream_session_t *listener;
1943   const u8 *app_name;
1944   u8 *str;
1945
1946   if (!app_wrk)
1947     {
1948       if (verbose)
1949         s = format (s, "%-40s%-25s%=10s%-15s%-15s%-10s", "Connection", "App",
1950                     "Wrk", "API Client", "ListenerID", "SegManager");
1951       else
1952         s = format (s, "%-40s%-25s%=10s", "Connection", "App", "Wrk");
1953
1954       return s;
1955     }
1956
1957   app_name = application_name_from_index (app_wrk->app_index);
1958   listener = listen_session_get_from_handle (handle);
1959   str = format (0, "%U", format_stream_session, listener, verbose);
1960
1961   if (verbose)
1962     {
1963       char buf[32];
1964       sprintf (buf, "%u(%u)", app_wrk->wrk_map_index, app_wrk->wrk_index);
1965       s = format (s, "%-40s%-25s%=10s%-15u%-15u%-10u", str, app_name,
1966                   buf, app_wrk->api_client_index, handle, sm_index);
1967     }
1968   else
1969     s = format (s, "%-40s%-25s%=10u", str, app_name, app_wrk->wrk_map_index);
1970
1971   return s;
1972 }
1973
1974 static void
1975 application_format_listeners (application_t * app, int verbose)
1976 {
1977   vlib_main_t *vm = vlib_get_main ();
1978   app_worker_map_t *wrk_map;
1979   app_worker_t *app_wrk;
1980   u32 sm_index;
1981   u64 handle;
1982
1983   if (!app)
1984     {
1985       vlib_cli_output (vm, "%U", format_app_worker_listener, 0 /* header */ ,
1986                        0, 0, verbose);
1987       return;
1988     }
1989
1990   /* *INDENT-OFF* */
1991   pool_foreach (wrk_map, app->worker_maps, ({
1992     app_wrk = app_worker_get (wrk_map->wrk_index);
1993     if (hash_elts (app_wrk->listeners_table) == 0)
1994       continue;
1995     hash_foreach (handle, sm_index, app_wrk->listeners_table, ({
1996       vlib_cli_output (vm, "%U", format_app_worker_listener, app_wrk,
1997                        handle, sm_index, verbose);
1998     }));
1999   }));
2000   /* *INDENT-ON* */
2001 }
2002
2003 static void
2004 app_worker_format_connects (app_worker_t * app_wrk, int verbose)
2005 {
2006   svm_fifo_segment_private_t *fifo_segment;
2007   vlib_main_t *vm = vlib_get_main ();
2008   segment_manager_t *sm;
2009   const u8 *app_name;
2010   u8 *s = 0;
2011
2012   /* Header */
2013   if (!app_wrk)
2014     {
2015       if (verbose)
2016         vlib_cli_output (vm, "%-40s%-20s%-15s%-10s", "Connection", "App",
2017                          "API Client", "SegManager");
2018       else
2019         vlib_cli_output (vm, "%-40s%-20s", "Connection", "App");
2020       return;
2021     }
2022
2023   if (app_wrk->connects_seg_manager == (u32) ~ 0)
2024     return;
2025
2026   app_name = application_name_from_index (app_wrk->app_index);
2027
2028   /* Across all fifo segments */
2029   sm = segment_manager_get (app_wrk->connects_seg_manager);
2030
2031   /* *INDENT-OFF* */
2032   segment_manager_foreach_segment_w_lock (fifo_segment, sm, ({
2033     svm_fifo_t *fifo;
2034     u8 *str;
2035
2036     fifo = svm_fifo_segment_get_fifo_list (fifo_segment);
2037     while (fifo)
2038       {
2039         u32 session_index, thread_index;
2040         stream_session_t *session;
2041
2042         session_index = fifo->master_session_index;
2043         thread_index = fifo->master_thread_index;
2044
2045         session = session_get (session_index, thread_index);
2046         str = format (0, "%U", format_stream_session, session, verbose);
2047
2048         if (verbose)
2049           s = format (s, "%-40s%-20s%-15u%-10u", str, app_name,
2050                       app_wrk->api_client_index, app_wrk->connects_seg_manager);
2051         else
2052           s = format (s, "%-40s%-20s", str, app_name);
2053
2054         vlib_cli_output (vm, "%v", s);
2055         vec_reset_length (s);
2056         vec_free (str);
2057
2058         fifo = fifo->next;
2059       }
2060     vec_free (s);
2061   }));
2062   /* *INDENT-ON* */
2063
2064 }
2065
2066 static void
2067 application_format_connects (application_t * app, int verbose)
2068 {
2069   app_worker_map_t *wrk_map;
2070   app_worker_t *app_wrk;
2071
2072   if (!app)
2073     {
2074       app_worker_format_connects (0, verbose);
2075       return;
2076     }
2077
2078   /* *INDENT-OFF* */
2079   pool_foreach (wrk_map, app->worker_maps, ({
2080     app_wrk = app_worker_get (wrk_map->wrk_index);
2081     app_worker_format_connects (app_wrk, verbose);
2082   }));
2083   /* *INDENT-ON* */
2084 }
2085
2086 static void
2087 app_worker_format_local_sessions (app_worker_t * app_wrk, int verbose)
2088 {
2089   vlib_main_t *vm = vlib_get_main ();
2090   local_session_t *ls;
2091   transport_proto_t tp;
2092   u8 *conn = 0;
2093
2094   /* Header */
2095   if (app_wrk == 0)
2096     {
2097       vlib_cli_output (vm, "%-40s%-15s%-20s", "Connection", "ServerApp",
2098                        "ClientApp");
2099       return;
2100     }
2101
2102   if (!pool_elts (app_wrk->local_sessions)
2103       && !pool_elts (app_wrk->local_connects))
2104     return;
2105
2106   /* *INDENT-OFF* */
2107   pool_foreach (ls, app_wrk->local_sessions, ({
2108     tp = session_type_transport_proto(ls->listener_session_type);
2109     conn = format (0, "[L][%U] *:%u", format_transport_proto_short, tp,
2110                    ls->port);
2111     vlib_cli_output (vm, "%-40v%-15u%-20u", conn, ls->app_wrk_index,
2112                      ls->client_wrk_index);
2113     vec_reset_length (conn);
2114   }));
2115   /* *INDENT-ON* */
2116
2117   vec_free (conn);
2118 }
2119
2120 static void
2121 application_format_local_sessions (application_t * app, int verbose)
2122 {
2123   vlib_main_t *vm = vlib_get_main ();
2124   app_worker_map_t *wrk_map;
2125   app_worker_t *app_wrk;
2126   transport_proto_t tp;
2127   local_session_t *ls;
2128   u8 *conn = 0;
2129
2130   if (!app)
2131     {
2132       app_worker_format_local_sessions (0, verbose);
2133       return;
2134     }
2135
2136   /*
2137    * Format local listeners
2138    */
2139
2140   /* *INDENT-OFF* */
2141   pool_foreach (ls, app->local_listen_sessions, ({
2142     tp = session_type_transport_proto (ls->listener_session_type);
2143     conn = format (0, "[L][%U] *:%u", format_transport_proto_short, tp,
2144                    ls->port);
2145     vlib_cli_output (vm, "%-40v%-15u%-20s", conn, ls->app_wrk_index, "*");
2146     vec_reset_length (conn);
2147   }));
2148   /* *INDENT-ON* */
2149
2150   /*
2151    * Format local accepted/connected sessions
2152    */
2153   /* *INDENT-OFF* */
2154   pool_foreach (wrk_map, app->worker_maps, ({
2155     app_wrk = app_worker_get (wrk_map->wrk_index);
2156     app_worker_format_local_sessions (app_wrk, verbose);
2157   }));
2158   /* *INDENT-ON* */
2159 }
2160
2161 static void
2162 app_worker_format_local_connects (app_worker_t * app, int verbose)
2163 {
2164   vlib_main_t *vm = vlib_get_main ();
2165   u32 app_wrk_index, session_index;
2166   app_worker_t *server_wrk;
2167   local_session_t *ls;
2168   uword client_key;
2169   u64 value;
2170
2171   /* Header */
2172   if (app == 0)
2173     {
2174       if (verbose)
2175         vlib_cli_output (vm, "%-40s%-15s%-20s%-10s", "Connection", "App",
2176                          "Peer App", "SegManager");
2177       else
2178         vlib_cli_output (vm, "%-40s%-15s%-20s", "Connection", "App",
2179                          "Peer App");
2180       return;
2181     }
2182
2183   if (!app->local_connects)
2184     return;
2185
2186   /* *INDENT-OFF* */
2187   hash_foreach (client_key, value, app->local_connects, ({
2188     application_client_local_connect_key_parse (client_key, &app_wrk_index,
2189                                                 &session_index);
2190     server_wrk = app_worker_get (app_wrk_index);
2191     ls = application_get_local_session (server_wrk, session_index);
2192     vlib_cli_output (vm, "%-40s%-15s%-20s", "TODO", ls->app_wrk_index,
2193                      ls->client_wrk_index);
2194   }));
2195   /* *INDENT-ON* */
2196 }
2197
2198 static void
2199 application_format_local_connects (application_t * app, int verbose)
2200 {
2201   app_worker_map_t *wrk_map;
2202   app_worker_t *app_wrk;
2203
2204   if (!app)
2205     {
2206       app_worker_format_local_connects (0, verbose);
2207       return;
2208     }
2209
2210   /* *INDENT-OFF* */
2211   pool_foreach (wrk_map, app->worker_maps, ({
2212     app_wrk = app_worker_get (wrk_map->wrk_index);
2213     app_worker_format_local_connects (app_wrk, verbose);
2214   }));
2215   /* *INDENT-ON* */
2216 }
2217
2218 u8 *
2219 format_application_worker (u8 * s, va_list * args)
2220 {
2221   app_worker_t *app_wrk = va_arg (*args, app_worker_t *);
2222   u32 indent = 1;
2223
2224   s = format (s, "%U wrk-index %u app-index %u map-index %u "
2225               "api-client-index %d\n", format_white_space, indent,
2226               app_wrk->wrk_index, app_wrk->app_index, app_wrk->wrk_map_index,
2227               app_wrk->api_client_index);
2228   return s;
2229 }
2230
2231 u8 *
2232 format_application (u8 * s, va_list * args)
2233 {
2234   application_t *app = va_arg (*args, application_t *);
2235   CLIB_UNUSED (int verbose) = va_arg (*args, int);
2236   segment_manager_properties_t *props;
2237   const u8 *app_ns_name, *app_name;
2238   app_worker_map_t *wrk_map;
2239   app_worker_t *app_wrk;
2240
2241   if (app == 0)
2242     {
2243       if (!verbose)
2244         s = format (s, "%-10s%-20s%-40s", "Index", "Name", "Namespace");
2245       return s;
2246     }
2247
2248   app_name = app_get_name (app);
2249   app_ns_name = app_namespace_id_from_index (app->ns_index);
2250   props = application_segment_manager_properties (app);
2251   if (!verbose)
2252     {
2253       s = format (s, "%-10u%-20s%-40s", app->app_index, app_name,
2254                   app_ns_name);
2255       return s;
2256     }
2257
2258   s = format (s, "app-name %s app-index %u ns-index %u seg-size %U\n",
2259               app_name, app->app_index, app->ns_index,
2260               format_memory_size, props->add_segment_size);
2261   s = format (s, "rx-fifo-size %U tx-fifo-size %U workers:\n",
2262               format_memory_size, props->rx_fifo_size,
2263               format_memory_size, props->tx_fifo_size);
2264
2265   /* *INDENT-OFF* */
2266   pool_foreach (wrk_map, app->worker_maps, ({
2267       app_wrk = app_worker_get (wrk_map->wrk_index);
2268       s = format (s, "%U", format_application_worker, app_wrk);
2269   }));
2270   /* *INDENT-ON* */
2271
2272   return s;
2273 }
2274
2275 void
2276 application_format_all_listeners (vlib_main_t * vm, int do_local, int verbose)
2277 {
2278   application_t *app;
2279
2280   if (!pool_elts (app_main.app_pool))
2281     {
2282       vlib_cli_output (vm, "No active server bindings");
2283       return;
2284     }
2285
2286   if (do_local)
2287     {
2288       application_format_local_sessions (0, verbose);
2289       /* *INDENT-OFF* */
2290       pool_foreach (app, app_main.app_pool, ({
2291         application_format_local_sessions (app, verbose);
2292       }));
2293       /* *INDENT-ON* */
2294     }
2295   else
2296     {
2297       application_format_listeners (0, verbose);
2298
2299       /* *INDENT-OFF* */
2300       pool_foreach (app, app_main.app_pool, ({
2301         application_format_listeners (app, verbose);
2302       }));
2303       /* *INDENT-ON* */
2304     }
2305 }
2306
2307 void
2308 application_format_all_clients (vlib_main_t * vm, int do_local, int verbose)
2309 {
2310   application_t *app;
2311
2312   if (!pool_elts (app_main.app_pool))
2313     {
2314       vlib_cli_output (vm, "No active apps");
2315       return;
2316     }
2317
2318   if (do_local)
2319     {
2320       application_format_local_connects (0, verbose);
2321
2322       /* *INDENT-OFF* */
2323       pool_foreach (app, app_main.app_pool, ({
2324         application_format_local_connects (app, verbose);
2325       }));
2326       /* *INDENT-ON* */
2327     }
2328   else
2329     {
2330       application_format_connects (0, verbose);
2331
2332       /* *INDENT-OFF* */
2333       pool_foreach (app, app_main.app_pool, ({
2334         application_format_connects (app, verbose);
2335       }));
2336       /* *INDENT-ON* */
2337     }
2338 }
2339
2340 static clib_error_t *
2341 show_app_command_fn (vlib_main_t * vm, unformat_input_t * input,
2342                      vlib_cli_command_t * cmd)
2343 {
2344   int do_server = 0, do_client = 0, do_local = 0;
2345   application_t *app;
2346   u32 app_index = ~0;
2347   int verbose = 0;
2348
2349   session_cli_return_if_not_enabled ();
2350
2351   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2352     {
2353       if (unformat (input, "server"))
2354         do_server = 1;
2355       else if (unformat (input, "client"))
2356         do_client = 1;
2357       else if (unformat (input, "local"))
2358         do_local = 1;
2359       else if (unformat (input, "%u", &app_index))
2360         ;
2361       else if (unformat (input, "verbose"))
2362         verbose = 1;
2363       else
2364         return clib_error_return (0, "unknown input `%U'",
2365                                   format_unformat_error, input);
2366     }
2367
2368   if (do_server)
2369     {
2370       application_format_all_listeners (vm, do_local, verbose);
2371       return 0;
2372     }
2373
2374   if (do_client)
2375     {
2376       application_format_all_clients (vm, do_local, verbose);
2377       return 0;
2378     }
2379
2380   if (app_index != ~0)
2381     {
2382       app = application_get_if_valid (app_index);
2383       if (!app)
2384         return clib_error_return (0, "No app with index %u", app_index);
2385
2386       vlib_cli_output (vm, "%U", format_application, app, /* verbose */ 1);
2387       return 0;
2388     }
2389
2390   /* Print app related info */
2391   if (!do_server && !do_client)
2392     {
2393       vlib_cli_output (vm, "%U", format_application, 0, 0);
2394       /* *INDENT-OFF* */
2395       pool_foreach (app, app_main.app_pool, ({
2396         vlib_cli_output (vm, "%U", format_application, app, 0);
2397       }));
2398       /* *INDENT-ON* */
2399     }
2400
2401   return 0;
2402 }
2403
2404 /* *INDENT-OFF* */
2405 VLIB_CLI_COMMAND (show_app_command, static) =
2406 {
2407   .path = "show app",
2408   .short_help = "show app [server|client] [verbose]",
2409   .function = show_app_command_fn,
2410 };
2411 /* *INDENT-ON* */
2412
2413 /*
2414  * fd.io coding-style-patch-verification: ON
2415  *
2416  * Local Variables:
2417  * eval: (c-set-style "gnu")
2418  * End:
2419  */