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