session: cleanup part 2
[vpp.git] / src / vnet / session / application.c
1 /*
2  * Copyright (c) 2017-2019 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/session/application.h>
17 #include <vnet/session/application_interface.h>
18 #include <vnet/session/application_namespace.h>
19 #include <vnet/session/session.h>
20
21 static app_main_t app_main;
22
23 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 (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 int
501 application_alloc_worker_and_init (application_t * app, app_worker_t ** wrk)
502 {
503   app_worker_map_t *wrk_map;
504   app_worker_t *app_wrk;
505   segment_manager_t *sm;
506   int rv;
507
508   app_wrk = app_worker_alloc (app);
509   wrk_map = app_worker_map_alloc (app);
510   wrk_map->wrk_index = app_wrk->wrk_index;
511   app_wrk->wrk_map_index = app_worker_map_index (app, wrk_map);
512
513   /*
514    * Setup first segment manager
515    */
516   sm = segment_manager_new ();
517   sm->app_wrk_index = app_wrk->wrk_index;
518
519   if ((rv = segment_manager_init (sm, app->sm_properties.segment_size,
520                                   app->sm_properties.prealloc_fifos)))
521     {
522       app_worker_free (app_wrk);
523       return rv;
524     }
525   sm->first_is_protected = 1;
526
527   /*
528    * Setup app worker
529    */
530   app_wrk->first_segment_manager = segment_manager_index (sm);
531   app_wrk->listeners_table = hash_create (0, sizeof (u64));
532   app_wrk->event_queue = segment_manager_event_queue (sm);
533   app_wrk->app_is_builtin = application_is_builtin (app);
534
535   /*
536    * Segment manager for local sessions
537    */
538   sm = segment_manager_new ();
539   sm->app_wrk_index = app_wrk->wrk_index;
540   app_wrk->local_segment_manager = segment_manager_index (sm);
541   app_wrk->local_connects = hash_create (0, sizeof (u64));
542
543   *wrk = app_wrk;
544
545   return 0;
546 }
547
548 /**
549  * Start listening local transport endpoint for requested transport.
550  *
551  * Creates a 'dummy' stream session with state LISTENING to be used in session
552  * lookups, prior to establishing connection. Requests transport to build
553  * it's own specific listening connection.
554  */
555 int
556 application_start_listen (application_t * app,
557                           session_endpoint_cfg_t * sep_ext,
558                           session_handle_t * res)
559 {
560   app_listener_t *app_listener;
561   u32 table_index, fib_proto;
562   session_endpoint_t *sep;
563   app_worker_t *app_wrk;
564   session_t *ls;
565   session_handle_t lh;
566   session_type_t sst;
567
568   /*
569    * Check if sep is already listened on
570    */
571   sep = (session_endpoint_t *) sep_ext;
572   fib_proto = session_endpoint_fib_proto (sep);
573   table_index = application_session_table (app, fib_proto);
574   lh = session_lookup_endpoint_listener (table_index, sep, 1);
575   if (lh != SESSION_INVALID_HANDLE)
576     {
577       ls = listen_session_get_from_handle (lh);
578       if (ls->app_index != app->app_index)
579         return VNET_API_ERROR_ADDRESS_IN_USE;
580
581       app_wrk = app_worker_get (sep_ext->app_wrk_index);
582       if (ls->app_wrk_index == app_wrk->wrk_index)
583         return VNET_API_ERROR_ADDRESS_IN_USE;
584
585       if (app_worker_start_listen (app_wrk, ls))
586         return -1;
587
588       app_listener = app_listener_get (app, ls->listener_db_index);
589       app_listener->workers = clib_bitmap_set (app_listener->workers,
590                                                app_wrk->wrk_map_index, 1);
591
592       *res = listen_session_get_handle (ls);
593       return 0;
594     }
595
596   /*
597    * Allocate new listener for application
598    */
599   sst = session_type_from_proto_and_ip (sep_ext->transport_proto,
600                                         sep_ext->is_ip4);
601   ls = listen_session_new (0, sst);
602   ls->app_index = app->app_index;
603   lh = listen_session_get_handle (ls);
604   if (session_listen (ls, sep_ext))
605     goto err;
606
607
608   ls = listen_session_get_from_handle (lh);
609   app_listener = app_listener_alloc (app);
610   ls->listener_db_index = app_listener->al_index;
611
612   /*
613    * Setup app worker as a listener
614    */
615   app_wrk = app_worker_get (sep_ext->app_wrk_index);
616   ls->app_wrk_index = app_wrk->wrk_index;
617   if (app_worker_start_listen (app_wrk, ls))
618     goto err;
619   app_listener->workers = clib_bitmap_set (app_listener->workers,
620                                            app_wrk->wrk_map_index, 1);
621
622   *res = lh;
623   return 0;
624
625 err:
626   listen_session_del (ls);
627   return -1;
628 }
629
630 int
631 application_change_listener_owner (session_t * s, app_worker_t * app_wrk)
632 {
633   app_worker_t *old_wrk = app_worker_get (s->app_wrk_index);
634   app_listener_t *app_listener;
635   application_t *app;
636
637   if (!old_wrk)
638     return -1;
639
640   hash_unset (old_wrk->listeners_table, listen_session_get_handle (s));
641   if (session_transport_service_type (s) == TRANSPORT_SERVICE_CL
642       && s->rx_fifo)
643     segment_manager_dealloc_fifos (s->rx_fifo->segment_index, s->rx_fifo,
644                                    s->tx_fifo);
645
646   if (app_worker_start_listen (app_wrk, s))
647     return -1;
648
649   s->app_wrk_index = app_wrk->wrk_index;
650
651   app = application_get (old_wrk->app_index);
652   if (!app)
653     return -1;
654
655   app_listener = app_listener_get (app, s->listener_db_index);
656   app_listener->workers = clib_bitmap_set (app_listener->workers,
657                                            app_wrk->wrk_map_index, 1);
658   app_listener->workers = clib_bitmap_set (app_listener->workers,
659                                            old_wrk->wrk_map_index, 0);
660   return 0;
661 }
662
663 /**
664  * Stop listening on session associated to handle
665  *
666  * @param handle        listener handle
667  * @param app_index     index of the app owning the handle.
668  * @param app_wrk_index index of the worker requesting the stop
669  */
670 int
671 application_stop_listen (u32 app_index, u32 app_wrk_index,
672                          session_handle_t handle)
673 {
674   app_listener_t *app_listener;
675   session_t *listener;
676   app_worker_t *app_wrk;
677   application_t *app;
678
679   listener = listen_session_get_from_handle (handle);
680   app = application_get (app_index);
681   if (PREDICT_FALSE (!app || app->app_index != listener->app_index))
682     {
683       clib_warning ("app doesn't own handle %llu!", handle);
684       return -1;
685     }
686
687   app_listener = app_listener_get (app, listener->listener_db_index);
688   if (!clib_bitmap_get (app_listener->workers, app_wrk_index))
689     {
690       clib_warning ("worker %u not listening on handle %lu", app_wrk_index,
691                     handle);
692       return 0;
693     }
694
695   app_wrk = application_get_worker (app, app_wrk_index);
696   app_worker_stop_listen (app_wrk, handle);
697   clib_bitmap_set_no_check (app_listener->workers, app_wrk_index, 0);
698
699   if (clib_bitmap_is_zero (app_listener->workers))
700     {
701       session_stop_listen (listener);
702       app_listener_free (app, app_listener);
703       listen_session_del (listener);
704     }
705
706   return 0;
707 }
708
709 clib_error_t *
710 vnet_app_worker_add_del (vnet_app_worker_add_del_args_t * a)
711 {
712   svm_fifo_segment_private_t *fs;
713   app_worker_map_t *wrk_map;
714   app_worker_t *app_wrk;
715   segment_manager_t *sm;
716   application_t *app;
717   int rv;
718
719   app = application_get (a->app_index);
720   if (!app)
721     return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
722                                    "App %u does not exist", a->app_index);
723
724   if (a->is_add)
725     {
726       if ((rv = application_alloc_worker_and_init (app, &app_wrk)))
727         return clib_error_return_code (0, rv, 0, "app wrk init: %d", rv);
728
729       /* Map worker api index to the app */
730       app_wrk->api_client_index = a->api_client_index;
731       application_api_table_add (app->app_index, a->api_client_index);
732
733       sm = segment_manager_get (app_wrk->first_segment_manager);
734       fs = segment_manager_get_segment_w_lock (sm, 0);
735       a->segment = &fs->ssvm;
736       a->segment_handle = segment_manager_segment_handle (sm, fs);
737       segment_manager_segment_reader_unlock (sm);
738       a->evt_q = app_wrk->event_queue;
739       a->wrk_map_index = app_wrk->wrk_map_index;
740     }
741   else
742     {
743       wrk_map = app_worker_map_get (app, a->wrk_map_index);
744       if (!wrk_map)
745         return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
746                                        "App %u does not have worker %u",
747                                        app->app_index, a->wrk_map_index);
748       app_wrk = app_worker_get (wrk_map->wrk_index);
749       if (!app_wrk)
750         return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
751                                        "No worker %u", a->wrk_map_index);
752       application_api_table_del (app_wrk->api_client_index);
753       app_worker_free (app_wrk);
754       app_worker_map_free (app, wrk_map);
755       if (application_n_workers (app) == 0)
756         application_free (app);
757     }
758   return 0;
759 }
760
761 int
762 application_is_proxy (application_t * app)
763 {
764   return (app->flags & APP_OPTIONS_FLAGS_IS_PROXY);
765 }
766
767 int
768 application_is_builtin (application_t * app)
769 {
770   return (app->flags & APP_OPTIONS_FLAGS_IS_BUILTIN);
771 }
772
773 int
774 application_is_builtin_proxy (application_t * app)
775 {
776   return (application_is_proxy (app) && application_is_builtin (app));
777 }
778
779 u8
780 application_has_local_scope (application_t * app)
781 {
782   return app->flags & APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
783 }
784
785 u8
786 application_has_global_scope (application_t * app)
787 {
788   return app->flags & APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
789 }
790
791 u8
792 application_use_mq_for_ctrl (application_t * app)
793 {
794   return app->flags & APP_OPTIONS_FLAGS_USE_MQ_FOR_CTRL_MSGS;
795 }
796
797 static clib_error_t *
798 application_start_stop_proxy_fib_proto (application_t * app, u8 fib_proto,
799                                         u8 transport_proto, u8 is_start)
800 {
801   app_namespace_t *app_ns = app_namespace_get (app->ns_index);
802   u8 is_ip4 = (fib_proto == FIB_PROTOCOL_IP4);
803   session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
804   transport_connection_t *tc;
805   app_worker_t *app_wrk;
806   session_t *s;
807   u64 handle;
808
809   /* TODO decide if we want proxy to be enabled for all workers */
810   app_wrk = application_get_default_worker (app);
811   if (is_start)
812     {
813       s = app_worker_first_listener (app_wrk, fib_proto, transport_proto);
814       if (!s)
815         {
816           sep.is_ip4 = is_ip4;
817           sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
818           sep.sw_if_index = app_ns->sw_if_index;
819           sep.transport_proto = transport_proto;
820           sep.app_wrk_index = app_wrk->wrk_index;       /* only default */
821           application_start_listen (app, &sep, &handle);
822           s = listen_session_get_from_handle (handle);
823           s->enqueue_epoch = SESSION_PROXY_LISTENER_INDEX;
824         }
825     }
826   else
827     {
828       s = app_worker_proxy_listener (app_wrk, fib_proto, transport_proto);
829       ASSERT (s);
830     }
831
832   tc = listen_session_get_transport (s);
833
834   if (!ip_is_zero (&tc->lcl_ip, 1))
835     {
836       u32 sti;
837       sep.is_ip4 = is_ip4;
838       sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
839       sep.transport_proto = transport_proto;
840       sep.port = 0;
841       sti = session_lookup_get_index_for_fib (fib_proto, sep.fib_index);
842       if (is_start)
843         session_lookup_add_session_endpoint (sti,
844                                              (session_endpoint_t *) & sep,
845                                              s->session_index);
846       else
847         session_lookup_del_session_endpoint (sti,
848                                              (session_endpoint_t *) & sep);
849     }
850
851   return 0;
852 }
853
854 static void
855 application_start_stop_proxy_local_scope (application_t * app,
856                                           u8 transport_proto, u8 is_start)
857 {
858   session_endpoint_t sep = SESSION_ENDPOINT_NULL;
859   app_namespace_t *app_ns;
860   app_ns = app_namespace_get (app->ns_index);
861   sep.is_ip4 = 1;
862   sep.transport_proto = transport_proto;
863   sep.port = 0;
864
865   if (is_start)
866     {
867       session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
868                                            app->app_index);
869       sep.is_ip4 = 0;
870       session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
871                                            app->app_index);
872     }
873   else
874     {
875       session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
876       sep.is_ip4 = 0;
877       session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
878     }
879 }
880
881 void
882 application_start_stop_proxy (application_t * app,
883                               transport_proto_t transport_proto, u8 is_start)
884 {
885   if (application_has_local_scope (app))
886     application_start_stop_proxy_local_scope (app, transport_proto, is_start);
887
888   if (application_has_global_scope (app))
889     {
890       application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP4,
891                                               transport_proto, is_start);
892       application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP6,
893                                               transport_proto, is_start);
894     }
895 }
896
897 void
898 application_setup_proxy (application_t * app)
899 {
900   u16 transports = app->proxied_transports;
901   transport_proto_t tp;
902
903   ASSERT (application_is_proxy (app));
904
905   /* *INDENT-OFF* */
906   transport_proto_foreach (tp, ({
907     if (transports & (1 << tp))
908       application_start_stop_proxy (app, tp, 1);
909   }));
910   /* *INDENT-ON* */
911 }
912
913 void
914 application_remove_proxy (application_t * app)
915 {
916   u16 transports = app->proxied_transports;
917   transport_proto_t tp;
918
919   ASSERT (application_is_proxy (app));
920
921   /* *INDENT-OFF* */
922   transport_proto_foreach (tp, ({
923     if (transports & (1 << tp))
924       application_start_stop_proxy (app, tp, 0);
925   }));
926   /* *INDENT-ON* */
927 }
928
929 segment_manager_properties_t *
930 application_segment_manager_properties (application_t * app)
931 {
932   return &app->sm_properties;
933 }
934
935 segment_manager_properties_t *
936 application_get_segment_manager_properties (u32 app_index)
937 {
938   application_t *app = application_get (app_index);
939   return &app->sm_properties;
940 }
941
942 local_session_t *
943 application_local_listen_session_alloc (application_t * app)
944 {
945   local_session_t *ll;
946   pool_get (app->local_listen_sessions, ll);
947   clib_memset (ll, 0, sizeof (*ll));
948   return ll;
949 }
950
951 u32
952 application_local_listener_index (application_t * app, local_session_t * ll)
953 {
954   return (ll - app->local_listen_sessions);
955 }
956
957 void
958 application_local_listen_session_free (application_t * app,
959                                        local_session_t * ll)
960 {
961   pool_put (app->local_listen_sessions, ll);
962   if (CLIB_DEBUG)
963     clib_memset (ll, 0xfb, sizeof (*ll));
964 }
965
966 int
967 application_start_local_listen (application_t * app,
968                                 session_endpoint_cfg_t * sep_ext,
969                                 session_handle_t * handle)
970 {
971   app_listener_t *app_listener;
972   session_endpoint_t *sep;
973   app_worker_t *app_wrk;
974   session_handle_t lh;
975   local_session_t *ll;
976   u32 table_index;
977
978   sep = (session_endpoint_t *) sep_ext;
979   table_index = application_local_session_table (app);
980   app_wrk = app_worker_get (sep_ext->app_wrk_index);
981
982   /* An exact sep match, as opposed to session_lookup_local_listener */
983   lh = session_lookup_endpoint_listener (table_index, sep, 1);
984   if (lh != SESSION_INVALID_HANDLE)
985     {
986       ll = application_get_local_listener_w_handle (lh);
987       if (ll->app_index != app->app_index)
988         return VNET_API_ERROR_ADDRESS_IN_USE;
989
990       if (ll->app_wrk_index == app_wrk->wrk_index)
991         return VNET_API_ERROR_ADDRESS_IN_USE;
992
993       app_listener = app_local_listener_get (app, ll->listener_db_index);
994       app_listener->workers = clib_bitmap_set (app_listener->workers,
995                                                app_wrk->wrk_map_index, 1);
996       *handle = application_local_session_handle (ll);
997       return 0;
998     }
999
1000   ll = application_local_listen_session_alloc (app);
1001   ll->session_type = session_type_from_proto_and_ip (TRANSPORT_PROTO_NONE, 0);
1002   ll->app_wrk_index = app_wrk->app_index;
1003   ll->session_index = application_local_listener_index (app, ll);
1004   ll->port = sep_ext->port;
1005   /* Store the original session type for the unbind */
1006   ll->listener_session_type =
1007     session_type_from_proto_and_ip (sep_ext->transport_proto,
1008                                     sep_ext->is_ip4);
1009   ll->transport_listener_index = ~0;
1010   ll->app_index = app->app_index;
1011
1012   app_listener = app_local_listener_alloc (app);
1013   ll->listener_db_index = app_listener->al_index;
1014   app_listener->workers = clib_bitmap_set (app_listener->workers,
1015                                            app_wrk->wrk_map_index, 1);
1016
1017   *handle = application_local_session_handle (ll);
1018   session_lookup_add_session_endpoint (table_index, sep, *handle);
1019
1020   return 0;
1021 }
1022
1023 /**
1024  * Clean up local session table. If we have a listener session use it to
1025  * find the port and proto. If not, the handle must be a local table handle
1026  * so parse it.
1027  */
1028 int
1029 application_stop_local_listen (u32 app_index, u32 wrk_map_index,
1030                                session_handle_t lh)
1031 {
1032   session_endpoint_t sep = SESSION_ENDPOINT_NULL;
1033   u32 table_index, ll_index, server_index;
1034   app_listener_t *app_listener;
1035   app_worker_t *server_wrk;
1036   session_t *sl = 0;
1037   local_session_t *ll, *ls;
1038   application_t *server;
1039
1040   server = application_get (app_index);
1041   table_index = application_local_session_table (server);
1042
1043   /* We have both local and global table binds. Figure from global what
1044    * sep we should be cleaning up.
1045    */
1046   if (!session_handle_is_local (lh))
1047     {
1048       sl = listen_session_get_from_handle (lh);
1049       if (!sl || listen_session_get_local_session_endpoint (sl, &sep))
1050         {
1051           clib_warning ("broken listener");
1052           return -1;
1053         }
1054       lh = session_lookup_endpoint_listener (table_index, &sep, 0);
1055       if (lh == SESSION_INVALID_HANDLE)
1056         return -1;
1057     }
1058
1059   local_session_parse_handle (lh, &server_index, &ll_index);
1060   if (PREDICT_FALSE (server_index != app_index))
1061     {
1062       clib_warning ("app %u does not own local handle 0x%lx", app_index, lh);
1063       return -1;
1064     }
1065
1066   ll = application_get_local_listen_session (server, ll_index);
1067   if (PREDICT_FALSE (!ll))
1068     {
1069       clib_warning ("no local listener");
1070       return -1;
1071     }
1072
1073   app_listener = app_local_listener_get (server, ll->listener_db_index);
1074   if (!clib_bitmap_get (app_listener->workers, wrk_map_index))
1075     {
1076       clib_warning ("app wrk %u not listening on handle %lu", wrk_map_index,
1077                     lh);
1078       return -1;
1079     }
1080
1081   server_wrk = application_get_worker (server, wrk_map_index);
1082   /* *INDENT-OFF* */
1083   pool_foreach (ls, server_wrk->local_sessions, ({
1084     if (ls->listener_index == ll->session_index)
1085       app_worker_local_session_disconnect (server_wrk->app_index, ls);
1086   }));
1087   /* *INDENT-ON* */
1088
1089   clib_bitmap_set_no_check (app_listener->workers, wrk_map_index, 0);
1090   if (clib_bitmap_is_zero (app_listener->workers))
1091     {
1092       app_local_listener_free (server, app_listener);
1093       application_local_listener_session_endpoint (ll, &sep);
1094       session_lookup_del_session_endpoint (table_index, &sep);
1095       application_local_listen_session_free (server, ll);
1096     }
1097
1098   return 0;
1099 }
1100
1101 clib_error_t *
1102 vnet_app_add_tls_cert (vnet_app_add_tls_cert_args_t * a)
1103 {
1104   application_t *app;
1105   app = application_get (a->app_index);
1106   if (!app)
1107     return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1108                                    0, "app %u doesn't exist", a->app_index);
1109   app->tls_cert = vec_dup (a->cert);
1110   return 0;
1111 }
1112
1113 clib_error_t *
1114 vnet_app_add_tls_key (vnet_app_add_tls_key_args_t * a)
1115 {
1116   application_t *app;
1117   app = application_get (a->app_index);
1118   if (!app)
1119     return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1120                                    0, "app %u doesn't exist", a->app_index);
1121   app->tls_key = vec_dup (a->key);
1122   return 0;
1123 }
1124
1125 static void
1126 application_format_listeners (application_t * app, int verbose)
1127 {
1128   vlib_main_t *vm = vlib_get_main ();
1129   app_worker_map_t *wrk_map;
1130   app_worker_t *app_wrk;
1131   u32 sm_index;
1132   u64 handle;
1133
1134   if (!app)
1135     {
1136       vlib_cli_output (vm, "%U", format_app_worker_listener, 0 /* header */ ,
1137                        0, 0, verbose);
1138       return;
1139     }
1140
1141   /* *INDENT-OFF* */
1142   pool_foreach (wrk_map, app->worker_maps, ({
1143     app_wrk = app_worker_get (wrk_map->wrk_index);
1144     if (hash_elts (app_wrk->listeners_table) == 0)
1145       continue;
1146     hash_foreach (handle, sm_index, app_wrk->listeners_table, ({
1147       vlib_cli_output (vm, "%U", format_app_worker_listener, app_wrk,
1148                        handle, sm_index, verbose);
1149     }));
1150   }));
1151   /* *INDENT-ON* */
1152 }
1153
1154 static void
1155 application_format_connects (application_t * app, int verbose)
1156 {
1157   app_worker_map_t *wrk_map;
1158   app_worker_t *app_wrk;
1159
1160   if (!app)
1161     {
1162       app_worker_format_connects (0, verbose);
1163       return;
1164     }
1165
1166   /* *INDENT-OFF* */
1167   pool_foreach (wrk_map, app->worker_maps, ({
1168     app_wrk = app_worker_get (wrk_map->wrk_index);
1169     app_worker_format_connects (app_wrk, verbose);
1170   }));
1171   /* *INDENT-ON* */
1172 }
1173
1174 static void
1175 application_format_local_sessions (application_t * app, int verbose)
1176 {
1177   vlib_main_t *vm = vlib_get_main ();
1178   app_worker_map_t *wrk_map;
1179   app_worker_t *app_wrk;
1180   transport_proto_t tp;
1181   local_session_t *ls;
1182   u8 *conn = 0;
1183
1184   if (!app)
1185     {
1186       app_worker_format_local_sessions (0, verbose);
1187       return;
1188     }
1189
1190   /*
1191    * Format local listeners
1192    */
1193
1194   /* *INDENT-OFF* */
1195   pool_foreach (ls, app->local_listen_sessions, ({
1196     tp = session_type_transport_proto (ls->listener_session_type);
1197     conn = format (0, "[L][%U] *:%u", format_transport_proto_short, tp,
1198                    ls->port);
1199     vlib_cli_output (vm, "%-40v%-15u%-20s", conn, ls->app_wrk_index, "*");
1200     vec_reset_length (conn);
1201   }));
1202   /* *INDENT-ON* */
1203
1204   /*
1205    * Format local accepted/connected sessions
1206    */
1207   /* *INDENT-OFF* */
1208   pool_foreach (wrk_map, app->worker_maps, ({
1209     app_wrk = app_worker_get (wrk_map->wrk_index);
1210     app_worker_format_local_sessions (app_wrk, verbose);
1211   }));
1212   /* *INDENT-ON* */
1213 }
1214
1215 static void
1216 application_format_local_connects (application_t * app, int verbose)
1217 {
1218   app_worker_map_t *wrk_map;
1219   app_worker_t *app_wrk;
1220
1221   if (!app)
1222     {
1223       app_worker_format_local_connects (0, verbose);
1224       return;
1225     }
1226
1227   /* *INDENT-OFF* */
1228   pool_foreach (wrk_map, app->worker_maps, ({
1229     app_wrk = app_worker_get (wrk_map->wrk_index);
1230     app_worker_format_local_connects (app_wrk, verbose);
1231   }));
1232   /* *INDENT-ON* */
1233 }
1234
1235 u8 *
1236 format_application (u8 * s, va_list * args)
1237 {
1238   application_t *app = va_arg (*args, application_t *);
1239   CLIB_UNUSED (int verbose) = va_arg (*args, int);
1240   segment_manager_properties_t *props;
1241   const u8 *app_ns_name, *app_name;
1242   app_worker_map_t *wrk_map;
1243   app_worker_t *app_wrk;
1244
1245   if (app == 0)
1246     {
1247       if (!verbose)
1248         s = format (s, "%-10s%-20s%-40s", "Index", "Name", "Namespace");
1249       return s;
1250     }
1251
1252   app_name = app_get_name (app);
1253   app_ns_name = app_namespace_id_from_index (app->ns_index);
1254   props = application_segment_manager_properties (app);
1255   if (!verbose)
1256     {
1257       s = format (s, "%-10u%-20s%-40s", app->app_index, app_name,
1258                   app_ns_name);
1259       return s;
1260     }
1261
1262   s = format (s, "app-name %s app-index %u ns-index %u seg-size %U\n",
1263               app_name, app->app_index, app->ns_index,
1264               format_memory_size, props->add_segment_size);
1265   s = format (s, "rx-fifo-size %U tx-fifo-size %U workers:\n",
1266               format_memory_size, props->rx_fifo_size,
1267               format_memory_size, props->tx_fifo_size);
1268
1269   /* *INDENT-OFF* */
1270   pool_foreach (wrk_map, app->worker_maps, ({
1271       app_wrk = app_worker_get (wrk_map->wrk_index);
1272       s = format (s, "%U", format_app_worker, app_wrk);
1273   }));
1274   /* *INDENT-ON* */
1275
1276   return s;
1277 }
1278
1279 void
1280 application_format_all_listeners (vlib_main_t * vm, int do_local, int verbose)
1281 {
1282   application_t *app;
1283
1284   if (!pool_elts (app_main.app_pool))
1285     {
1286       vlib_cli_output (vm, "No active server bindings");
1287       return;
1288     }
1289
1290   if (do_local)
1291     {
1292       application_format_local_sessions (0, verbose);
1293       /* *INDENT-OFF* */
1294       pool_foreach (app, app_main.app_pool, ({
1295         application_format_local_sessions (app, verbose);
1296       }));
1297       /* *INDENT-ON* */
1298     }
1299   else
1300     {
1301       application_format_listeners (0, verbose);
1302
1303       /* *INDENT-OFF* */
1304       pool_foreach (app, app_main.app_pool, ({
1305         application_format_listeners (app, verbose);
1306       }));
1307       /* *INDENT-ON* */
1308     }
1309 }
1310
1311 void
1312 application_format_all_clients (vlib_main_t * vm, int do_local, int verbose)
1313 {
1314   application_t *app;
1315
1316   if (!pool_elts (app_main.app_pool))
1317     {
1318       vlib_cli_output (vm, "No active apps");
1319       return;
1320     }
1321
1322   if (do_local)
1323     {
1324       application_format_local_connects (0, verbose);
1325
1326       /* *INDENT-OFF* */
1327       pool_foreach (app, app_main.app_pool, ({
1328         application_format_local_connects (app, verbose);
1329       }));
1330       /* *INDENT-ON* */
1331     }
1332   else
1333     {
1334       application_format_connects (0, verbose);
1335
1336       /* *INDENT-OFF* */
1337       pool_foreach (app, app_main.app_pool, ({
1338         application_format_connects (app, verbose);
1339       }));
1340       /* *INDENT-ON* */
1341     }
1342 }
1343
1344 static clib_error_t *
1345 show_app_command_fn (vlib_main_t * vm, unformat_input_t * input,
1346                      vlib_cli_command_t * cmd)
1347 {
1348   int do_server = 0, do_client = 0, do_local = 0;
1349   application_t *app;
1350   u32 app_index = ~0;
1351   int verbose = 0;
1352
1353   session_cli_return_if_not_enabled ();
1354
1355   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1356     {
1357       if (unformat (input, "server"))
1358         do_server = 1;
1359       else if (unformat (input, "client"))
1360         do_client = 1;
1361       else if (unformat (input, "local"))
1362         do_local = 1;
1363       else if (unformat (input, "%u", &app_index))
1364         ;
1365       else if (unformat (input, "verbose"))
1366         verbose = 1;
1367       else
1368         return clib_error_return (0, "unknown input `%U'",
1369                                   format_unformat_error, input);
1370     }
1371
1372   if (do_server)
1373     {
1374       application_format_all_listeners (vm, do_local, verbose);
1375       return 0;
1376     }
1377
1378   if (do_client)
1379     {
1380       application_format_all_clients (vm, do_local, verbose);
1381       return 0;
1382     }
1383
1384   if (app_index != ~0)
1385     {
1386       app = application_get_if_valid (app_index);
1387       if (!app)
1388         return clib_error_return (0, "No app with index %u", app_index);
1389
1390       vlib_cli_output (vm, "%U", format_application, app, /* verbose */ 1);
1391       return 0;
1392     }
1393
1394   /* Print app related info */
1395   if (!do_server && !do_client)
1396     {
1397       vlib_cli_output (vm, "%U", format_application, 0, 0);
1398       /* *INDENT-OFF* */
1399       pool_foreach (app, app_main.app_pool, ({
1400         vlib_cli_output (vm, "%U", format_application, app, 0);
1401       }));
1402       /* *INDENT-ON* */
1403     }
1404
1405   return 0;
1406 }
1407
1408 /* *INDENT-OFF* */
1409 VLIB_CLI_COMMAND (show_app_command, static) =
1410 {
1411   .path = "show app",
1412   .short_help = "show app [server|client] [verbose]",
1413   .function = show_app_command_fn,
1414 };
1415 /* *INDENT-ON* */
1416
1417 /*
1418  * fd.io coding-style-patch-verification: ON
1419  *
1420  * Local Variables:
1421  * eval: (c-set-style "gnu")
1422  * End:
1423  */