vcl: wait for segments with segment handle
[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       a->segment_handle = segment_manager_segment_handle (sm, fs);
928       segment_manager_segment_reader_unlock (sm);
929       a->evt_q = app_wrk->event_queue;
930       a->wrk_map_index = app_wrk->wrk_map_index;
931     }
932   else
933     {
934       wrk_map = app_worker_map_get (app, a->wrk_map_index);
935       if (!wrk_map)
936         return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
937                                        "App %u does not have worker %u",
938                                        app->app_index, a->wrk_map_index);
939       app_wrk = app_worker_get (wrk_map->wrk_index);
940       if (!app_wrk)
941         return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
942                                        "No worker %u", a->wrk_map_index);
943       application_api_table_del (app_wrk->api_client_index);
944       app_worker_free (app_wrk);
945       app_worker_map_free (app, wrk_map);
946       if (application_n_workers (app) == 0)
947         application_free (app);
948     }
949   return 0;
950 }
951
952 segment_manager_t *
953 application_get_local_segment_manager (app_worker_t * app)
954 {
955   return segment_manager_get (app->local_segment_manager);
956 }
957
958 segment_manager_t *
959 application_get_local_segment_manager_w_session (app_worker_t * app,
960                                                  local_session_t * ls)
961 {
962   stream_session_t *listener;
963   if (application_local_session_listener_has_transport (ls))
964     {
965       listener = listen_session_get (ls->listener_index);
966       return app_worker_get_listen_segment_manager (app, listener);
967     }
968   return segment_manager_get (app->local_segment_manager);
969 }
970
971 int
972 application_is_proxy (application_t * app)
973 {
974   return (app->flags & APP_OPTIONS_FLAGS_IS_PROXY);
975 }
976
977 int
978 application_is_builtin (application_t * app)
979 {
980   return (app->flags & APP_OPTIONS_FLAGS_IS_BUILTIN);
981 }
982
983 int
984 application_is_builtin_proxy (application_t * app)
985 {
986   return (application_is_proxy (app) && application_is_builtin (app));
987 }
988
989 u8
990 application_has_local_scope (application_t * app)
991 {
992   return app->flags & APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
993 }
994
995 u8
996 application_has_global_scope (application_t * app)
997 {
998   return app->flags & APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
999 }
1000
1001 u8
1002 application_use_mq_for_ctrl (application_t * app)
1003 {
1004   return app->flags & APP_OPTIONS_FLAGS_USE_MQ_FOR_CTRL_MSGS;
1005 }
1006
1007 /**
1008  * Send an API message to the external app, to map new segment
1009  */
1010 int
1011 app_worker_add_segment_notify (u32 app_wrk_index, u64 segment_handle)
1012 {
1013   app_worker_t *app_wrk = app_worker_get (app_wrk_index);
1014   application_t *app = application_get (app_wrk->app_index);
1015   return app->cb_fns.add_segment_callback (app_wrk->api_client_index,
1016                                            segment_handle);
1017 }
1018
1019 u32
1020 application_n_listeners (app_worker_t * app)
1021 {
1022   return hash_elts (app->listeners_table);
1023 }
1024
1025 stream_session_t *
1026 app_worker_first_listener (app_worker_t * app, u8 fib_proto,
1027                            u8 transport_proto)
1028 {
1029   stream_session_t *listener;
1030   u64 handle;
1031   u32 sm_index;
1032   u8 sst;
1033
1034   sst = session_type_from_proto_and_ip (transport_proto,
1035                                         fib_proto == FIB_PROTOCOL_IP4);
1036
1037   /* *INDENT-OFF* */
1038    hash_foreach (handle, sm_index, app->listeners_table, ({
1039      listener = listen_session_get_from_handle (handle);
1040      if (listener->session_type == sst
1041          && listener->enqueue_epoch != SESSION_PROXY_LISTENER_INDEX)
1042        return listener;
1043    }));
1044   /* *INDENT-ON* */
1045
1046   return 0;
1047 }
1048
1049 u8
1050 app_worker_application_is_builtin (app_worker_t * app_wrk)
1051 {
1052   return app_wrk->app_is_builtin;
1053 }
1054
1055 stream_session_t *
1056 application_proxy_listener (app_worker_t * app, u8 fib_proto,
1057                             u8 transport_proto)
1058 {
1059   stream_session_t *listener;
1060   u64 handle;
1061   u32 sm_index;
1062   u8 sst;
1063
1064   sst = session_type_from_proto_and_ip (transport_proto,
1065                                         fib_proto == FIB_PROTOCOL_IP4);
1066
1067   /* *INDENT-OFF* */
1068    hash_foreach (handle, sm_index, app->listeners_table, ({
1069      listener = listen_session_get_from_handle (handle);
1070      if (listener->session_type == sst
1071          && listener->enqueue_epoch == SESSION_PROXY_LISTENER_INDEX)
1072        return listener;
1073    }));
1074   /* *INDENT-ON* */
1075
1076   return 0;
1077 }
1078
1079 static clib_error_t *
1080 application_start_stop_proxy_fib_proto (application_t * app, u8 fib_proto,
1081                                         u8 transport_proto, u8 is_start)
1082 {
1083   app_namespace_t *app_ns = app_namespace_get (app->ns_index);
1084   u8 is_ip4 = (fib_proto == FIB_PROTOCOL_IP4);
1085   session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
1086   transport_connection_t *tc;
1087   app_worker_t *app_wrk;
1088   stream_session_t *s;
1089   u64 handle;
1090
1091   /* TODO decide if we want proxy to be enabled for all workers */
1092   app_wrk = application_get_default_worker (app);
1093   if (is_start)
1094     {
1095       s = app_worker_first_listener (app_wrk, fib_proto, transport_proto);
1096       if (!s)
1097         {
1098           sep.is_ip4 = is_ip4;
1099           sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
1100           sep.sw_if_index = app_ns->sw_if_index;
1101           sep.transport_proto = transport_proto;
1102           sep.app_wrk_index = app_wrk->wrk_index;       /* only default */
1103           application_start_listen (app, &sep, &handle);
1104           s = listen_session_get_from_handle (handle);
1105           s->enqueue_epoch = SESSION_PROXY_LISTENER_INDEX;
1106         }
1107     }
1108   else
1109     {
1110       s = application_proxy_listener (app_wrk, fib_proto, transport_proto);
1111       ASSERT (s);
1112     }
1113
1114   tc = listen_session_get_transport (s);
1115
1116   if (!ip_is_zero (&tc->lcl_ip, 1))
1117     {
1118       u32 sti;
1119       sep.is_ip4 = is_ip4;
1120       sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
1121       sep.transport_proto = transport_proto;
1122       sep.port = 0;
1123       sti = session_lookup_get_index_for_fib (fib_proto, sep.fib_index);
1124       if (is_start)
1125         session_lookup_add_session_endpoint (sti,
1126                                              (session_endpoint_t *) & sep,
1127                                              s->session_index);
1128       else
1129         session_lookup_del_session_endpoint (sti,
1130                                              (session_endpoint_t *) & sep);
1131     }
1132
1133   return 0;
1134 }
1135
1136 static void
1137 application_start_stop_proxy_local_scope (application_t * app,
1138                                           u8 transport_proto, u8 is_start)
1139 {
1140   session_endpoint_t sep = SESSION_ENDPOINT_NULL;
1141   app_namespace_t *app_ns;
1142   app_ns = app_namespace_get (app->ns_index);
1143   sep.is_ip4 = 1;
1144   sep.transport_proto = transport_proto;
1145   sep.port = 0;
1146
1147   if (is_start)
1148     {
1149       session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
1150                                            app->app_index);
1151       sep.is_ip4 = 0;
1152       session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
1153                                            app->app_index);
1154     }
1155   else
1156     {
1157       session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
1158       sep.is_ip4 = 0;
1159       session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
1160     }
1161 }
1162
1163 void
1164 application_start_stop_proxy (application_t * app,
1165                               transport_proto_t transport_proto, u8 is_start)
1166 {
1167   if (application_has_local_scope (app))
1168     application_start_stop_proxy_local_scope (app, transport_proto, is_start);
1169
1170   if (application_has_global_scope (app))
1171     {
1172       application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP4,
1173                                               transport_proto, is_start);
1174       application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP6,
1175                                               transport_proto, is_start);
1176     }
1177 }
1178
1179 void
1180 application_setup_proxy (application_t * app)
1181 {
1182   u16 transports = app->proxied_transports;
1183   transport_proto_t tp;
1184
1185   ASSERT (application_is_proxy (app));
1186
1187   /* *INDENT-OFF* */
1188   transport_proto_foreach (tp, ({
1189     if (transports & (1 << tp))
1190       application_start_stop_proxy (app, tp, 1);
1191   }));
1192   /* *INDENT-ON* */
1193 }
1194
1195 void
1196 application_remove_proxy (application_t * app)
1197 {
1198   u16 transports = app->proxied_transports;
1199   transport_proto_t tp;
1200
1201   ASSERT (application_is_proxy (app));
1202
1203   /* *INDENT-OFF* */
1204   transport_proto_foreach (tp, ({
1205     if (transports & (1 << tp))
1206       application_start_stop_proxy (app, tp, 0);
1207   }));
1208   /* *INDENT-ON* */
1209 }
1210
1211 segment_manager_properties_t *
1212 application_segment_manager_properties (application_t * app)
1213 {
1214   return &app->sm_properties;
1215 }
1216
1217 segment_manager_properties_t *
1218 application_get_segment_manager_properties (u32 app_index)
1219 {
1220   application_t *app = application_get (app_index);
1221   return &app->sm_properties;
1222 }
1223
1224 static inline int
1225 app_enqueue_evt (svm_msg_q_t * mq, svm_msg_q_msg_t * msg, u8 lock)
1226 {
1227   if (PREDICT_FALSE (svm_msg_q_is_full (mq)))
1228     {
1229       clib_warning ("evt q full");
1230       svm_msg_q_free_msg (mq, msg);
1231       if (lock)
1232         svm_msg_q_unlock (mq);
1233       return -1;
1234     }
1235
1236   if (lock)
1237     {
1238       svm_msg_q_add_and_unlock (mq, msg);
1239       return 0;
1240     }
1241
1242   /* Even when not locking the ring, we must wait for queue mutex */
1243   if (svm_msg_q_add (mq, msg, SVM_Q_WAIT))
1244     {
1245       clib_warning ("msg q add returned");
1246       return -1;
1247     }
1248   return 0;
1249 }
1250
1251 static inline int
1252 app_send_io_evt_rx (app_worker_t * app_wrk, stream_session_t * s, u8 lock)
1253 {
1254   session_event_t *evt;
1255   svm_msg_q_msg_t msg;
1256   svm_msg_q_t *mq;
1257
1258   if (PREDICT_FALSE (s->session_state != SESSION_STATE_READY
1259                      && s->session_state != SESSION_STATE_LISTENING))
1260     {
1261       /* Session is closed so app will never clean up. Flush rx fifo */
1262       if (s->session_state == SESSION_STATE_CLOSED)
1263         svm_fifo_dequeue_drop_all (s->server_rx_fifo);
1264       return 0;
1265     }
1266
1267   if (app_worker_application_is_builtin (app_wrk))
1268     {
1269       application_t *app = application_get (app_wrk->app_index);
1270       return app->cb_fns.builtin_app_rx_callback (s);
1271     }
1272
1273   if (svm_fifo_has_event (s->server_rx_fifo)
1274       || svm_fifo_is_empty (s->server_rx_fifo))
1275     return 0;
1276
1277   mq = app_wrk->event_queue;
1278   if (lock)
1279     svm_msg_q_lock (mq);
1280
1281   if (PREDICT_FALSE (svm_msg_q_ring_is_full (mq, SESSION_MQ_IO_EVT_RING)))
1282     {
1283       clib_warning ("evt q rings full");
1284       if (lock)
1285         svm_msg_q_unlock (mq);
1286       return -1;
1287     }
1288
1289   msg = svm_msg_q_alloc_msg_w_ring (mq, SESSION_MQ_IO_EVT_RING);
1290   ASSERT (!svm_msg_q_msg_is_invalid (&msg));
1291
1292   evt = (session_event_t *) svm_msg_q_msg_data (mq, &msg);
1293   evt->fifo = s->server_rx_fifo;
1294   evt->event_type = FIFO_EVENT_APP_RX;
1295
1296   (void) svm_fifo_set_event (s->server_rx_fifo);
1297
1298   if (app_enqueue_evt (mq, &msg, lock))
1299     return -1;
1300   return 0;
1301 }
1302
1303 static inline int
1304 app_send_io_evt_tx (app_worker_t * app_wrk, stream_session_t * s, u8 lock)
1305 {
1306   svm_msg_q_t *mq;
1307   session_event_t *evt;
1308   svm_msg_q_msg_t msg;
1309
1310   if (app_worker_application_is_builtin (app_wrk))
1311     return 0;
1312
1313   mq = app_wrk->event_queue;
1314   if (lock)
1315     svm_msg_q_lock (mq);
1316
1317   if (PREDICT_FALSE (svm_msg_q_ring_is_full (mq, SESSION_MQ_IO_EVT_RING)))
1318     {
1319       clib_warning ("evt q rings full");
1320       if (lock)
1321         svm_msg_q_unlock (mq);
1322       return -1;
1323     }
1324
1325   msg = svm_msg_q_alloc_msg_w_ring (mq, SESSION_MQ_IO_EVT_RING);
1326   ASSERT (!svm_msg_q_msg_is_invalid (&msg));
1327
1328   evt = (session_event_t *) svm_msg_q_msg_data (mq, &msg);
1329   evt->event_type = FIFO_EVENT_APP_TX;
1330   evt->fifo = s->server_tx_fifo;
1331
1332   return app_enqueue_evt (mq, &msg, lock);
1333 }
1334
1335 /* *INDENT-OFF* */
1336 typedef int (app_send_evt_handler_fn) (app_worker_t *app,
1337                                        stream_session_t *s,
1338                                        u8 lock);
1339 static app_send_evt_handler_fn * const app_send_evt_handler_fns[3] = {
1340     app_send_io_evt_rx,
1341     0,
1342     app_send_io_evt_tx,
1343 };
1344 /* *INDENT-ON* */
1345
1346 /**
1347  * Send event to application
1348  *
1349  * Logic from queue perspective is non-blocking. If there's
1350  * not enough space to enqueue a message, we return.
1351  */
1352 int
1353 app_worker_send_event (app_worker_t * app, stream_session_t * s, u8 evt_type)
1354 {
1355   ASSERT (app && evt_type <= FIFO_EVENT_APP_TX);
1356   return app_send_evt_handler_fns[evt_type] (app, s, 0 /* lock */ );
1357 }
1358
1359 /**
1360  * Send event to application
1361  *
1362  * Logic from queue perspective is blocking. However, if queue is full,
1363  * we return.
1364  */
1365 int
1366 app_worker_lock_and_send_event (app_worker_t * app, stream_session_t * s,
1367                                 u8 evt_type)
1368 {
1369   return app_send_evt_handler_fns[evt_type] (app, s, 1 /* lock */ );
1370 }
1371
1372 local_session_t *
1373 application_local_session_alloc (app_worker_t * app_wrk)
1374 {
1375   local_session_t *s;
1376   pool_get (app_wrk->local_sessions, s);
1377   clib_memset (s, 0, sizeof (*s));
1378   s->app_wrk_index = app_wrk->wrk_index;
1379   s->session_index = s - app_wrk->local_sessions;
1380   s->session_type = session_type_from_proto_and_ip (TRANSPORT_PROTO_NONE, 0);
1381   return s;
1382 }
1383
1384 void
1385 application_local_session_free (app_worker_t * app, local_session_t * s)
1386 {
1387   pool_put (app->local_sessions, s);
1388   if (CLIB_DEBUG)
1389     clib_memset (s, 0xfc, sizeof (*s));
1390 }
1391
1392 local_session_t *
1393 application_get_local_session (app_worker_t * app_wrk, u32 session_index)
1394 {
1395   if (pool_is_free_index (app_wrk->local_sessions, session_index))
1396     return 0;
1397   return pool_elt_at_index (app_wrk->local_sessions, session_index);
1398 }
1399
1400 local_session_t *
1401 application_get_local_session_from_handle (session_handle_t handle)
1402 {
1403   app_worker_t *server_wrk;
1404   u32 session_index, server_wrk_index;
1405   local_session_parse_handle (handle, &server_wrk_index, &session_index);
1406   server_wrk = app_worker_get_if_valid (server_wrk_index);
1407   if (!server_wrk)
1408     return 0;
1409   return application_get_local_session (server_wrk, session_index);
1410 }
1411
1412 local_session_t *
1413 application_local_listen_session_alloc (application_t * app)
1414 {
1415   local_session_t *ll;
1416   pool_get (app->local_listen_sessions, ll);
1417   clib_memset (ll, 0, sizeof (*ll));
1418   return ll;
1419 }
1420
1421 u32
1422 application_local_listener_index (application_t * app, local_session_t * ll)
1423 {
1424   return (ll - app->local_listen_sessions);
1425 }
1426
1427 void
1428 application_local_listen_session_free (application_t * app,
1429                                        local_session_t * ll)
1430 {
1431   pool_put (app->local_listen_sessions, ll);
1432   if (CLIB_DEBUG)
1433     clib_memset (ll, 0xfb, sizeof (*ll));
1434 }
1435
1436 int
1437 application_start_local_listen (application_t * app,
1438                                 session_endpoint_cfg_t * sep_ext,
1439                                 session_handle_t * handle)
1440 {
1441   app_listener_t *app_listener;
1442   session_endpoint_t *sep;
1443   app_worker_t *app_wrk;
1444   session_handle_t lh;
1445   local_session_t *ll;
1446   u32 table_index;
1447
1448   sep = (session_endpoint_t *) sep_ext;
1449   table_index = application_local_session_table (app);
1450   app_wrk = app_worker_get (sep_ext->app_wrk_index);
1451
1452   /* An exact sep match, as opposed to session_lookup_local_listener */
1453   lh = session_lookup_endpoint_listener (table_index, sep, 1);
1454   if (lh != SESSION_INVALID_HANDLE)
1455     {
1456       ll = application_get_local_listener_w_handle (lh);
1457       if (ll->app_index != app->app_index)
1458         return VNET_API_ERROR_ADDRESS_IN_USE;
1459
1460       if (ll->app_wrk_index == app_wrk->wrk_index)
1461         return VNET_API_ERROR_ADDRESS_IN_USE;
1462
1463       app_listener = app_local_listener_get (app, ll->listener_db_index);
1464       app_listener->workers = clib_bitmap_set (app_listener->workers,
1465                                                app_wrk->wrk_map_index, 1);
1466       *handle = application_local_session_handle (ll);
1467       return 0;
1468     }
1469
1470   ll = application_local_listen_session_alloc (app);
1471   ll->session_type = session_type_from_proto_and_ip (TRANSPORT_PROTO_NONE, 0);
1472   ll->app_wrk_index = app_wrk->app_index;
1473   ll->session_index = application_local_listener_index (app, ll);
1474   ll->port = sep_ext->port;
1475   /* Store the original session type for the unbind */
1476   ll->listener_session_type =
1477     session_type_from_proto_and_ip (sep_ext->transport_proto,
1478                                     sep_ext->is_ip4);
1479   ll->transport_listener_index = ~0;
1480   ll->app_index = app->app_index;
1481
1482   app_listener = app_local_listener_alloc (app);
1483   ll->listener_db_index = app_listener->al_index;
1484   app_listener->workers = clib_bitmap_set (app_listener->workers,
1485                                            app_wrk->wrk_map_index, 1);
1486
1487   *handle = application_local_session_handle (ll);
1488   session_lookup_add_session_endpoint (table_index, sep, *handle);
1489
1490   return 0;
1491 }
1492
1493 /**
1494  * Clean up local session table. If we have a listener session use it to
1495  * find the port and proto. If not, the handle must be a local table handle
1496  * so parse it.
1497  */
1498 int
1499 application_stop_local_listen (u32 app_index, u32 wrk_map_index,
1500                                session_handle_t lh)
1501 {
1502   session_endpoint_t sep = SESSION_ENDPOINT_NULL;
1503   u32 table_index, ll_index, server_index;
1504   app_listener_t *app_listener;
1505   app_worker_t *server_wrk;
1506   stream_session_t *sl = 0;
1507   local_session_t *ll, *ls;
1508   application_t *server;
1509
1510   server = application_get (app_index);
1511   table_index = application_local_session_table (server);
1512
1513   /* We have both local and global table binds. Figure from global what
1514    * the sep we should be cleaning up is.
1515    */
1516   if (!session_handle_is_local (lh))
1517     {
1518       sl = listen_session_get_from_handle (lh);
1519       if (!sl || listen_session_get_local_session_endpoint (sl, &sep))
1520         {
1521           clib_warning ("broken listener");
1522           return -1;
1523         }
1524       lh = session_lookup_endpoint_listener (table_index, &sep, 0);
1525       if (lh == SESSION_INVALID_HANDLE)
1526         return -1;
1527     }
1528
1529   local_session_parse_handle (lh, &server_index, &ll_index);
1530   if (PREDICT_FALSE (server_index != app_index))
1531     {
1532       clib_warning ("app %u does not own local handle 0x%lx", app_index, lh);
1533       return -1;
1534     }
1535
1536   ll = application_get_local_listen_session (server, ll_index);
1537   if (PREDICT_FALSE (!ll))
1538     {
1539       clib_warning ("no local listener");
1540       return -1;
1541     }
1542
1543   app_listener = app_local_listener_get (server, ll->listener_db_index);
1544   if (!clib_bitmap_get (app_listener->workers, wrk_map_index))
1545     {
1546       clib_warning ("app wrk %u not listening on handle %lu", wrk_map_index,
1547                     lh);
1548       return -1;
1549     }
1550
1551   server_wrk = application_get_worker (server, wrk_map_index);
1552   /* *INDENT-OFF* */
1553   pool_foreach (ls, server_wrk->local_sessions, ({
1554     if (ls->listener_index == ll->session_index)
1555       application_local_session_disconnect (server_wrk->app_index, ls);
1556   }));
1557   /* *INDENT-ON* */
1558
1559   clib_bitmap_set_no_check (app_listener->workers, wrk_map_index, 0);
1560   if (clib_bitmap_is_zero (app_listener->workers))
1561     {
1562       app_local_listener_free (server, app_listener);
1563       application_local_listener_session_endpoint (ll, &sep);
1564       session_lookup_del_session_endpoint (table_index, &sep);
1565       application_local_listen_session_free (server, ll);
1566     }
1567
1568   return 0;
1569 }
1570
1571 static void
1572 application_local_session_fix_eventds (svm_msg_q_t * sq, svm_msg_q_t * cq)
1573 {
1574   int fd;
1575
1576   /*
1577    * segment manager initializes only the producer eventds, since vpp is
1578    * typically the producer. But for local sessions, we also pass to the
1579    * apps the mqs they listen on for events from peer apps, so they are also
1580    * consumer fds.
1581    */
1582   fd = svm_msg_q_get_producer_eventfd (sq);
1583   svm_msg_q_set_consumer_eventfd (sq, fd);
1584   fd = svm_msg_q_get_producer_eventfd (cq);
1585   svm_msg_q_set_consumer_eventfd (cq, fd);
1586 }
1587
1588 int
1589 application_local_session_connect (app_worker_t * client_wrk,
1590                                    app_worker_t * server_wrk,
1591                                    local_session_t * ll, u32 opaque)
1592 {
1593   u32 seg_size, evt_q_sz, evt_q_elts, margin = 16 << 10;
1594   u32 round_rx_fifo_sz, round_tx_fifo_sz, sm_index;
1595   segment_manager_properties_t *props, *cprops;
1596   int rv, has_transport, seg_index;
1597   svm_fifo_segment_private_t *seg;
1598   application_t *server, *client;
1599   segment_manager_t *sm;
1600   local_session_t *ls;
1601   svm_msg_q_t *sq, *cq;
1602   u64 segment_handle;
1603
1604   ls = application_local_session_alloc (server_wrk);
1605   server = application_get (server_wrk->app_index);
1606   client = application_get (client_wrk->app_index);
1607
1608   props = application_segment_manager_properties (server);
1609   cprops = application_segment_manager_properties (client);
1610   evt_q_elts = props->evt_q_size + cprops->evt_q_size;
1611   evt_q_sz = segment_manager_evt_q_expected_size (evt_q_elts);
1612   round_rx_fifo_sz = 1 << max_log2 (props->rx_fifo_size);
1613   round_tx_fifo_sz = 1 << max_log2 (props->tx_fifo_size);
1614   seg_size = round_rx_fifo_sz + round_tx_fifo_sz + evt_q_sz + margin;
1615
1616   has_transport = session_has_transport ((stream_session_t *) ll);
1617   if (!has_transport)
1618     {
1619       /* Local sessions don't have backing transport */
1620       ls->port = ll->port;
1621       sm = application_get_local_segment_manager (server_wrk);
1622     }
1623   else
1624     {
1625       stream_session_t *sl = (stream_session_t *) ll;
1626       transport_connection_t *tc;
1627       tc = listen_session_get_transport (sl);
1628       ls->port = tc->lcl_port;
1629       sm = app_worker_get_listen_segment_manager (server_wrk, sl);
1630     }
1631
1632   seg_index = segment_manager_add_segment (sm, seg_size);
1633   if (seg_index < 0)
1634     {
1635       clib_warning ("failed to add new cut-through segment");
1636       return seg_index;
1637     }
1638   seg = segment_manager_get_segment_w_lock (sm, seg_index);
1639   sq = segment_manager_alloc_queue (seg, props);
1640   cq = segment_manager_alloc_queue (seg, cprops);
1641
1642   if (props->use_mq_eventfd)
1643     application_local_session_fix_eventds (sq, cq);
1644
1645   ls->server_evt_q = pointer_to_uword (sq);
1646   ls->client_evt_q = pointer_to_uword (cq);
1647   rv = segment_manager_try_alloc_fifos (seg, props->rx_fifo_size,
1648                                         props->tx_fifo_size,
1649                                         &ls->server_rx_fifo,
1650                                         &ls->server_tx_fifo);
1651   if (rv)
1652     {
1653       clib_warning ("failed to add fifos in cut-through segment");
1654       segment_manager_segment_reader_unlock (sm);
1655       goto failed;
1656     }
1657   sm_index = segment_manager_index (sm);
1658   ls->server_rx_fifo->ct_session_index = ls->session_index;
1659   ls->server_tx_fifo->ct_session_index = ls->session_index;
1660   ls->server_rx_fifo->segment_manager = sm_index;
1661   ls->server_tx_fifo->segment_manager = sm_index;
1662   ls->server_rx_fifo->segment_index = seg_index;
1663   ls->server_tx_fifo->segment_index = seg_index;
1664   ls->svm_segment_index = seg_index;
1665   ls->listener_index = ll->session_index;
1666   ls->client_wrk_index = client_wrk->wrk_index;
1667   ls->client_opaque = opaque;
1668   ls->listener_session_type = ll->session_type;
1669   ls->session_state = SESSION_STATE_READY;
1670
1671   segment_handle = segment_manager_segment_handle (sm, seg);
1672   if ((rv = server->cb_fns.add_segment_callback (server_wrk->api_client_index,
1673                                                  segment_handle)))
1674     {
1675       clib_warning ("failed to notify server of new segment");
1676       segment_manager_segment_reader_unlock (sm);
1677       goto failed;
1678     }
1679   segment_manager_segment_reader_unlock (sm);
1680   if ((rv = server->cb_fns.session_accept_callback ((stream_session_t *) ls)))
1681     {
1682       clib_warning ("failed to send accept cut-through notify to server");
1683       goto failed;
1684     }
1685   if (server->flags & APP_OPTIONS_FLAGS_IS_BUILTIN)
1686     application_local_session_connect_notify (ls);
1687
1688   return 0;
1689
1690 failed:
1691   if (!has_transport)
1692     segment_manager_del_segment (sm, seg);
1693   return rv;
1694 }
1695
1696 static uword
1697 application_client_local_connect_key (local_session_t * ls)
1698 {
1699   return ((uword) ls->app_wrk_index << 32 | (uword) ls->session_index);
1700 }
1701
1702 static void
1703 application_client_local_connect_key_parse (uword key, u32 * app_wrk_index,
1704                                             u32 * session_index)
1705 {
1706   *app_wrk_index = key >> 32;
1707   *session_index = key & 0xFFFFFFFF;
1708 }
1709
1710 int
1711 application_local_session_connect_notify (local_session_t * ls)
1712 {
1713   svm_fifo_segment_private_t *seg;
1714   app_worker_t *client_wrk, *server_wrk;
1715   segment_manager_t *sm;
1716   application_t *client;
1717   int rv, is_fail = 0;
1718   u64 segment_handle;
1719   uword client_key;
1720
1721   client_wrk = app_worker_get (ls->client_wrk_index);
1722   server_wrk = app_worker_get (ls->app_wrk_index);
1723   client = application_get (client_wrk->app_index);
1724
1725   sm = application_get_local_segment_manager_w_session (server_wrk, ls);
1726   seg = segment_manager_get_segment_w_lock (sm, ls->svm_segment_index);
1727   segment_handle = segment_manager_segment_handle (sm, seg);
1728   if ((rv = client->cb_fns.add_segment_callback (client_wrk->api_client_index,
1729                                                  segment_handle)))
1730     {
1731       clib_warning ("failed to notify client %u of new segment",
1732                     ls->client_wrk_index);
1733       segment_manager_segment_reader_unlock (sm);
1734       application_local_session_disconnect (ls->client_wrk_index, ls);
1735       is_fail = 1;
1736     }
1737   else
1738     {
1739       segment_manager_segment_reader_unlock (sm);
1740     }
1741
1742   client->cb_fns.session_connected_callback (client_wrk->wrk_index,
1743                                              ls->client_opaque,
1744                                              (stream_session_t *) ls,
1745                                              is_fail);
1746
1747   client_key = application_client_local_connect_key (ls);
1748   hash_set (client_wrk->local_connects, client_key, client_key);
1749   return 0;
1750 }
1751
1752 int
1753 application_local_session_cleanup (app_worker_t * client_wrk,
1754                                    app_worker_t * server_wrk,
1755                                    local_session_t * ls)
1756 {
1757   svm_fifo_segment_private_t *seg;
1758   stream_session_t *listener;
1759   segment_manager_t *sm;
1760   uword client_key;
1761   u8 has_transport;
1762
1763   /* Retrieve listener transport type as it is the one that decides where
1764    * the fifos are allocated */
1765   has_transport = application_local_session_listener_has_transport (ls);
1766   if (!has_transport)
1767     sm = application_get_local_segment_manager_w_session (server_wrk, ls);
1768   else
1769     {
1770       listener = listen_session_get (ls->listener_index);
1771       sm = app_worker_get_listen_segment_manager (server_wrk, listener);
1772     }
1773
1774   seg = segment_manager_get_segment (sm, ls->svm_segment_index);
1775   if (client_wrk)
1776     {
1777       client_key = application_client_local_connect_key (ls);
1778       hash_unset (client_wrk->local_connects, client_key);
1779     }
1780
1781   if (!has_transport)
1782     {
1783       application_t *server = application_get (server_wrk->app_index);
1784       u64 segment_handle = segment_manager_segment_handle (sm, seg);
1785       server->cb_fns.del_segment_callback (server_wrk->api_client_index,
1786                                            segment_handle);
1787       if (client_wrk)
1788         {
1789           application_t *client = application_get (client_wrk->app_index);
1790           client->cb_fns.del_segment_callback (client_wrk->api_client_index,
1791                                                segment_handle);
1792         }
1793       segment_manager_del_segment (sm, seg);
1794     }
1795
1796   application_local_session_free (server_wrk, ls);
1797
1798   return 0;
1799 }
1800
1801 int
1802 application_local_session_disconnect (u32 app_index, local_session_t * ls)
1803 {
1804   app_worker_t *client_wrk, *server_wrk;
1805   u8 is_server = 0, is_client = 0;
1806   application_t *app;
1807
1808   app = application_get_if_valid (app_index);
1809   if (!app)
1810     return 0;
1811
1812   client_wrk = app_worker_get_if_valid (ls->client_wrk_index);
1813   server_wrk = app_worker_get (ls->app_wrk_index);
1814
1815   if (server_wrk->app_index == app_index)
1816     is_server = 1;
1817   else if (client_wrk && client_wrk->app_index == app_index)
1818     is_client = 1;
1819
1820   if (!is_server && !is_client)
1821     {
1822       clib_warning ("app %u is neither client nor server for session 0x%lx",
1823                     app_index, application_local_session_handle (ls));
1824       return VNET_API_ERROR_INVALID_VALUE;
1825     }
1826
1827   if (ls->session_state == SESSION_STATE_CLOSED)
1828     return application_local_session_cleanup (client_wrk, server_wrk, ls);
1829
1830   if (app_index == ls->client_wrk_index)
1831     {
1832       mq_send_local_session_disconnected_cb (ls->app_wrk_index, ls);
1833     }
1834   else
1835     {
1836       if (!client_wrk)
1837         {
1838           return application_local_session_cleanup (client_wrk, server_wrk,
1839                                                     ls);
1840         }
1841       else if (ls->session_state < SESSION_STATE_READY)
1842         {
1843           application_t *client = application_get (client_wrk->app_index);
1844           client->cb_fns.session_connected_callback (client_wrk->wrk_index,
1845                                                      ls->client_opaque,
1846                                                      (stream_session_t *) ls,
1847                                                      1 /* is_fail */ );
1848           ls->session_state = SESSION_STATE_CLOSED;
1849           return application_local_session_cleanup (client_wrk, server_wrk,
1850                                                     ls);
1851         }
1852       else
1853         {
1854           mq_send_local_session_disconnected_cb (client_wrk->wrk_index, ls);
1855         }
1856     }
1857
1858   ls->session_state = SESSION_STATE_CLOSED;
1859
1860   return 0;
1861 }
1862
1863 int
1864 application_local_session_disconnect_w_index (u32 app_wrk_index, u32 ls_index)
1865 {
1866   app_worker_t *app_wrk;
1867   local_session_t *ls;
1868   app_wrk = app_worker_get (app_wrk_index);
1869   ls = application_get_local_session (app_wrk, ls_index);
1870   return application_local_session_disconnect (app_wrk_index, ls);
1871 }
1872
1873 void
1874 app_worker_local_sessions_free (app_worker_t * app_wrk)
1875 {
1876   u32 index, server_wrk_index, session_index;
1877   u64 handle, *handles = 0;
1878   app_worker_t *server_wrk;
1879   segment_manager_t *sm;
1880   local_session_t *ls;
1881   int i;
1882
1883   /*
1884    * Local sessions
1885    */
1886   if (app_wrk->local_sessions)
1887     {
1888       /* *INDENT-OFF* */
1889       pool_foreach (ls, app_wrk->local_sessions, ({
1890         application_local_session_disconnect (app_wrk->wrk_index, ls);
1891       }));
1892       /* *INDENT-ON* */
1893     }
1894
1895   /*
1896    * Local connects
1897    */
1898   vec_reset_length (handles);
1899   /* *INDENT-OFF* */
1900   hash_foreach (handle, index, app_wrk->local_connects, ({
1901     vec_add1 (handles, handle);
1902   }));
1903   /* *INDENT-ON* */
1904
1905   for (i = 0; i < vec_len (handles); i++)
1906     {
1907       application_client_local_connect_key_parse (handles[i],
1908                                                   &server_wrk_index,
1909                                                   &session_index);
1910       server_wrk = app_worker_get_if_valid (server_wrk_index);
1911       if (server_wrk)
1912         {
1913           ls = application_get_local_session (server_wrk, session_index);
1914           application_local_session_disconnect (app_wrk->wrk_index, ls);
1915         }
1916     }
1917
1918   sm = segment_manager_get (app_wrk->local_segment_manager);
1919   sm->app_wrk_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
1920   segment_manager_del (sm);
1921 }
1922
1923 clib_error_t *
1924 vnet_app_add_tls_cert (vnet_app_add_tls_cert_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_cert = vec_dup (a->cert);
1932   return 0;
1933 }
1934
1935 clib_error_t *
1936 vnet_app_add_tls_key (vnet_app_add_tls_key_args_t * a)
1937 {
1938   application_t *app;
1939   app = application_get (a->app_index);
1940   if (!app)
1941     return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1942                                    0, "app %u doesn't exist", a->app_index);
1943   app->tls_key = vec_dup (a->key);
1944   return 0;
1945 }
1946
1947 u8 *
1948 format_app_worker_listener (u8 * s, va_list * args)
1949 {
1950   app_worker_t *app_wrk = va_arg (*args, app_worker_t *);
1951   u64 handle = va_arg (*args, u64);
1952   u32 sm_index = va_arg (*args, u32);
1953   int verbose = va_arg (*args, int);
1954   stream_session_t *listener;
1955   const u8 *app_name;
1956   u8 *str;
1957
1958   if (!app_wrk)
1959     {
1960       if (verbose)
1961         s = format (s, "%-40s%-25s%=10s%-15s%-15s%-10s", "Connection", "App",
1962                     "Wrk", "API Client", "ListenerID", "SegManager");
1963       else
1964         s = format (s, "%-40s%-25s%=10s", "Connection", "App", "Wrk");
1965
1966       return s;
1967     }
1968
1969   app_name = application_name_from_index (app_wrk->app_index);
1970   listener = listen_session_get_from_handle (handle);
1971   str = format (0, "%U", format_stream_session, listener, verbose);
1972
1973   if (verbose)
1974     {
1975       char buf[32];
1976       sprintf (buf, "%u(%u)", app_wrk->wrk_map_index, app_wrk->wrk_index);
1977       s = format (s, "%-40s%-25s%=10s%-15u%-15u%-10u", str, app_name,
1978                   buf, app_wrk->api_client_index, handle, sm_index);
1979     }
1980   else
1981     s = format (s, "%-40s%-25s%=10u", str, app_name, app_wrk->wrk_map_index);
1982
1983   return s;
1984 }
1985
1986 static void
1987 application_format_listeners (application_t * app, int verbose)
1988 {
1989   vlib_main_t *vm = vlib_get_main ();
1990   app_worker_map_t *wrk_map;
1991   app_worker_t *app_wrk;
1992   u32 sm_index;
1993   u64 handle;
1994
1995   if (!app)
1996     {
1997       vlib_cli_output (vm, "%U", format_app_worker_listener, 0 /* header */ ,
1998                        0, 0, verbose);
1999       return;
2000     }
2001
2002   /* *INDENT-OFF* */
2003   pool_foreach (wrk_map, app->worker_maps, ({
2004     app_wrk = app_worker_get (wrk_map->wrk_index);
2005     if (hash_elts (app_wrk->listeners_table) == 0)
2006       continue;
2007     hash_foreach (handle, sm_index, app_wrk->listeners_table, ({
2008       vlib_cli_output (vm, "%U", format_app_worker_listener, app_wrk,
2009                        handle, sm_index, verbose);
2010     }));
2011   }));
2012   /* *INDENT-ON* */
2013 }
2014
2015 static void
2016 app_worker_format_connects (app_worker_t * app_wrk, int verbose)
2017 {
2018   svm_fifo_segment_private_t *fifo_segment;
2019   vlib_main_t *vm = vlib_get_main ();
2020   segment_manager_t *sm;
2021   const u8 *app_name;
2022   u8 *s = 0;
2023
2024   /* Header */
2025   if (!app_wrk)
2026     {
2027       if (verbose)
2028         vlib_cli_output (vm, "%-40s%-20s%-15s%-10s", "Connection", "App",
2029                          "API Client", "SegManager");
2030       else
2031         vlib_cli_output (vm, "%-40s%-20s", "Connection", "App");
2032       return;
2033     }
2034
2035   if (app_wrk->connects_seg_manager == (u32) ~ 0)
2036     return;
2037
2038   app_name = application_name_from_index (app_wrk->app_index);
2039
2040   /* Across all fifo segments */
2041   sm = segment_manager_get (app_wrk->connects_seg_manager);
2042
2043   /* *INDENT-OFF* */
2044   segment_manager_foreach_segment_w_lock (fifo_segment, sm, ({
2045     svm_fifo_t *fifo;
2046     u8 *str;
2047
2048     fifo = svm_fifo_segment_get_fifo_list (fifo_segment);
2049     while (fifo)
2050       {
2051         u32 session_index, thread_index;
2052         stream_session_t *session;
2053
2054         session_index = fifo->master_session_index;
2055         thread_index = fifo->master_thread_index;
2056
2057         session = session_get (session_index, thread_index);
2058         str = format (0, "%U", format_stream_session, session, verbose);
2059
2060         if (verbose)
2061           s = format (s, "%-40s%-20s%-15u%-10u", str, app_name,
2062                       app_wrk->api_client_index, app_wrk->connects_seg_manager);
2063         else
2064           s = format (s, "%-40s%-20s", str, app_name);
2065
2066         vlib_cli_output (vm, "%v", s);
2067         vec_reset_length (s);
2068         vec_free (str);
2069
2070         fifo = fifo->next;
2071       }
2072     vec_free (s);
2073   }));
2074   /* *INDENT-ON* */
2075
2076 }
2077
2078 static void
2079 application_format_connects (application_t * app, int verbose)
2080 {
2081   app_worker_map_t *wrk_map;
2082   app_worker_t *app_wrk;
2083
2084   if (!app)
2085     {
2086       app_worker_format_connects (0, verbose);
2087       return;
2088     }
2089
2090   /* *INDENT-OFF* */
2091   pool_foreach (wrk_map, app->worker_maps, ({
2092     app_wrk = app_worker_get (wrk_map->wrk_index);
2093     app_worker_format_connects (app_wrk, verbose);
2094   }));
2095   /* *INDENT-ON* */
2096 }
2097
2098 static void
2099 app_worker_format_local_sessions (app_worker_t * app_wrk, int verbose)
2100 {
2101   vlib_main_t *vm = vlib_get_main ();
2102   local_session_t *ls;
2103   transport_proto_t tp;
2104   u8 *conn = 0;
2105
2106   /* Header */
2107   if (app_wrk == 0)
2108     {
2109       vlib_cli_output (vm, "%-40s%-15s%-20s", "Connection", "ServerApp",
2110                        "ClientApp");
2111       return;
2112     }
2113
2114   if (!pool_elts (app_wrk->local_sessions)
2115       && !pool_elts (app_wrk->local_connects))
2116     return;
2117
2118   /* *INDENT-OFF* */
2119   pool_foreach (ls, app_wrk->local_sessions, ({
2120     tp = session_type_transport_proto(ls->listener_session_type);
2121     conn = format (0, "[L][%U] *:%u", format_transport_proto_short, tp,
2122                    ls->port);
2123     vlib_cli_output (vm, "%-40v%-15u%-20u", conn, ls->app_wrk_index,
2124                      ls->client_wrk_index);
2125     vec_reset_length (conn);
2126   }));
2127   /* *INDENT-ON* */
2128
2129   vec_free (conn);
2130 }
2131
2132 static void
2133 application_format_local_sessions (application_t * app, int verbose)
2134 {
2135   vlib_main_t *vm = vlib_get_main ();
2136   app_worker_map_t *wrk_map;
2137   app_worker_t *app_wrk;
2138   transport_proto_t tp;
2139   local_session_t *ls;
2140   u8 *conn = 0;
2141
2142   if (!app)
2143     {
2144       app_worker_format_local_sessions (0, verbose);
2145       return;
2146     }
2147
2148   /*
2149    * Format local listeners
2150    */
2151
2152   /* *INDENT-OFF* */
2153   pool_foreach (ls, app->local_listen_sessions, ({
2154     tp = session_type_transport_proto (ls->listener_session_type);
2155     conn = format (0, "[L][%U] *:%u", format_transport_proto_short, tp,
2156                    ls->port);
2157     vlib_cli_output (vm, "%-40v%-15u%-20s", conn, ls->app_wrk_index, "*");
2158     vec_reset_length (conn);
2159   }));
2160   /* *INDENT-ON* */
2161
2162   /*
2163    * Format local accepted/connected sessions
2164    */
2165   /* *INDENT-OFF* */
2166   pool_foreach (wrk_map, app->worker_maps, ({
2167     app_wrk = app_worker_get (wrk_map->wrk_index);
2168     app_worker_format_local_sessions (app_wrk, verbose);
2169   }));
2170   /* *INDENT-ON* */
2171 }
2172
2173 static void
2174 app_worker_format_local_connects (app_worker_t * app, int verbose)
2175 {
2176   vlib_main_t *vm = vlib_get_main ();
2177   u32 app_wrk_index, session_index;
2178   app_worker_t *server_wrk;
2179   local_session_t *ls;
2180   uword client_key;
2181   u64 value;
2182
2183   /* Header */
2184   if (app == 0)
2185     {
2186       if (verbose)
2187         vlib_cli_output (vm, "%-40s%-15s%-20s%-10s", "Connection", "App",
2188                          "Peer App", "SegManager");
2189       else
2190         vlib_cli_output (vm, "%-40s%-15s%-20s", "Connection", "App",
2191                          "Peer App");
2192       return;
2193     }
2194
2195   if (!app->local_connects)
2196     return;
2197
2198   /* *INDENT-OFF* */
2199   hash_foreach (client_key, value, app->local_connects, ({
2200     application_client_local_connect_key_parse (client_key, &app_wrk_index,
2201                                                 &session_index);
2202     server_wrk = app_worker_get (app_wrk_index);
2203     ls = application_get_local_session (server_wrk, session_index);
2204     vlib_cli_output (vm, "%-40s%-15s%-20s", "TODO", ls->app_wrk_index,
2205                      ls->client_wrk_index);
2206   }));
2207   /* *INDENT-ON* */
2208 }
2209
2210 static void
2211 application_format_local_connects (application_t * app, int verbose)
2212 {
2213   app_worker_map_t *wrk_map;
2214   app_worker_t *app_wrk;
2215
2216   if (!app)
2217     {
2218       app_worker_format_local_connects (0, verbose);
2219       return;
2220     }
2221
2222   /* *INDENT-OFF* */
2223   pool_foreach (wrk_map, app->worker_maps, ({
2224     app_wrk = app_worker_get (wrk_map->wrk_index);
2225     app_worker_format_local_connects (app_wrk, verbose);
2226   }));
2227   /* *INDENT-ON* */
2228 }
2229
2230 u8 *
2231 format_application_worker (u8 * s, va_list * args)
2232 {
2233   app_worker_t *app_wrk = va_arg (*args, app_worker_t *);
2234   u32 indent = 1;
2235
2236   s = format (s, "%U wrk-index %u app-index %u map-index %u "
2237               "api-client-index %d\n", format_white_space, indent,
2238               app_wrk->wrk_index, app_wrk->app_index, app_wrk->wrk_map_index,
2239               app_wrk->api_client_index);
2240   return s;
2241 }
2242
2243 u8 *
2244 format_application (u8 * s, va_list * args)
2245 {
2246   application_t *app = va_arg (*args, application_t *);
2247   CLIB_UNUSED (int verbose) = va_arg (*args, int);
2248   segment_manager_properties_t *props;
2249   const u8 *app_ns_name, *app_name;
2250   app_worker_map_t *wrk_map;
2251   app_worker_t *app_wrk;
2252
2253   if (app == 0)
2254     {
2255       if (!verbose)
2256         s = format (s, "%-10s%-20s%-40s", "Index", "Name", "Namespace");
2257       return s;
2258     }
2259
2260   app_name = app_get_name (app);
2261   app_ns_name = app_namespace_id_from_index (app->ns_index);
2262   props = application_segment_manager_properties (app);
2263   if (!verbose)
2264     {
2265       s = format (s, "%-10u%-20s%-40s", app->app_index, app_name,
2266                   app_ns_name);
2267       return s;
2268     }
2269
2270   s = format (s, "app-name %s app-index %u ns-index %u seg-size %U\n",
2271               app_name, app->app_index, app->ns_index,
2272               format_memory_size, props->add_segment_size);
2273   s = format (s, "rx-fifo-size %U tx-fifo-size %U workers:\n",
2274               format_memory_size, props->rx_fifo_size,
2275               format_memory_size, props->tx_fifo_size);
2276
2277   /* *INDENT-OFF* */
2278   pool_foreach (wrk_map, app->worker_maps, ({
2279       app_wrk = app_worker_get (wrk_map->wrk_index);
2280       s = format (s, "%U", format_application_worker, app_wrk);
2281   }));
2282   /* *INDENT-ON* */
2283
2284   return s;
2285 }
2286
2287 void
2288 application_format_all_listeners (vlib_main_t * vm, int do_local, int verbose)
2289 {
2290   application_t *app;
2291
2292   if (!pool_elts (app_main.app_pool))
2293     {
2294       vlib_cli_output (vm, "No active server bindings");
2295       return;
2296     }
2297
2298   if (do_local)
2299     {
2300       application_format_local_sessions (0, verbose);
2301       /* *INDENT-OFF* */
2302       pool_foreach (app, app_main.app_pool, ({
2303         application_format_local_sessions (app, verbose);
2304       }));
2305       /* *INDENT-ON* */
2306     }
2307   else
2308     {
2309       application_format_listeners (0, verbose);
2310
2311       /* *INDENT-OFF* */
2312       pool_foreach (app, app_main.app_pool, ({
2313         application_format_listeners (app, verbose);
2314       }));
2315       /* *INDENT-ON* */
2316     }
2317 }
2318
2319 void
2320 application_format_all_clients (vlib_main_t * vm, int do_local, int verbose)
2321 {
2322   application_t *app;
2323
2324   if (!pool_elts (app_main.app_pool))
2325     {
2326       vlib_cli_output (vm, "No active apps");
2327       return;
2328     }
2329
2330   if (do_local)
2331     {
2332       application_format_local_connects (0, verbose);
2333
2334       /* *INDENT-OFF* */
2335       pool_foreach (app, app_main.app_pool, ({
2336         application_format_local_connects (app, verbose);
2337       }));
2338       /* *INDENT-ON* */
2339     }
2340   else
2341     {
2342       application_format_connects (0, verbose);
2343
2344       /* *INDENT-OFF* */
2345       pool_foreach (app, app_main.app_pool, ({
2346         application_format_connects (app, verbose);
2347       }));
2348       /* *INDENT-ON* */
2349     }
2350 }
2351
2352 static clib_error_t *
2353 show_app_command_fn (vlib_main_t * vm, unformat_input_t * input,
2354                      vlib_cli_command_t * cmd)
2355 {
2356   int do_server = 0, do_client = 0, do_local = 0;
2357   application_t *app;
2358   u32 app_index = ~0;
2359   int verbose = 0;
2360
2361   session_cli_return_if_not_enabled ();
2362
2363   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2364     {
2365       if (unformat (input, "server"))
2366         do_server = 1;
2367       else if (unformat (input, "client"))
2368         do_client = 1;
2369       else if (unformat (input, "local"))
2370         do_local = 1;
2371       else if (unformat (input, "%u", &app_index))
2372         ;
2373       else if (unformat (input, "verbose"))
2374         verbose = 1;
2375       else
2376         return clib_error_return (0, "unknown input `%U'",
2377                                   format_unformat_error, input);
2378     }
2379
2380   if (do_server)
2381     {
2382       application_format_all_listeners (vm, do_local, verbose);
2383       return 0;
2384     }
2385
2386   if (do_client)
2387     {
2388       application_format_all_clients (vm, do_local, verbose);
2389       return 0;
2390     }
2391
2392   if (app_index != ~0)
2393     {
2394       app = application_get_if_valid (app_index);
2395       if (!app)
2396         return clib_error_return (0, "No app with index %u", app_index);
2397
2398       vlib_cli_output (vm, "%U", format_application, app, /* verbose */ 1);
2399       return 0;
2400     }
2401
2402   /* Print app related info */
2403   if (!do_server && !do_client)
2404     {
2405       vlib_cli_output (vm, "%U", format_application, 0, 0);
2406       /* *INDENT-OFF* */
2407       pool_foreach (app, app_main.app_pool, ({
2408         vlib_cli_output (vm, "%U", format_application, app, 0);
2409       }));
2410       /* *INDENT-ON* */
2411     }
2412
2413   return 0;
2414 }
2415
2416 /* *INDENT-OFF* */
2417 VLIB_CLI_COMMAND (show_app_command, static) =
2418 {
2419   .path = "show app",
2420   .short_help = "show app [server|client] [verbose]",
2421   .function = show_app_command_fn,
2422 };
2423 /* *INDENT-ON* */
2424
2425 /*
2426  * fd.io coding-style-patch-verification: ON
2427  *
2428  * Local Variables:
2429  * eval: (c-set-style "gnu")
2430  * End:
2431  */