session: refactor listen logic
[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 void
24 application_local_listener_session_endpoint (local_session_t * ll,
25                                              session_endpoint_t * sep)
26 {
27   sep->transport_proto =
28     session_type_transport_proto (ll->listener_session_type);
29   sep->port = ll->port;
30   sep->is_ip4 = ll->listener_session_type & 1;
31 }
32
33 static app_listener_t *
34 app_listener_alloc (application_t * app)
35 {
36   app_listener_t *app_listener;
37   pool_get (app->listeners, app_listener);
38   clib_memset (app_listener, 0, sizeof (*app_listener));
39   app_listener->al_index = app_listener - app->listeners;
40   app_listener->app_index = app->app_index;
41   app_listener->session_index = SESSION_INVALID_INDEX;
42   app_listener->local_index = SESSION_INVALID_INDEX;
43   return app_listener;
44 }
45
46 app_listener_t *
47 app_listener_get (application_t * app, u32 app_listener_index)
48 {
49   return pool_elt_at_index (app->listeners, app_listener_index);
50 }
51
52 static void
53 app_listener_free (application_t * app, app_listener_t * app_listener)
54 {
55   clib_bitmap_free (app_listener->workers);
56   pool_put (app->listeners, app_listener);
57   if (CLIB_DEBUG)
58     clib_memset (app_listener, 0xfa, sizeof (*app_listener));
59 }
60
61 local_session_t *
62 application_local_listen_session_alloc (application_t * app)
63 {
64   local_session_t *ll;
65   pool_get_zero (app->local_listen_sessions, ll);
66   ll->session_index = ll - app->local_listen_sessions;
67   ll->session_type = session_type_from_proto_and_ip (TRANSPORT_PROTO_NONE, 0);
68   ll->app_index = app->app_index;
69   return ll;
70 }
71
72 void
73 application_local_listen_session_free (application_t * app,
74                                        local_session_t * ll)
75 {
76   pool_put (app->local_listen_sessions, ll);
77   if (CLIB_DEBUG)
78     clib_memset (ll, 0xfb, sizeof (*ll));
79 }
80
81 static u32
82 app_listener_id (app_listener_t * al)
83 {
84   ASSERT (al->app_index < 1 << 16 && al->al_index < 1 << 16);
85   return (al->app_index << 16 | al->al_index);
86 }
87
88 session_handle_t
89 app_listener_handle (app_listener_t * al)
90 {
91   return ((u64) SESSION_LISTENER_PREFIX << 32 | (u64) app_listener_id (al));
92 }
93
94 static void
95 app_listener_id_parse (u32 listener_id, u32 * app_index,
96                        u32 * app_listener_index)
97 {
98   *app_index = listener_id >> 16;
99   *app_listener_index = listener_id & 0xFFFF;
100 }
101
102 void
103 app_listener_handle_parse (session_handle_t handle, u32 * app_index,
104                            u32 * app_listener_index)
105 {
106   app_listener_id_parse (handle & 0xFFFFFFFF, app_index, app_listener_index);
107 }
108
109 static app_listener_t *
110 app_listener_get_w_id (u32 listener_id)
111 {
112   u32 app_index, app_listener_index;
113   application_t *app;
114
115   app_listener_id_parse (listener_id, &app_index, &app_listener_index);
116   app = application_get_if_valid (app_index);
117   if (!app)
118     return 0;
119   return app_listener_get (app, app_listener_index);
120 }
121
122 app_listener_t *
123 app_listener_get_w_session (session_t * ls)
124 {
125   application_t *app;
126
127   app = application_get_if_valid (ls->app_index);
128   if (!app)
129     return 0;
130   return app_listener_get (app, ls->al_index);
131 }
132
133 app_listener_t *
134 app_listener_get_w_handle (session_handle_t handle)
135 {
136
137   if (handle >> 32 != SESSION_LISTENER_PREFIX)
138     return 0;
139
140   return app_listener_get_w_id (handle & 0xFFFFFFFF);
141 }
142
143 app_listener_t *
144 app_listener_lookup (application_t * app, session_endpoint_cfg_t * sep_ext)
145 {
146   u32 table_index, fib_proto;
147   session_endpoint_t *sep;
148   session_handle_t handle;
149   local_session_t *ll;
150   session_t *ls;
151
152   sep = (session_endpoint_t *) sep_ext;
153   if (application_has_local_scope (app) && session_endpoint_is_local (sep))
154     {
155       table_index = application_local_session_table (app);
156       handle = session_lookup_endpoint_listener (table_index, sep, 1);
157       if (handle != SESSION_INVALID_HANDLE)
158         {
159           ll = application_get_local_listener_w_handle (handle);
160           return app_listener_get_w_session ((session_t *) ll);
161         }
162     }
163
164   fib_proto = session_endpoint_fib_proto (sep);
165   table_index = application_session_table (app, fib_proto);
166   handle = session_lookup_endpoint_listener (table_index, sep, 1);
167   if (handle != SESSION_INVALID_HANDLE)
168     {
169       ls = listen_session_get_from_handle (handle);
170       return app_listener_get_w_session ((session_t *) ls);
171     }
172
173   return 0;
174 }
175
176 int
177 app_listener_alloc_and_init (application_t * app,
178                              session_endpoint_cfg_t * sep,
179                              app_listener_t ** listener)
180 {
181   app_listener_t *app_listener;
182   local_session_t *ll = 0;
183   session_handle_t lh;
184   session_type_t st;
185   session_t *ls = 0;
186   int rv;
187
188   app_listener = app_listener_alloc (app);
189   st = session_type_from_proto_and_ip (sep->transport_proto, sep->is_ip4);
190
191   /*
192    * Add session endpoint to local session table. Only binds to "inaddr_any"
193    * (i.e., zero address) are added to local scope table.
194    */
195   if (application_has_local_scope (app)
196       && session_endpoint_is_local ((session_endpoint_t *) sep))
197     {
198       u32 table_index;
199
200       ll = application_local_listen_session_alloc (app);
201       ll->port = sep->port;
202       /* Store the original session type for the unbind */
203       ll->listener_session_type = st;
204       table_index = application_local_session_table (app);
205       lh = application_local_session_handle (ll);
206       session_lookup_add_session_endpoint (table_index,
207                                            (session_endpoint_t *) sep, lh);
208       app_listener->local_index = ll->session_index;
209       ll->al_index = app_listener->al_index;
210     }
211
212   if (application_has_global_scope (app))
213     {
214       /*
215        * Start listening on local endpoint for requested transport and scope.
216        * Creates a stream session with state LISTENING to be used in session
217        * lookups, prior to establishing connection. Requests transport to
218        * build it's own specific listening connection.
219        */
220       ls = listen_session_new (0, st);
221       ls->app_index = app->app_index;
222       ls->app_wrk_index = sep->app_wrk_index;
223
224       /* Listen pool can be reallocated if the transport is
225        * recursive (tls) */
226       lh = session_handle (ls);
227
228       if ((rv = session_listen (ls, sep)))
229         {
230           ls = session_get_from_handle (lh);
231           session_free (ls);
232           return rv;
233         }
234       app_listener->session_index = ls->session_index;
235       ls->al_index = app_listener->al_index;
236     }
237
238   if (!ll && !ls)
239     {
240       app_listener_free (app, app_listener);
241       return -1;
242     }
243
244   *listener = app_listener;
245   return 0;
246 }
247
248 void
249 app_listener_cleanup (app_listener_t * al)
250 {
251   application_t *app = application_get (al->app_index);
252
253   if (al->session_index != SESSION_INVALID_INDEX)
254     {
255       session_t *ls = session_get (al->session_index, 0);
256       session_stop_listen (ls);
257       listen_session_del (ls);
258     }
259   if (al->local_index != SESSION_INVALID_INDEX)
260     {
261       session_endpoint_t sep = SESSION_ENDPOINT_NULL;
262       local_session_t *ll;
263       u32 table_index;
264
265       table_index = application_local_session_table (app);
266       ll = application_get_local_listen_session (app, al->local_index);
267       application_local_listener_session_endpoint (ll, &sep);
268       session_lookup_del_session_endpoint (table_index, &sep);
269       application_local_listen_session_free (app, ll);
270     }
271   app_listener_free (app, al);
272 }
273
274 app_worker_t *
275 app_listener_select_worker (app_listener_t * al)
276 {
277   application_t *app;
278   u32 wrk_index;
279
280   app = application_get (al->app_index);
281   wrk_index = clib_bitmap_next_set (al->workers, al->accept_rotor + 1);
282   if (wrk_index == ~0)
283     wrk_index = clib_bitmap_first_set (al->workers);
284
285   ASSERT (wrk_index != ~0);
286   al->accept_rotor = wrk_index;
287   return application_get_worker (app, wrk_index);
288 }
289
290 session_t *
291 app_listener_get_session (app_listener_t * al)
292 {
293   if (al->session_index == SESSION_INVALID_INDEX)
294     return 0;
295
296   return listen_session_get (al->session_index);
297 }
298
299 static app_worker_map_t *
300 app_worker_map_alloc (application_t * app)
301 {
302   app_worker_map_t *map;
303   pool_get (app->worker_maps, map);
304   clib_memset (map, 0, sizeof (*map));
305   return map;
306 }
307
308 static u32
309 app_worker_map_index (application_t * app, app_worker_map_t * map)
310 {
311   return (map - app->worker_maps);
312 }
313
314 static void
315 app_worker_map_free (application_t * app, app_worker_map_t * map)
316 {
317   pool_put (app->worker_maps, map);
318 }
319
320 static app_worker_map_t *
321 app_worker_map_get (application_t * app, u32 map_index)
322 {
323   if (pool_is_free_index (app->worker_maps, map_index))
324     return 0;
325   return pool_elt_at_index (app->worker_maps, map_index);
326 }
327
328 static const u8 *
329 app_get_name (application_t * app)
330 {
331   return app->name;
332 }
333
334 u32
335 application_session_table (application_t * app, u8 fib_proto)
336 {
337   app_namespace_t *app_ns;
338   app_ns = app_namespace_get (app->ns_index);
339   if (!application_has_global_scope (app))
340     return APP_INVALID_INDEX;
341   if (fib_proto == FIB_PROTOCOL_IP4)
342     return session_lookup_get_index_for_fib (fib_proto,
343                                              app_ns->ip4_fib_index);
344   else
345     return session_lookup_get_index_for_fib (fib_proto,
346                                              app_ns->ip6_fib_index);
347 }
348
349 u32
350 application_local_session_table (application_t * app)
351 {
352   app_namespace_t *app_ns;
353   if (!application_has_local_scope (app))
354     return APP_INVALID_INDEX;
355   app_ns = app_namespace_get (app->ns_index);
356   return app_ns->local_table_index;
357 }
358
359 /**
360  * Returns app name for app-index
361  */
362 const u8 *
363 application_name_from_index (u32 app_index)
364 {
365   application_t *app = application_get (app_index);
366   if (!app)
367     return 0;
368   return app_get_name (app);
369 }
370
371 static void
372 application_api_table_add (u32 app_index, u32 api_client_index)
373 {
374   if (api_client_index != APP_INVALID_INDEX)
375     hash_set (app_main.app_by_api_client_index, api_client_index, app_index);
376 }
377
378 static void
379 application_api_table_del (u32 api_client_index)
380 {
381   hash_unset (app_main.app_by_api_client_index, api_client_index);
382 }
383
384 static void
385 application_name_table_add (application_t * app)
386 {
387   hash_set_mem (app_main.app_by_name, app->name, app->app_index);
388 }
389
390 static void
391 application_name_table_del (application_t * app)
392 {
393   hash_unset_mem (app_main.app_by_name, app->name);
394 }
395
396 application_t *
397 application_lookup (u32 api_client_index)
398 {
399   uword *p;
400   p = hash_get (app_main.app_by_api_client_index, api_client_index);
401   if (p)
402     return application_get_if_valid (p[0]);
403
404   return 0;
405 }
406
407 application_t *
408 application_lookup_name (const u8 * name)
409 {
410   uword *p;
411   p = hash_get_mem (app_main.app_by_name, name);
412   if (p)
413     return application_get (p[0]);
414
415   return 0;
416 }
417
418 application_t *
419 application_alloc (void)
420 {
421   application_t *app;
422   pool_get (app_main.app_pool, app);
423   clib_memset (app, 0, sizeof (*app));
424   app->app_index = app - app_main.app_pool;
425   return app;
426 }
427
428 application_t *
429 application_get (u32 app_index)
430 {
431   if (app_index == APP_INVALID_INDEX)
432     return 0;
433   return pool_elt_at_index (app_main.app_pool, app_index);
434 }
435
436 application_t *
437 application_get_if_valid (u32 app_index)
438 {
439   if (pool_is_free_index (app_main.app_pool, app_index))
440     return 0;
441
442   return pool_elt_at_index (app_main.app_pool, app_index);
443 }
444
445 static void
446 application_verify_cb_fns (session_cb_vft_t * cb_fns)
447 {
448   if (cb_fns->session_accept_callback == 0)
449     clib_warning ("No accept callback function provided");
450   if (cb_fns->session_connected_callback == 0)
451     clib_warning ("No session connected callback function provided");
452   if (cb_fns->session_disconnect_callback == 0)
453     clib_warning ("No session disconnect callback function provided");
454   if (cb_fns->session_reset_callback == 0)
455     clib_warning ("No session reset callback function provided");
456 }
457
458 /**
459  * Check app config for given segment type
460  *
461  * Returns 1 on success and 0 otherwise
462  */
463 static u8
464 application_verify_cfg (ssvm_segment_type_t st)
465 {
466   u8 is_valid;
467   if (st == SSVM_SEGMENT_MEMFD)
468     {
469       is_valid = (session_manager_get_evt_q_segment () != 0);
470       if (!is_valid)
471         clib_warning ("memfd seg: vpp's event qs IN binary api svm region");
472       return is_valid;
473     }
474   else if (st == SSVM_SEGMENT_SHM)
475     {
476       is_valid = (session_manager_get_evt_q_segment () == 0);
477       if (!is_valid)
478         clib_warning ("shm seg: vpp's event qs NOT IN binary api svm region");
479       return is_valid;
480     }
481   else
482     return 1;
483 }
484
485 int
486 application_alloc_and_init (app_init_args_t * a)
487 {
488   ssvm_segment_type_t seg_type = SSVM_SEGMENT_MEMFD;
489   segment_manager_properties_t *props;
490   vl_api_registration_t *reg;
491   application_t *app;
492   u64 *options;
493
494   app = application_alloc ();
495   options = a->options;
496   /*
497    * Make sure we support the requested configuration
498    */
499   if (!(options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_IS_BUILTIN))
500     {
501       reg = vl_api_client_index_to_registration (a->api_client_index);
502       if (!reg)
503         return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
504       if (vl_api_registration_file_index (reg) == VL_API_INVALID_FI)
505         seg_type = SSVM_SEGMENT_SHM;
506     }
507   else
508     {
509       if (options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_EVT_MQ_USE_EVENTFD)
510         {
511           clib_warning ("mq eventfds can only be used if socket transport is "
512                         "used for api");
513           return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
514         }
515       seg_type = SSVM_SEGMENT_PRIVATE;
516     }
517
518   if (!application_verify_cfg (seg_type))
519     return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
520
521   /* Check that the obvious things are properly set up */
522   application_verify_cb_fns (a->session_cb_vft);
523
524   app->flags = options[APP_OPTIONS_FLAGS];
525   app->cb_fns = *a->session_cb_vft;
526   app->ns_index = options[APP_OPTIONS_NAMESPACE];
527   app->proxied_transports = options[APP_OPTIONS_PROXY_TRANSPORT];
528   app->name = vec_dup (a->name);
529
530   /* If no scope enabled, default to global */
531   if (!application_has_global_scope (app)
532       && !application_has_local_scope (app))
533     app->flags |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
534
535   props = application_segment_manager_properties (app);
536   segment_manager_properties_init (props);
537   props->segment_size = options[APP_OPTIONS_ADD_SEGMENT_SIZE];
538   props->prealloc_fifos = options[APP_OPTIONS_PREALLOC_FIFO_PAIRS];
539   if (options[APP_OPTIONS_ADD_SEGMENT_SIZE])
540     {
541       props->add_segment_size = options[APP_OPTIONS_ADD_SEGMENT_SIZE];
542       props->add_segment = 1;
543     }
544   if (options[APP_OPTIONS_RX_FIFO_SIZE])
545     props->rx_fifo_size = options[APP_OPTIONS_RX_FIFO_SIZE];
546   if (options[APP_OPTIONS_TX_FIFO_SIZE])
547     props->tx_fifo_size = options[APP_OPTIONS_TX_FIFO_SIZE];
548   if (options[APP_OPTIONS_EVT_QUEUE_SIZE])
549     props->evt_q_size = options[APP_OPTIONS_EVT_QUEUE_SIZE];
550   if (options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_EVT_MQ_USE_EVENTFD)
551     props->use_mq_eventfd = 1;
552   if (options[APP_OPTIONS_TLS_ENGINE])
553     app->tls_engine = options[APP_OPTIONS_TLS_ENGINE];
554   props->segment_type = seg_type;
555
556   /* Add app to lookup by api_client_index table */
557   if (!application_is_builtin (app))
558     application_api_table_add (app->app_index, a->api_client_index);
559   else
560     application_name_table_add (app);
561
562   a->app_index = app->app_index;
563
564   APP_DBG ("New app name: %v api index: %u index %u", app->name,
565            app->api_client_index, app->app_index);
566
567   return 0;
568 }
569
570 void
571 application_free (application_t * app)
572 {
573   app_worker_map_t *wrk_map;
574   app_worker_t *app_wrk;
575   u32 table_index;
576   local_session_t *ll;
577   session_endpoint_t sep;
578
579   /*
580    * The app event queue allocated in first segment is cleared with
581    * the segment manager. No need to explicitly free it.
582    */
583   APP_DBG ("Delete app name %v api index: %d index: %d", app->name,
584            app->api_client_index, app->app_index);
585
586   if (application_is_proxy (app))
587     application_remove_proxy (app);
588
589   /*
590    * Free workers
591    */
592
593   /* *INDENT-OFF* */
594   pool_flush (wrk_map, app->worker_maps, ({
595     app_wrk = app_worker_get (wrk_map->wrk_index);
596     app_worker_free (app_wrk);
597   }));
598   /* *INDENT-ON* */
599   pool_free (app->worker_maps);
600
601   /*
602    * Free local listeners. Global table unbinds stop local listeners
603    * as well, but if we have only local binds, these won't be cleaned up.
604    * Don't bother with local accepted sessions, we clean them when
605    * cleaning up the worker.
606    */
607   table_index = application_local_session_table (app);
608   /* *INDENT-OFF* */
609   pool_foreach (ll, app->local_listen_sessions, ({
610     application_local_listener_session_endpoint (ll, &sep);
611     session_lookup_del_session_endpoint (table_index, &sep);
612   }));
613   /* *INDENT-ON* */
614   pool_free (app->local_listen_sessions);
615
616   /*
617    * Cleanup remaining state
618    */
619   if (application_is_builtin (app))
620     application_name_table_del (app);
621   vec_free (app->name);
622   vec_free (app->tls_cert);
623   vec_free (app->tls_key);
624   pool_put (app_main.app_pool, app);
625 }
626
627 void
628 application_detach_process (application_t * app, u32 api_client_index)
629 {
630   vnet_app_worker_add_del_args_t _args = { 0 }, *args = &_args;
631   app_worker_map_t *wrk_map;
632   u32 *wrks = 0, *wrk_index;
633   app_worker_t *app_wrk;
634
635   if (api_client_index == ~0)
636     {
637       application_free (app);
638       return;
639     }
640
641   APP_DBG ("Detaching for app %v index %u api client index %u", app->name,
642            app->app_index, app->api_client_index);
643
644   /* *INDENT-OFF* */
645   pool_foreach (wrk_map, app->worker_maps, ({
646     app_wrk = app_worker_get (wrk_map->wrk_index);
647     if (app_wrk->api_client_index == api_client_index)
648       vec_add1 (wrks, app_wrk->wrk_index);
649   }));
650   /* *INDENT-ON* */
651
652   if (!vec_len (wrks))
653     {
654       clib_warning ("no workers for app %u api_index %u", app->app_index,
655                     api_client_index);
656       return;
657     }
658
659   args->app_index = app->app_index;
660   args->api_client_index = api_client_index;
661   vec_foreach (wrk_index, wrks)
662   {
663     app_wrk = app_worker_get (wrk_index[0]);
664     args->wrk_map_index = app_wrk->wrk_map_index;
665     args->is_add = 0;
666     vnet_app_worker_add_del (args);
667   }
668   vec_free (wrks);
669 }
670
671 app_worker_t *
672 application_get_worker (application_t * app, u32 wrk_map_index)
673 {
674   app_worker_map_t *map;
675   map = app_worker_map_get (app, wrk_map_index);
676   if (!map)
677     return 0;
678   return app_worker_get (map->wrk_index);
679 }
680
681 app_worker_t *
682 application_get_default_worker (application_t * app)
683 {
684   return application_get_worker (app, 0);
685 }
686
687 u32
688 application_n_workers (application_t * app)
689 {
690   return pool_elts (app->worker_maps);
691 }
692
693 app_worker_t *
694 application_listener_select_worker (session_t * ls)
695 {
696   app_listener_t *al;
697
698   al = app_listener_get_w_session (ls);
699   return app_listener_select_worker (al);
700 }
701
702 int
703 application_alloc_worker_and_init (application_t * app, app_worker_t ** wrk)
704 {
705   app_worker_map_t *wrk_map;
706   app_worker_t *app_wrk;
707   segment_manager_t *sm;
708   int rv;
709
710   app_wrk = app_worker_alloc (app);
711   wrk_map = app_worker_map_alloc (app);
712   wrk_map->wrk_index = app_wrk->wrk_index;
713   app_wrk->wrk_map_index = app_worker_map_index (app, wrk_map);
714
715   /*
716    * Setup first segment manager
717    */
718   sm = segment_manager_new ();
719   sm->app_wrk_index = app_wrk->wrk_index;
720
721   if ((rv = segment_manager_init (sm, app->sm_properties.segment_size,
722                                   app->sm_properties.prealloc_fifos)))
723     {
724       app_worker_free (app_wrk);
725       return rv;
726     }
727   sm->first_is_protected = 1;
728
729   /*
730    * Setup app worker
731    */
732   app_wrk->first_segment_manager = segment_manager_index (sm);
733   app_wrk->listeners_table = hash_create (0, sizeof (u64));
734   app_wrk->event_queue = segment_manager_event_queue (sm);
735   app_wrk->app_is_builtin = application_is_builtin (app);
736
737   /*
738    * Segment manager for local sessions
739    */
740   sm = segment_manager_new ();
741   sm->app_wrk_index = app_wrk->wrk_index;
742   app_wrk->local_segment_manager = segment_manager_index (sm);
743   app_wrk->local_connects = hash_create (0, sizeof (u64));
744
745   *wrk = app_wrk;
746
747   return 0;
748 }
749
750 int
751 application_change_listener_owner (session_t * s, app_worker_t * app_wrk)
752 {
753   app_worker_t *old_wrk = app_worker_get (s->app_wrk_index);
754   app_listener_t *app_listener;
755   application_t *app;
756
757   if (!old_wrk)
758     return -1;
759
760   hash_unset (old_wrk->listeners_table, listen_session_get_handle (s));
761   if (session_transport_service_type (s) == TRANSPORT_SERVICE_CL
762       && s->rx_fifo)
763     segment_manager_dealloc_fifos (s->rx_fifo->segment_index, s->rx_fifo,
764                                    s->tx_fifo);
765
766   app = application_get (old_wrk->app_index);
767   if (!app)
768     return -1;
769
770   app_listener = app_listener_get (app, s->al_index);
771
772   /* Only remove from lb for now */
773   app_listener->workers = clib_bitmap_set (app_listener->workers,
774                                            old_wrk->wrk_map_index, 0);
775
776   if (app_worker_start_listen (app_wrk, app_listener))
777     return -1;
778
779   s->app_wrk_index = app_wrk->wrk_index;
780
781   return 0;
782 }
783
784 clib_error_t *
785 vnet_app_worker_add_del (vnet_app_worker_add_del_args_t * a)
786 {
787   svm_fifo_segment_private_t *fs;
788   app_worker_map_t *wrk_map;
789   app_worker_t *app_wrk;
790   segment_manager_t *sm;
791   application_t *app;
792   int rv;
793
794   app = application_get (a->app_index);
795   if (!app)
796     return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
797                                    "App %u does not exist", a->app_index);
798
799   if (a->is_add)
800     {
801       if ((rv = application_alloc_worker_and_init (app, &app_wrk)))
802         return clib_error_return_code (0, rv, 0, "app wrk init: %d", rv);
803
804       /* Map worker api index to the app */
805       app_wrk->api_client_index = a->api_client_index;
806       application_api_table_add (app->app_index, a->api_client_index);
807
808       sm = segment_manager_get (app_wrk->first_segment_manager);
809       fs = segment_manager_get_segment_w_lock (sm, 0);
810       a->segment = &fs->ssvm;
811       a->segment_handle = segment_manager_segment_handle (sm, fs);
812       segment_manager_segment_reader_unlock (sm);
813       a->evt_q = app_wrk->event_queue;
814       a->wrk_map_index = app_wrk->wrk_map_index;
815     }
816   else
817     {
818       wrk_map = app_worker_map_get (app, a->wrk_map_index);
819       if (!wrk_map)
820         return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
821                                        "App %u does not have worker %u",
822                                        app->app_index, a->wrk_map_index);
823       app_wrk = app_worker_get (wrk_map->wrk_index);
824       if (!app_wrk)
825         return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
826                                        "No worker %u", a->wrk_map_index);
827       application_api_table_del (app_wrk->api_client_index);
828       app_worker_free (app_wrk);
829       app_worker_map_free (app, wrk_map);
830       if (application_n_workers (app) == 0)
831         application_free (app);
832     }
833   return 0;
834 }
835
836 int
837 application_is_proxy (application_t * app)
838 {
839   return (app->flags & APP_OPTIONS_FLAGS_IS_PROXY);
840 }
841
842 int
843 application_is_builtin (application_t * app)
844 {
845   return (app->flags & APP_OPTIONS_FLAGS_IS_BUILTIN);
846 }
847
848 int
849 application_is_builtin_proxy (application_t * app)
850 {
851   return (application_is_proxy (app) && application_is_builtin (app));
852 }
853
854 u8
855 application_has_local_scope (application_t * app)
856 {
857   return app->flags & APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
858 }
859
860 u8
861 application_has_global_scope (application_t * app)
862 {
863   return app->flags & APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
864 }
865
866 u8
867 application_use_mq_for_ctrl (application_t * app)
868 {
869   return app->flags & APP_OPTIONS_FLAGS_USE_MQ_FOR_CTRL_MSGS;
870 }
871
872 static clib_error_t *
873 application_start_stop_proxy_fib_proto (application_t * app, u8 fib_proto,
874                                         u8 transport_proto, u8 is_start)
875 {
876   app_namespace_t *app_ns = app_namespace_get (app->ns_index);
877   u8 is_ip4 = (fib_proto == FIB_PROTOCOL_IP4);
878   session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
879   transport_connection_t *tc;
880   app_worker_t *app_wrk;
881   app_listener_t *al;
882   session_t *s;
883   u32 flags;
884
885   /* TODO decide if we want proxy to be enabled for all workers */
886   app_wrk = application_get_default_worker (app);
887   if (is_start)
888     {
889       s = app_worker_first_listener (app_wrk, fib_proto, transport_proto);
890       if (!s)
891         {
892           sep.is_ip4 = is_ip4;
893           sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
894           sep.sw_if_index = app_ns->sw_if_index;
895           sep.transport_proto = transport_proto;
896           sep.app_wrk_index = app_wrk->wrk_index;       /* only default */
897
898           /* force global scope listener */
899           flags = app->flags;
900           app->flags &= ~APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
901           app_listener_alloc_and_init (app, &sep, &al);
902           app->flags = flags;
903
904           app_worker_start_listen (app_wrk, al);
905           s = listen_session_get (al->session_index);
906           s->enqueue_epoch = SESSION_PROXY_LISTENER_INDEX;
907         }
908     }
909   else
910     {
911       s = app_worker_proxy_listener (app_wrk, fib_proto, transport_proto);
912       ASSERT (s);
913     }
914
915   tc = listen_session_get_transport (s);
916
917   if (!ip_is_zero (&tc->lcl_ip, 1))
918     {
919       u32 sti;
920       sep.is_ip4 = is_ip4;
921       sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
922       sep.transport_proto = transport_proto;
923       sep.port = 0;
924       sti = session_lookup_get_index_for_fib (fib_proto, sep.fib_index);
925       if (is_start)
926         session_lookup_add_session_endpoint (sti,
927                                              (session_endpoint_t *) & sep,
928                                              s->session_index);
929       else
930         session_lookup_del_session_endpoint (sti,
931                                              (session_endpoint_t *) & sep);
932     }
933
934   return 0;
935 }
936
937 static void
938 application_start_stop_proxy_local_scope (application_t * app,
939                                           u8 transport_proto, u8 is_start)
940 {
941   session_endpoint_t sep = SESSION_ENDPOINT_NULL;
942   app_namespace_t *app_ns;
943   app_ns = app_namespace_get (app->ns_index);
944   sep.is_ip4 = 1;
945   sep.transport_proto = transport_proto;
946   sep.port = 0;
947
948   if (is_start)
949     {
950       session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
951                                            app->app_index);
952       sep.is_ip4 = 0;
953       session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
954                                            app->app_index);
955     }
956   else
957     {
958       session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
959       sep.is_ip4 = 0;
960       session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
961     }
962 }
963
964 void
965 application_start_stop_proxy (application_t * app,
966                               transport_proto_t transport_proto, u8 is_start)
967 {
968   if (application_has_local_scope (app))
969     application_start_stop_proxy_local_scope (app, transport_proto, is_start);
970
971   if (application_has_global_scope (app))
972     {
973       application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP4,
974                                               transport_proto, is_start);
975       application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP6,
976                                               transport_proto, is_start);
977     }
978 }
979
980 void
981 application_setup_proxy (application_t * app)
982 {
983   u16 transports = app->proxied_transports;
984   transport_proto_t tp;
985
986   ASSERT (application_is_proxy (app));
987
988   /* *INDENT-OFF* */
989   transport_proto_foreach (tp, ({
990     if (transports & (1 << tp))
991       application_start_stop_proxy (app, tp, 1);
992   }));
993   /* *INDENT-ON* */
994 }
995
996 void
997 application_remove_proxy (application_t * app)
998 {
999   u16 transports = app->proxied_transports;
1000   transport_proto_t tp;
1001
1002   ASSERT (application_is_proxy (app));
1003
1004   /* *INDENT-OFF* */
1005   transport_proto_foreach (tp, ({
1006     if (transports & (1 << tp))
1007       application_start_stop_proxy (app, tp, 0);
1008   }));
1009   /* *INDENT-ON* */
1010 }
1011
1012 segment_manager_properties_t *
1013 application_segment_manager_properties (application_t * app)
1014 {
1015   return &app->sm_properties;
1016 }
1017
1018 segment_manager_properties_t *
1019 application_get_segment_manager_properties (u32 app_index)
1020 {
1021   application_t *app = application_get (app_index);
1022   return &app->sm_properties;
1023 }
1024
1025 clib_error_t *
1026 vnet_app_add_tls_cert (vnet_app_add_tls_cert_args_t * a)
1027 {
1028   application_t *app;
1029   app = application_get (a->app_index);
1030   if (!app)
1031     return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1032                                    0, "app %u doesn't exist", a->app_index);
1033   app->tls_cert = vec_dup (a->cert);
1034   return 0;
1035 }
1036
1037 clib_error_t *
1038 vnet_app_add_tls_key (vnet_app_add_tls_key_args_t * a)
1039 {
1040   application_t *app;
1041   app = application_get (a->app_index);
1042   if (!app)
1043     return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1044                                    0, "app %u doesn't exist", a->app_index);
1045   app->tls_key = vec_dup (a->key);
1046   return 0;
1047 }
1048
1049 static void
1050 application_format_listeners (application_t * app, int verbose)
1051 {
1052   vlib_main_t *vm = vlib_get_main ();
1053   app_worker_map_t *wrk_map;
1054   app_worker_t *app_wrk;
1055   u32 sm_index;
1056   u64 handle;
1057
1058   if (!app)
1059     {
1060       vlib_cli_output (vm, "%U", format_app_worker_listener, 0 /* header */ ,
1061                        0, 0, verbose);
1062       return;
1063     }
1064
1065   /* *INDENT-OFF* */
1066   pool_foreach (wrk_map, app->worker_maps, ({
1067     app_wrk = app_worker_get (wrk_map->wrk_index);
1068     if (hash_elts (app_wrk->listeners_table) == 0)
1069       continue;
1070     hash_foreach (handle, sm_index, app_wrk->listeners_table, ({
1071       vlib_cli_output (vm, "%U", format_app_worker_listener, app_wrk,
1072                        handle, sm_index, verbose);
1073     }));
1074   }));
1075   /* *INDENT-ON* */
1076 }
1077
1078 static void
1079 application_format_connects (application_t * app, int verbose)
1080 {
1081   app_worker_map_t *wrk_map;
1082   app_worker_t *app_wrk;
1083
1084   if (!app)
1085     {
1086       app_worker_format_connects (0, verbose);
1087       return;
1088     }
1089
1090   /* *INDENT-OFF* */
1091   pool_foreach (wrk_map, app->worker_maps, ({
1092     app_wrk = app_worker_get (wrk_map->wrk_index);
1093     app_worker_format_connects (app_wrk, verbose);
1094   }));
1095   /* *INDENT-ON* */
1096 }
1097
1098 static void
1099 application_format_local_sessions (application_t * app, int verbose)
1100 {
1101   vlib_main_t *vm = vlib_get_main ();
1102   app_worker_map_t *wrk_map;
1103   app_worker_t *app_wrk;
1104   transport_proto_t tp;
1105   local_session_t *ls;
1106   u8 *conn = 0;
1107
1108   if (!app)
1109     {
1110       app_worker_format_local_sessions (0, verbose);
1111       return;
1112     }
1113
1114   /*
1115    * Format local listeners
1116    */
1117
1118   /* *INDENT-OFF* */
1119   pool_foreach (ls, app->local_listen_sessions, ({
1120     tp = session_type_transport_proto (ls->listener_session_type);
1121     conn = format (0, "[L][%U] *:%u", format_transport_proto_short, tp,
1122                    ls->port);
1123     vlib_cli_output (vm, "%-40v%-15u%-20s", conn, ls->app_wrk_index, "*");
1124     vec_reset_length (conn);
1125   }));
1126   /* *INDENT-ON* */
1127
1128   /*
1129    * Format local accepted/connected sessions
1130    */
1131   /* *INDENT-OFF* */
1132   pool_foreach (wrk_map, app->worker_maps, ({
1133     app_wrk = app_worker_get (wrk_map->wrk_index);
1134     app_worker_format_local_sessions (app_wrk, verbose);
1135   }));
1136   /* *INDENT-ON* */
1137 }
1138
1139 static void
1140 application_format_local_connects (application_t * app, int verbose)
1141 {
1142   app_worker_map_t *wrk_map;
1143   app_worker_t *app_wrk;
1144
1145   if (!app)
1146     {
1147       app_worker_format_local_connects (0, verbose);
1148       return;
1149     }
1150
1151   /* *INDENT-OFF* */
1152   pool_foreach (wrk_map, app->worker_maps, ({
1153     app_wrk = app_worker_get (wrk_map->wrk_index);
1154     app_worker_format_local_connects (app_wrk, verbose);
1155   }));
1156   /* *INDENT-ON* */
1157 }
1158
1159 u8 *
1160 format_application (u8 * s, va_list * args)
1161 {
1162   application_t *app = va_arg (*args, application_t *);
1163   CLIB_UNUSED (int verbose) = va_arg (*args, int);
1164   segment_manager_properties_t *props;
1165   const u8 *app_ns_name, *app_name;
1166   app_worker_map_t *wrk_map;
1167   app_worker_t *app_wrk;
1168
1169   if (app == 0)
1170     {
1171       if (!verbose)
1172         s = format (s, "%-10s%-20s%-40s", "Index", "Name", "Namespace");
1173       return s;
1174     }
1175
1176   app_name = app_get_name (app);
1177   app_ns_name = app_namespace_id_from_index (app->ns_index);
1178   props = application_segment_manager_properties (app);
1179   if (!verbose)
1180     {
1181       s = format (s, "%-10u%-20s%-40s", app->app_index, app_name,
1182                   app_ns_name);
1183       return s;
1184     }
1185
1186   s = format (s, "app-name %s app-index %u ns-index %u seg-size %U\n",
1187               app_name, app->app_index, app->ns_index,
1188               format_memory_size, props->add_segment_size);
1189   s = format (s, "rx-fifo-size %U tx-fifo-size %U workers:\n",
1190               format_memory_size, props->rx_fifo_size,
1191               format_memory_size, props->tx_fifo_size);
1192
1193   /* *INDENT-OFF* */
1194   pool_foreach (wrk_map, app->worker_maps, ({
1195       app_wrk = app_worker_get (wrk_map->wrk_index);
1196       s = format (s, "%U", format_app_worker, app_wrk);
1197   }));
1198   /* *INDENT-ON* */
1199
1200   return s;
1201 }
1202
1203 void
1204 application_format_all_listeners (vlib_main_t * vm, int do_local, int verbose)
1205 {
1206   application_t *app;
1207
1208   if (!pool_elts (app_main.app_pool))
1209     {
1210       vlib_cli_output (vm, "No active server bindings");
1211       return;
1212     }
1213
1214   if (do_local)
1215     {
1216       application_format_local_sessions (0, verbose);
1217       /* *INDENT-OFF* */
1218       pool_foreach (app, app_main.app_pool, ({
1219         application_format_local_sessions (app, verbose);
1220       }));
1221       /* *INDENT-ON* */
1222     }
1223   else
1224     {
1225       application_format_listeners (0, verbose);
1226
1227       /* *INDENT-OFF* */
1228       pool_foreach (app, app_main.app_pool, ({
1229         application_format_listeners (app, verbose);
1230       }));
1231       /* *INDENT-ON* */
1232     }
1233 }
1234
1235 void
1236 application_format_all_clients (vlib_main_t * vm, int do_local, int verbose)
1237 {
1238   application_t *app;
1239
1240   if (!pool_elts (app_main.app_pool))
1241     {
1242       vlib_cli_output (vm, "No active apps");
1243       return;
1244     }
1245
1246   if (do_local)
1247     {
1248       application_format_local_connects (0, verbose);
1249
1250       /* *INDENT-OFF* */
1251       pool_foreach (app, app_main.app_pool, ({
1252         application_format_local_connects (app, verbose);
1253       }));
1254       /* *INDENT-ON* */
1255     }
1256   else
1257     {
1258       application_format_connects (0, verbose);
1259
1260       /* *INDENT-OFF* */
1261       pool_foreach (app, app_main.app_pool, ({
1262         application_format_connects (app, verbose);
1263       }));
1264       /* *INDENT-ON* */
1265     }
1266 }
1267
1268 static clib_error_t *
1269 show_app_command_fn (vlib_main_t * vm, unformat_input_t * input,
1270                      vlib_cli_command_t * cmd)
1271 {
1272   int do_server = 0, do_client = 0, do_local = 0;
1273   application_t *app;
1274   u32 app_index = ~0;
1275   int verbose = 0;
1276
1277   session_cli_return_if_not_enabled ();
1278
1279   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1280     {
1281       if (unformat (input, "server"))
1282         do_server = 1;
1283       else if (unformat (input, "client"))
1284         do_client = 1;
1285       else if (unformat (input, "local"))
1286         do_local = 1;
1287       else if (unformat (input, "%u", &app_index))
1288         ;
1289       else if (unformat (input, "verbose"))
1290         verbose = 1;
1291       else
1292         return clib_error_return (0, "unknown input `%U'",
1293                                   format_unformat_error, input);
1294     }
1295
1296   if (do_server)
1297     {
1298       application_format_all_listeners (vm, do_local, verbose);
1299       return 0;
1300     }
1301
1302   if (do_client)
1303     {
1304       application_format_all_clients (vm, do_local, verbose);
1305       return 0;
1306     }
1307
1308   if (app_index != ~0)
1309     {
1310       app = application_get_if_valid (app_index);
1311       if (!app)
1312         return clib_error_return (0, "No app with index %u", app_index);
1313
1314       vlib_cli_output (vm, "%U", format_application, app, /* verbose */ 1);
1315       return 0;
1316     }
1317
1318   /* Print app related info */
1319   if (!do_server && !do_client)
1320     {
1321       vlib_cli_output (vm, "%U", format_application, 0, 0);
1322       /* *INDENT-OFF* */
1323       pool_foreach (app, app_main.app_pool, ({
1324         vlib_cli_output (vm, "%U", format_application, app, 0);
1325       }));
1326       /* *INDENT-ON* */
1327     }
1328
1329   return 0;
1330 }
1331
1332 /* *INDENT-OFF* */
1333 VLIB_CLI_COMMAND (show_app_command, static) =
1334 {
1335   .path = "show app",
1336   .short_help = "show app [server|client] [verbose]",
1337   .function = show_app_command_fn,
1338 };
1339 /* *INDENT-ON* */
1340
1341 /*
1342  * fd.io coding-style-patch-verification: ON
1343  *
1344  * Local Variables:
1345  * eval: (c-set-style "gnu")
1346  * End:
1347  */