tcp: harden for high scale scenarios
[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 #define app_interface_check_thread_and_barrier(_fn, _arg)               \
24   if (PREDICT_FALSE (!vlib_thread_is_main_w_barrier ()))                \
25     {                                                                   \
26       vlib_rpc_call_main_thread (_fn, (u8 *) _arg, sizeof(*_arg));      \
27       return 0;                                                         \
28     }
29
30 static void
31 application_local_listener_session_endpoint (local_session_t * ll,
32                                              session_endpoint_t * sep)
33 {
34   sep->transport_proto =
35     session_type_transport_proto (ll->listener_session_type);
36   sep->port = ll->port;
37   sep->is_ip4 = ll->listener_session_type & 1;
38 }
39
40 static app_listener_t *
41 app_listener_alloc (application_t * app)
42 {
43   app_listener_t *app_listener;
44   pool_get (app->listeners, app_listener);
45   clib_memset (app_listener, 0, sizeof (*app_listener));
46   app_listener->al_index = app_listener - app->listeners;
47   app_listener->app_index = app->app_index;
48   app_listener->session_index = SESSION_INVALID_INDEX;
49   app_listener->local_index = SESSION_INVALID_INDEX;
50   return app_listener;
51 }
52
53 app_listener_t *
54 app_listener_get (application_t * app, u32 app_listener_index)
55 {
56   return pool_elt_at_index (app->listeners, app_listener_index);
57 }
58
59 static void
60 app_listener_free (application_t * app, app_listener_t * app_listener)
61 {
62   clib_bitmap_free (app_listener->workers);
63   pool_put (app->listeners, app_listener);
64   if (CLIB_DEBUG)
65     clib_memset (app_listener, 0xfa, sizeof (*app_listener));
66 }
67
68 local_session_t *
69 application_local_listen_session_alloc (application_t * app)
70 {
71   local_session_t *ll;
72   pool_get_zero (app->local_listen_sessions, ll);
73   ll->session_index = ll - app->local_listen_sessions;
74   ll->session_type = session_type_from_proto_and_ip (TRANSPORT_PROTO_NONE, 0);
75   ll->app_index = app->app_index;
76   ll->session_state = SESSION_STATE_LISTENING;
77   return ll;
78 }
79
80 void
81 application_local_listen_session_free (application_t * app,
82                                        local_session_t * ll)
83 {
84   pool_put (app->local_listen_sessions, ll);
85   if (CLIB_DEBUG)
86     clib_memset (ll, 0xfb, sizeof (*ll));
87 }
88
89 static u32
90 app_listener_id (app_listener_t * al)
91 {
92   ASSERT (al->app_index < 1 << 16 && al->al_index < 1 << 16);
93   return (al->app_index << 16 | al->al_index);
94 }
95
96 session_handle_t
97 app_listener_handle (app_listener_t * al)
98 {
99   return ((u64) SESSION_LISTENER_PREFIX << 32 | (u64) app_listener_id (al));
100 }
101
102 static void
103 app_listener_id_parse (u32 listener_id, u32 * app_index,
104                        u32 * app_listener_index)
105 {
106   *app_index = listener_id >> 16;
107   *app_listener_index = listener_id & 0xFFFF;
108 }
109
110 void
111 app_listener_handle_parse (session_handle_t handle, u32 * app_index,
112                            u32 * app_listener_index)
113 {
114   app_listener_id_parse (handle & 0xFFFFFFFF, app_index, app_listener_index);
115 }
116
117 static app_listener_t *
118 app_listener_get_w_id (u32 listener_id)
119 {
120   u32 app_index, app_listener_index;
121   application_t *app;
122
123   app_listener_id_parse (listener_id, &app_index, &app_listener_index);
124   app = application_get_if_valid (app_index);
125   if (!app)
126     return 0;
127   return app_listener_get (app, app_listener_index);
128 }
129
130 app_listener_t *
131 app_listener_get_w_session (session_t * ls)
132 {
133   application_t *app;
134
135   app = application_get_if_valid (ls->app_index);
136   if (!app)
137     return 0;
138   return app_listener_get (app, ls->al_index);
139 }
140
141 app_listener_t *
142 app_listener_get_w_handle (session_handle_t handle)
143 {
144
145   if (handle >> 32 != SESSION_LISTENER_PREFIX)
146     return 0;
147
148   return app_listener_get_w_id (handle & 0xFFFFFFFF);
149 }
150
151 app_listener_t *
152 app_listener_lookup (application_t * app, session_endpoint_cfg_t * sep_ext)
153 {
154   u32 table_index, fib_proto;
155   session_endpoint_t *sep;
156   session_handle_t handle;
157   local_session_t *ll;
158   session_t *ls;
159
160   sep = (session_endpoint_t *) sep_ext;
161   if (application_has_local_scope (app) && session_endpoint_is_local (sep))
162     {
163       table_index = application_local_session_table (app);
164       handle = session_lookup_endpoint_listener (table_index, sep, 1);
165       if (handle != SESSION_INVALID_HANDLE)
166         {
167           ll = application_get_local_listener_w_handle (handle);
168           return app_listener_get_w_session ((session_t *) ll);
169         }
170     }
171
172   fib_proto = session_endpoint_fib_proto (sep);
173   table_index = application_session_table (app, fib_proto);
174   handle = session_lookup_endpoint_listener (table_index, sep, 1);
175   if (handle != SESSION_INVALID_HANDLE)
176     {
177       ls = listen_session_get_from_handle (handle);
178       return app_listener_get_w_session ((session_t *) ls);
179     }
180
181   return 0;
182 }
183
184 int
185 app_listener_alloc_and_init (application_t * app,
186                              session_endpoint_cfg_t * sep,
187                              app_listener_t ** listener)
188 {
189   app_listener_t *app_listener;
190   local_session_t *ll = 0;
191   session_handle_t lh;
192   session_type_t st;
193   session_t *ls = 0;
194   int rv;
195
196   app_listener = app_listener_alloc (app);
197   st = session_type_from_proto_and_ip (sep->transport_proto, sep->is_ip4);
198
199   /*
200    * Add session endpoint to local session table. Only binds to "inaddr_any"
201    * (i.e., zero address) are added to local scope table.
202    */
203   if (application_has_local_scope (app)
204       && session_endpoint_is_local ((session_endpoint_t *) sep))
205     {
206       u32 table_index;
207
208       ll = application_local_listen_session_alloc (app);
209       ll->port = sep->port;
210       /* Store the original session type for the unbind */
211       ll->listener_session_type = st;
212       table_index = application_local_session_table (app);
213       lh = application_local_session_handle (ll);
214       session_lookup_add_session_endpoint (table_index,
215                                            (session_endpoint_t *) sep, lh);
216       app_listener->local_index = ll->session_index;
217       ll->al_index = app_listener->al_index;
218     }
219
220   if (application_has_global_scope (app))
221     {
222       /*
223        * Start listening on local endpoint for requested transport and scope.
224        * Creates a stream session with state LISTENING to be used in session
225        * lookups, prior to establishing connection. Requests transport to
226        * build it's own specific listening connection.
227        */
228       ls = listen_session_new (0, st);
229       ls->app_index = app->app_index;
230       ls->app_wrk_index = sep->app_wrk_index;
231
232       /* Listen pool can be reallocated if the transport is
233        * recursive (tls) */
234       lh = session_handle (ls);
235
236       if ((rv = session_listen (ls, sep)))
237         {
238           ls = session_get_from_handle (lh);
239           session_free (ls);
240           return rv;
241         }
242       app_listener->session_index = ls->session_index;
243       ls->al_index = app_listener->al_index;
244     }
245
246   if (!ll && !ls)
247     {
248       app_listener_free (app, app_listener);
249       return -1;
250     }
251
252   *listener = app_listener;
253   return 0;
254 }
255
256 void
257 app_listener_cleanup (app_listener_t * al)
258 {
259   application_t *app = application_get (al->app_index);
260
261   if (al->session_index != SESSION_INVALID_INDEX)
262     {
263       session_t *ls = session_get (al->session_index, 0);
264       session_stop_listen (ls);
265       listen_session_del (ls);
266     }
267   if (al->local_index != SESSION_INVALID_INDEX)
268     {
269       session_endpoint_t sep = SESSION_ENDPOINT_NULL;
270       local_session_t *ll;
271       u32 table_index;
272
273       table_index = application_local_session_table (app);
274       ll = application_get_local_listen_session (app, al->local_index);
275       application_local_listener_session_endpoint (ll, &sep);
276       session_lookup_del_session_endpoint (table_index, &sep);
277       application_local_listen_session_free (app, ll);
278     }
279   app_listener_free (app, al);
280 }
281
282 app_worker_t *
283 app_listener_select_worker (app_listener_t * al)
284 {
285   application_t *app;
286   u32 wrk_index;
287
288   app = application_get (al->app_index);
289   wrk_index = clib_bitmap_next_set (al->workers, al->accept_rotor + 1);
290   if (wrk_index == ~0)
291     wrk_index = clib_bitmap_first_set (al->workers);
292
293   ASSERT (wrk_index != ~0);
294   al->accept_rotor = wrk_index;
295   return application_get_worker (app, wrk_index);
296 }
297
298 session_t *
299 app_listener_get_session (app_listener_t * al)
300 {
301   if (al->session_index == SESSION_INVALID_INDEX)
302     return 0;
303
304   return listen_session_get (al->session_index);
305 }
306
307 static app_worker_map_t *
308 app_worker_map_alloc (application_t * app)
309 {
310   app_worker_map_t *map;
311   pool_get (app->worker_maps, map);
312   clib_memset (map, 0, sizeof (*map));
313   return map;
314 }
315
316 static u32
317 app_worker_map_index (application_t * app, app_worker_map_t * map)
318 {
319   return (map - app->worker_maps);
320 }
321
322 static void
323 app_worker_map_free (application_t * app, app_worker_map_t * map)
324 {
325   pool_put (app->worker_maps, map);
326 }
327
328 static app_worker_map_t *
329 app_worker_map_get (application_t * app, u32 map_index)
330 {
331   if (pool_is_free_index (app->worker_maps, map_index))
332     return 0;
333   return pool_elt_at_index (app->worker_maps, map_index);
334 }
335
336 static const u8 *
337 app_get_name (application_t * app)
338 {
339   return app->name;
340 }
341
342 u32
343 application_session_table (application_t * app, u8 fib_proto)
344 {
345   app_namespace_t *app_ns;
346   app_ns = app_namespace_get (app->ns_index);
347   if (!application_has_global_scope (app))
348     return APP_INVALID_INDEX;
349   if (fib_proto == FIB_PROTOCOL_IP4)
350     return session_lookup_get_index_for_fib (fib_proto,
351                                              app_ns->ip4_fib_index);
352   else
353     return session_lookup_get_index_for_fib (fib_proto,
354                                              app_ns->ip6_fib_index);
355 }
356
357 u32
358 application_local_session_table (application_t * app)
359 {
360   app_namespace_t *app_ns;
361   if (!application_has_local_scope (app))
362     return APP_INVALID_INDEX;
363   app_ns = app_namespace_get (app->ns_index);
364   return app_ns->local_table_index;
365 }
366
367 /**
368  * Returns app name for app-index
369  */
370 const u8 *
371 application_name_from_index (u32 app_index)
372 {
373   application_t *app = application_get (app_index);
374   if (!app)
375     return 0;
376   return app_get_name (app);
377 }
378
379 static void
380 application_api_table_add (u32 app_index, u32 api_client_index)
381 {
382   if (api_client_index != APP_INVALID_INDEX)
383     hash_set (app_main.app_by_api_client_index, api_client_index, app_index);
384 }
385
386 static void
387 application_api_table_del (u32 api_client_index)
388 {
389   hash_unset (app_main.app_by_api_client_index, api_client_index);
390 }
391
392 static void
393 application_name_table_add (application_t * app)
394 {
395   hash_set_mem (app_main.app_by_name, app->name, app->app_index);
396 }
397
398 static void
399 application_name_table_del (application_t * app)
400 {
401   hash_unset_mem (app_main.app_by_name, app->name);
402 }
403
404 application_t *
405 application_lookup (u32 api_client_index)
406 {
407   uword *p;
408   p = hash_get (app_main.app_by_api_client_index, api_client_index);
409   if (p)
410     return application_get_if_valid (p[0]);
411
412   return 0;
413 }
414
415 application_t *
416 application_lookup_name (const u8 * name)
417 {
418   uword *p;
419   p = hash_get_mem (app_main.app_by_name, name);
420   if (p)
421     return application_get (p[0]);
422
423   return 0;
424 }
425
426 static application_t *
427 application_alloc (void)
428 {
429   application_t *app;
430   pool_get (app_main.app_pool, app);
431   clib_memset (app, 0, sizeof (*app));
432   app->app_index = app - app_main.app_pool;
433   return app;
434 }
435
436 application_t *
437 application_get (u32 app_index)
438 {
439   if (app_index == APP_INVALID_INDEX)
440     return 0;
441   return pool_elt_at_index (app_main.app_pool, app_index);
442 }
443
444 application_t *
445 application_get_if_valid (u32 app_index)
446 {
447   if (pool_is_free_index (app_main.app_pool, app_index))
448     return 0;
449
450   return pool_elt_at_index (app_main.app_pool, app_index);
451 }
452
453 static void
454 application_verify_cb_fns (session_cb_vft_t * cb_fns)
455 {
456   if (cb_fns->session_accept_callback == 0)
457     clib_warning ("No accept callback function provided");
458   if (cb_fns->session_connected_callback == 0)
459     clib_warning ("No session connected callback function provided");
460   if (cb_fns->session_disconnect_callback == 0)
461     clib_warning ("No session disconnect callback function provided");
462   if (cb_fns->session_reset_callback == 0)
463     clib_warning ("No session reset callback function provided");
464 }
465
466 /**
467  * Check app config for given segment type
468  *
469  * Returns 1 on success and 0 otherwise
470  */
471 static u8
472 application_verify_cfg (ssvm_segment_type_t st)
473 {
474   u8 is_valid;
475   if (st == SSVM_SEGMENT_MEMFD)
476     {
477       is_valid = (session_manager_get_evt_q_segment () != 0);
478       if (!is_valid)
479         clib_warning ("memfd seg: vpp's event qs IN binary api svm region");
480       return is_valid;
481     }
482   else if (st == SSVM_SEGMENT_SHM)
483     {
484       is_valid = (session_manager_get_evt_q_segment () == 0);
485       if (!is_valid)
486         clib_warning ("shm seg: vpp's event qs NOT IN binary api svm region");
487       return is_valid;
488     }
489   else
490     return 1;
491 }
492
493 static int
494 application_alloc_and_init (app_init_args_t * a)
495 {
496   ssvm_segment_type_t seg_type = SSVM_SEGMENT_MEMFD;
497   segment_manager_properties_t *props;
498   vl_api_registration_t *reg;
499   application_t *app;
500   u64 *options;
501
502   app = application_alloc ();
503   options = a->options;
504   /*
505    * Make sure we support the requested configuration
506    */
507   if (!(options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_IS_BUILTIN))
508     {
509       reg = vl_api_client_index_to_registration (a->api_client_index);
510       if (!reg)
511         return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
512       if (vl_api_registration_file_index (reg) == VL_API_INVALID_FI)
513         seg_type = SSVM_SEGMENT_SHM;
514     }
515   else
516     {
517       if (options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_EVT_MQ_USE_EVENTFD)
518         {
519           clib_warning ("mq eventfds can only be used if socket transport is "
520                         "used for api");
521           return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
522         }
523       seg_type = SSVM_SEGMENT_PRIVATE;
524     }
525
526   if (!application_verify_cfg (seg_type))
527     return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
528
529   /* Check that the obvious things are properly set up */
530   application_verify_cb_fns (a->session_cb_vft);
531
532   app->flags = options[APP_OPTIONS_FLAGS];
533   app->cb_fns = *a->session_cb_vft;
534   app->ns_index = options[APP_OPTIONS_NAMESPACE];
535   app->proxied_transports = options[APP_OPTIONS_PROXY_TRANSPORT];
536   app->name = vec_dup (a->name);
537
538   /* If no scope enabled, default to global */
539   if (!application_has_global_scope (app)
540       && !application_has_local_scope (app))
541     app->flags |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
542
543   props = application_segment_manager_properties (app);
544   segment_manager_properties_init (props);
545   props->segment_size = options[APP_OPTIONS_ADD_SEGMENT_SIZE];
546   props->prealloc_fifos = options[APP_OPTIONS_PREALLOC_FIFO_PAIRS];
547   if (options[APP_OPTIONS_ADD_SEGMENT_SIZE])
548     {
549       props->add_segment_size = options[APP_OPTIONS_ADD_SEGMENT_SIZE];
550       props->add_segment = 1;
551     }
552   if (options[APP_OPTIONS_RX_FIFO_SIZE])
553     props->rx_fifo_size = options[APP_OPTIONS_RX_FIFO_SIZE];
554   if (options[APP_OPTIONS_TX_FIFO_SIZE])
555     props->tx_fifo_size = options[APP_OPTIONS_TX_FIFO_SIZE];
556   if (options[APP_OPTIONS_EVT_QUEUE_SIZE])
557     props->evt_q_size = options[APP_OPTIONS_EVT_QUEUE_SIZE];
558   if (options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_EVT_MQ_USE_EVENTFD)
559     props->use_mq_eventfd = 1;
560   if (options[APP_OPTIONS_TLS_ENGINE])
561     app->tls_engine = options[APP_OPTIONS_TLS_ENGINE];
562   props->segment_type = seg_type;
563
564   /* Add app to lookup by api_client_index table */
565   if (!application_is_builtin (app))
566     application_api_table_add (app->app_index, a->api_client_index);
567   else
568     application_name_table_add (app);
569
570   a->app_index = app->app_index;
571
572   APP_DBG ("New app name: %v api index: %u index %u", app->name,
573            app->api_client_index, app->app_index);
574
575   return 0;
576 }
577
578 static void
579 application_free (application_t * app)
580 {
581   app_worker_map_t *wrk_map;
582   app_worker_t *app_wrk;
583   u32 table_index;
584   local_session_t *ll;
585   session_endpoint_t sep;
586
587   /*
588    * The app event queue allocated in first segment is cleared with
589    * the segment manager. No need to explicitly free it.
590    */
591   APP_DBG ("Delete app name %v api index: %d index: %d", app->name,
592            app->api_client_index, app->app_index);
593
594   if (application_is_proxy (app))
595     application_remove_proxy (app);
596
597   /*
598    * Free workers
599    */
600
601   /* *INDENT-OFF* */
602   pool_flush (wrk_map, app->worker_maps, ({
603     app_wrk = app_worker_get (wrk_map->wrk_index);
604     app_worker_free (app_wrk);
605   }));
606   /* *INDENT-ON* */
607   pool_free (app->worker_maps);
608
609   /*
610    * Free local listeners. Global table unbinds stop local listeners
611    * as well, but if we have only local binds, these won't be cleaned up.
612    * Don't bother with local accepted sessions, we clean them when
613    * cleaning up the worker.
614    */
615   table_index = application_local_session_table (app);
616   /* *INDENT-OFF* */
617   pool_foreach (ll, app->local_listen_sessions, ({
618     application_local_listener_session_endpoint (ll, &sep);
619     session_lookup_del_session_endpoint (table_index, &sep);
620   }));
621   /* *INDENT-ON* */
622   pool_free (app->local_listen_sessions);
623
624   /*
625    * Cleanup remaining state
626    */
627   if (application_is_builtin (app))
628     application_name_table_del (app);
629   vec_free (app->name);
630   vec_free (app->tls_cert);
631   vec_free (app->tls_key);
632   pool_put (app_main.app_pool, app);
633 }
634
635 static void
636 application_detach_process (application_t * app, u32 api_client_index)
637 {
638   vnet_app_worker_add_del_args_t _args = { 0 }, *args = &_args;
639   app_worker_map_t *wrk_map;
640   u32 *wrks = 0, *wrk_index;
641   app_worker_t *app_wrk;
642
643   if (api_client_index == ~0)
644     {
645       application_free (app);
646       return;
647     }
648
649   APP_DBG ("Detaching for app %v index %u api client index %u", app->name,
650            app->app_index, app->api_client_index);
651
652   /* *INDENT-OFF* */
653   pool_foreach (wrk_map, app->worker_maps, ({
654     app_wrk = app_worker_get (wrk_map->wrk_index);
655     if (app_wrk->api_client_index == api_client_index)
656       vec_add1 (wrks, app_wrk->wrk_index);
657   }));
658   /* *INDENT-ON* */
659
660   if (!vec_len (wrks))
661     {
662       clib_warning ("no workers for app %u api_index %u", app->app_index,
663                     api_client_index);
664       return;
665     }
666
667   args->app_index = app->app_index;
668   args->api_client_index = api_client_index;
669   vec_foreach (wrk_index, wrks)
670   {
671     app_wrk = app_worker_get (wrk_index[0]);
672     args->wrk_map_index = app_wrk->wrk_map_index;
673     args->is_add = 0;
674     vnet_app_worker_add_del (args);
675   }
676   vec_free (wrks);
677 }
678
679 app_worker_t *
680 application_get_worker (application_t * app, u32 wrk_map_index)
681 {
682   app_worker_map_t *map;
683   map = app_worker_map_get (app, wrk_map_index);
684   if (!map)
685     return 0;
686   return app_worker_get (map->wrk_index);
687 }
688
689 app_worker_t *
690 application_get_default_worker (application_t * app)
691 {
692   return application_get_worker (app, 0);
693 }
694
695 u32
696 application_n_workers (application_t * app)
697 {
698   return pool_elts (app->worker_maps);
699 }
700
701 app_worker_t *
702 application_listener_select_worker (session_t * ls)
703 {
704   app_listener_t *al;
705
706   al = app_listener_get_w_session (ls);
707   return app_listener_select_worker (al);
708 }
709
710 int
711 application_alloc_worker_and_init (application_t * app, app_worker_t ** wrk)
712 {
713   app_worker_map_t *wrk_map;
714   app_worker_t *app_wrk;
715   segment_manager_t *sm;
716   int rv;
717
718   app_wrk = app_worker_alloc (app);
719   wrk_map = app_worker_map_alloc (app);
720   wrk_map->wrk_index = app_wrk->wrk_index;
721   app_wrk->wrk_map_index = app_worker_map_index (app, wrk_map);
722
723   /*
724    * Setup first segment manager
725    */
726   sm = segment_manager_new ();
727   sm->app_wrk_index = app_wrk->wrk_index;
728
729   if ((rv = segment_manager_init (sm, app->sm_properties.segment_size,
730                                   app->sm_properties.prealloc_fifos)))
731     {
732       app_worker_free (app_wrk);
733       return rv;
734     }
735   sm->first_is_protected = 1;
736
737   /*
738    * Setup app worker
739    */
740   app_wrk->first_segment_manager = segment_manager_index (sm);
741   app_wrk->listeners_table = hash_create (0, sizeof (u64));
742   app_wrk->event_queue = segment_manager_event_queue (sm);
743   app_wrk->app_is_builtin = application_is_builtin (app);
744
745   /*
746    * Segment manager for local sessions
747    */
748   sm = segment_manager_new ();
749   sm->app_wrk_index = app_wrk->wrk_index;
750   app_wrk->local_segment_manager = segment_manager_index (sm);
751   app_wrk->local_connects = hash_create (0, sizeof (u64));
752
753   *wrk = app_wrk;
754
755   return 0;
756 }
757
758 int
759 vnet_app_worker_add_del (vnet_app_worker_add_del_args_t * a)
760 {
761   svm_fifo_segment_private_t *fs;
762   app_worker_map_t *wrk_map;
763   app_worker_t *app_wrk;
764   segment_manager_t *sm;
765   application_t *app;
766   int rv;
767
768   app = application_get (a->app_index);
769   if (!app)
770     return VNET_API_ERROR_INVALID_VALUE;
771
772   if (a->is_add)
773     {
774       if ((rv = application_alloc_worker_and_init (app, &app_wrk)))
775         return rv;
776
777       /* Map worker api index to the app */
778       app_wrk->api_client_index = a->api_client_index;
779       application_api_table_add (app->app_index, a->api_client_index);
780
781       sm = segment_manager_get (app_wrk->first_segment_manager);
782       fs = segment_manager_get_segment_w_lock (sm, 0);
783       a->segment = &fs->ssvm;
784       a->segment_handle = segment_manager_segment_handle (sm, fs);
785       segment_manager_segment_reader_unlock (sm);
786       a->evt_q = app_wrk->event_queue;
787       a->wrk_map_index = app_wrk->wrk_map_index;
788     }
789   else
790     {
791       wrk_map = app_worker_map_get (app, a->wrk_map_index);
792       if (!wrk_map)
793         return VNET_API_ERROR_INVALID_VALUE;
794
795       app_wrk = app_worker_get (wrk_map->wrk_index);
796       if (!app_wrk)
797         return VNET_API_ERROR_INVALID_VALUE;
798
799       application_api_table_del (app_wrk->api_client_index);
800       app_worker_free (app_wrk);
801       app_worker_map_free (app, wrk_map);
802       if (application_n_workers (app) == 0)
803         application_free (app);
804     }
805   return 0;
806 }
807
808 static int
809 app_validate_namespace (u8 * namespace_id, u64 secret, u32 * app_ns_index)
810 {
811   app_namespace_t *app_ns;
812   if (vec_len (namespace_id) == 0)
813     {
814       /* Use default namespace */
815       *app_ns_index = 0;
816       return 0;
817     }
818
819   *app_ns_index = app_namespace_index_from_id (namespace_id);
820   if (*app_ns_index == APP_NAMESPACE_INVALID_INDEX)
821     return VNET_API_ERROR_APP_INVALID_NS;
822   app_ns = app_namespace_get (*app_ns_index);
823   if (!app_ns)
824     return VNET_API_ERROR_APP_INVALID_NS;
825   if (app_ns->ns_secret != secret)
826     return VNET_API_ERROR_APP_WRONG_NS_SECRET;
827   return 0;
828 }
829
830 static u8 *
831 app_name_from_api_index (u32 api_client_index)
832 {
833   vl_api_registration_t *regp;
834   regp = vl_api_client_index_to_registration (api_client_index);
835   if (regp)
836     return format (0, "%s%c", regp->name, 0);
837
838   clib_warning ("api client index %u does not have an api registration!",
839                 api_client_index);
840   return format (0, "unknown%c", 0);
841 }
842
843 /**
844  * Attach application to vpp
845  *
846  * Allocates a vpp app, i.e., a structure that keeps back pointers
847  * to external app and a segment manager for shared memory fifo based
848  * communication with the external app.
849  */
850 int
851 vnet_application_attach (vnet_app_attach_args_t * a)
852 {
853   svm_fifo_segment_private_t *fs;
854   application_t *app = 0;
855   app_worker_t *app_wrk;
856   segment_manager_t *sm;
857   u32 app_ns_index = 0;
858   u8 *app_name = 0;
859   u64 secret;
860   int rv;
861
862   if (a->api_client_index != APP_INVALID_INDEX)
863     app = application_lookup (a->api_client_index);
864   else if (a->name)
865     app = application_lookup_name (a->name);
866   else
867     return VNET_API_ERROR_INVALID_VALUE;
868
869   if (app)
870     return VNET_API_ERROR_APP_ALREADY_ATTACHED;
871
872   if (a->api_client_index != APP_INVALID_INDEX)
873     {
874       app_name = app_name_from_api_index (a->api_client_index);
875       a->name = app_name;
876     }
877
878   secret = a->options[APP_OPTIONS_NAMESPACE_SECRET];
879   if ((rv = app_validate_namespace (a->namespace_id, secret, &app_ns_index)))
880     return rv;
881   a->options[APP_OPTIONS_NAMESPACE] = app_ns_index;
882
883   if ((rv = application_alloc_and_init ((app_init_args_t *) a)))
884     return rv;
885
886   app = application_get (a->app_index);
887   if ((rv = application_alloc_worker_and_init (app, &app_wrk)))
888     return rv;
889
890   a->app_evt_q = app_wrk->event_queue;
891   app_wrk->api_client_index = a->api_client_index;
892   sm = segment_manager_get (app_wrk->first_segment_manager);
893   fs = segment_manager_get_segment_w_lock (sm, 0);
894
895   if (application_is_proxy (app))
896     application_setup_proxy (app);
897
898   ASSERT (vec_len (fs->ssvm.name) <= 128);
899   a->segment = &fs->ssvm;
900   a->segment_handle = segment_manager_segment_handle (sm, fs);
901
902   segment_manager_segment_reader_unlock (sm);
903   vec_free (app_name);
904   return 0;
905 }
906
907 /**
908  * Detach application from vpp
909  */
910 int
911 vnet_application_detach (vnet_app_detach_args_t * a)
912 {
913   application_t *app;
914
915   app = application_get_if_valid (a->app_index);
916   if (!app)
917     {
918       clib_warning ("app not attached");
919       return VNET_API_ERROR_APPLICATION_NOT_ATTACHED;
920     }
921
922   app_interface_check_thread_and_barrier (vnet_application_detach, a);
923   application_detach_process (app, a->api_client_index);
924   return 0;
925 }
926
927
928 static u8
929 session_endpoint_in_ns (session_endpoint_t * sep)
930 {
931   u8 is_lep = session_endpoint_is_local (sep);
932   if (!is_lep && sep->sw_if_index != ENDPOINT_INVALID_INDEX
933       && !ip_interface_has_address (sep->sw_if_index, &sep->ip, sep->is_ip4))
934     {
935       clib_warning ("sw_if_index %u not configured with ip %U",
936                     sep->sw_if_index, format_ip46_address, &sep->ip,
937                     sep->is_ip4);
938       return 0;
939     }
940   return (is_lep || ip_is_local (sep->fib_index, &sep->ip, sep->is_ip4));
941 }
942
943 static void
944 session_endpoint_update_for_app (session_endpoint_cfg_t * sep,
945                                  application_t * app, u8 is_connect)
946 {
947   app_namespace_t *app_ns;
948   u32 ns_index, fib_index;
949
950   ns_index = app->ns_index;
951
952   /* App is a transport proto, so fetch the calling app's ns */
953   if (app->flags & APP_OPTIONS_FLAGS_IS_TRANSPORT_APP)
954     {
955       app_worker_t *owner_wrk;
956       application_t *owner_app;
957
958       owner_wrk = app_worker_get (sep->app_wrk_index);
959       owner_app = application_get (owner_wrk->app_index);
960       ns_index = owner_app->ns_index;
961     }
962   app_ns = app_namespace_get (ns_index);
963   if (!app_ns)
964     return;
965
966   /* Ask transport and network to bind to/connect using local interface
967    * that "supports" app's namespace. This will fix our local connection
968    * endpoint.
969    */
970
971   /* If in default namespace and user requested a fib index use it */
972   if (ns_index == 0 && sep->fib_index != ENDPOINT_INVALID_INDEX)
973     fib_index = sep->fib_index;
974   else
975     fib_index = sep->is_ip4 ? app_ns->ip4_fib_index : app_ns->ip6_fib_index;
976   sep->peer.fib_index = fib_index;
977   sep->fib_index = fib_index;
978
979   if (!is_connect)
980     {
981       sep->sw_if_index = app_ns->sw_if_index;
982     }
983   else
984     {
985       if (app_ns->sw_if_index != APP_NAMESPACE_INVALID_INDEX
986           && sep->peer.sw_if_index != ENDPOINT_INVALID_INDEX
987           && sep->peer.sw_if_index != app_ns->sw_if_index)
988         clib_warning ("Local sw_if_index different from app ns sw_if_index");
989
990       sep->peer.sw_if_index = app_ns->sw_if_index;
991     }
992 }
993
994 int
995 vnet_listen (vnet_listen_args_t * a)
996 {
997   app_listener_t *app_listener;
998   app_worker_t *app_wrk;
999   application_t *app;
1000   int rv;
1001
1002   app = application_get_if_valid (a->app_index);
1003   if (!app)
1004     return VNET_API_ERROR_APPLICATION_NOT_ATTACHED;
1005
1006   app_wrk = application_get_worker (app, a->wrk_map_index);
1007   if (!app_wrk)
1008     return VNET_API_ERROR_INVALID_VALUE;
1009
1010   a->sep_ext.app_wrk_index = app_wrk->wrk_index;
1011
1012   session_endpoint_update_for_app (&a->sep_ext, app, 0 /* is_connect */ );
1013   if (!session_endpoint_in_ns (&a->sep))
1014     return VNET_API_ERROR_INVALID_VALUE_2;
1015
1016   /*
1017    * Check if we already have an app listener
1018    */
1019   app_listener = app_listener_lookup (app, &a->sep_ext);
1020   if (app_listener)
1021     {
1022       if (app_listener->app_index != app->app_index)
1023         return VNET_API_ERROR_ADDRESS_IN_USE;
1024       if (app_worker_start_listen (app_wrk, app_listener))
1025         return -1;
1026       a->handle = app_listener_handle (app_listener);
1027       return 0;
1028     }
1029
1030   /*
1031    * Create new app listener
1032    */
1033   if ((rv = app_listener_alloc_and_init (app, &a->sep_ext, &app_listener)))
1034     return rv;
1035
1036   if ((rv = app_worker_start_listen (app_wrk, app_listener)))
1037     {
1038       app_listener_cleanup (app_listener);
1039       return rv;
1040     }
1041
1042   a->handle = app_listener_handle (app_listener);
1043   return 0;
1044 }
1045
1046 int
1047 vnet_connect (vnet_connect_args_t * a)
1048 {
1049   app_worker_t *server_wrk, *client_wrk;
1050   application_t *client;
1051   local_session_t *ll;
1052   app_listener_t *al;
1053   u32 table_index;
1054   session_t *ls;
1055   u8 fib_proto;
1056   u64 lh;
1057
1058   if (session_endpoint_is_zero (&a->sep))
1059     return VNET_API_ERROR_INVALID_VALUE;
1060
1061   client = application_get (a->app_index);
1062   session_endpoint_update_for_app (&a->sep_ext, client, 1 /* is_connect */ );
1063   client_wrk = application_get_worker (client, a->wrk_map_index);
1064
1065   /*
1066    * First check the local scope for locally attached destinations.
1067    * If we have local scope, we pass *all* connects through it since we may
1068    * have special policy rules even for non-local destinations, think proxy.
1069    */
1070   if (application_has_local_scope (client))
1071     {
1072       table_index = application_local_session_table (client);
1073       lh = session_lookup_local_endpoint (table_index, &a->sep);
1074       if (lh == SESSION_DROP_HANDLE)
1075         return VNET_API_ERROR_APP_CONNECT_FILTERED;
1076
1077       if (lh == SESSION_INVALID_HANDLE)
1078         goto global_scope;
1079
1080       ll = application_get_local_listener_w_handle (lh);
1081       al = app_listener_get_w_session ((session_t *) ll);
1082
1083       /*
1084        * Break loop if rule in local table points to connecting app. This
1085        * can happen if client is a generic proxy. Route connect through
1086        * global table instead.
1087        */
1088       if (al->app_index == a->app_index)
1089         goto global_scope;
1090
1091       server_wrk = app_listener_select_worker (al);
1092       return app_worker_local_session_connect (client_wrk, server_wrk, ll,
1093                                                a->api_context);
1094     }
1095
1096   /*
1097    * If nothing found, check the global scope for locally attached
1098    * destinations. Make sure first that we're allowed to.
1099    */
1100
1101 global_scope:
1102   if (session_endpoint_is_local (&a->sep))
1103     return VNET_API_ERROR_SESSION_CONNECT;
1104
1105   if (!application_has_global_scope (client))
1106     return VNET_API_ERROR_APP_CONNECT_SCOPE;
1107
1108   fib_proto = session_endpoint_fib_proto (&a->sep);
1109   table_index = application_session_table (client, fib_proto);
1110   ls = session_lookup_listener (table_index, &a->sep);
1111   if (ls)
1112     {
1113       al = app_listener_get_w_session (ls);
1114       server_wrk = app_listener_select_worker (al);
1115       ll = (local_session_t *) ls;
1116       return app_worker_local_session_connect (client_wrk, server_wrk, ll,
1117                                                a->api_context);
1118     }
1119
1120   /*
1121    * Not connecting to a local server, propagate to transport
1122    */
1123   if (app_worker_connect_session (client_wrk, &a->sep, a->api_context))
1124     return VNET_API_ERROR_SESSION_CONNECT;
1125   return 0;
1126 }
1127
1128 int
1129 vnet_unlisten (vnet_unlisten_args_t * a)
1130 {
1131   app_worker_t *app_wrk;
1132   app_listener_t *al;
1133   application_t *app;
1134
1135   if (!(app = application_get_if_valid (a->app_index)))
1136     return VNET_API_ERROR_APPLICATION_NOT_ATTACHED;
1137
1138   al = app_listener_get_w_handle (a->handle);
1139   if (al->app_index != app->app_index)
1140     {
1141       clib_warning ("app doesn't own handle %llu!", a->handle);
1142       return -1;
1143     }
1144
1145   app_wrk = application_get_worker (app, a->wrk_map_index);
1146   if (!app_wrk)
1147     {
1148       clib_warning ("no app %u worker %u", app->app_index, a->wrk_map_index);
1149       return -1;
1150     }
1151
1152   return app_worker_stop_listen (app_wrk, al);
1153 }
1154
1155 int
1156 vnet_disconnect_session (vnet_disconnect_args_t * a)
1157 {
1158   if (session_handle_is_local (a->handle))
1159     {
1160       local_session_t *ls;
1161
1162       /* Disconnect reply came to worker 1 not main thread */
1163       app_interface_check_thread_and_barrier (vnet_disconnect_session, a);
1164
1165       if (!(ls = app_worker_get_local_session_from_handle (a->handle)))
1166         return 0;
1167
1168       return app_worker_local_session_disconnect (a->app_index, ls);
1169     }
1170   else
1171     {
1172       app_worker_t *app_wrk;
1173       session_t *s;
1174
1175       s = session_get_from_handle_if_valid (a->handle);
1176       if (!s)
1177         return VNET_API_ERROR_INVALID_VALUE;
1178       app_wrk = app_worker_get (s->app_wrk_index);
1179       if (app_wrk->app_index != a->app_index)
1180         return VNET_API_ERROR_INVALID_VALUE;
1181
1182       /* We're peeking into another's thread pool. Make sure */
1183       ASSERT (s->session_index == session_index_from_handle (a->handle));
1184
1185       session_close (s);
1186     }
1187   return 0;
1188 }
1189
1190 int
1191 application_change_listener_owner (session_t * s, app_worker_t * app_wrk)
1192 {
1193   app_worker_t *old_wrk = app_worker_get (s->app_wrk_index);
1194   app_listener_t *app_listener;
1195   application_t *app;
1196
1197   if (!old_wrk)
1198     return -1;
1199
1200   hash_unset (old_wrk->listeners_table, listen_session_get_handle (s));
1201   if (session_transport_service_type (s) == TRANSPORT_SERVICE_CL
1202       && s->rx_fifo)
1203     segment_manager_dealloc_fifos (s->rx_fifo->segment_index, s->rx_fifo,
1204                                    s->tx_fifo);
1205
1206   app = application_get (old_wrk->app_index);
1207   if (!app)
1208     return -1;
1209
1210   app_listener = app_listener_get (app, s->al_index);
1211
1212   /* Only remove from lb for now */
1213   app_listener->workers = clib_bitmap_set (app_listener->workers,
1214                                            old_wrk->wrk_map_index, 0);
1215
1216   if (app_worker_start_listen (app_wrk, app_listener))
1217     return -1;
1218
1219   s->app_wrk_index = app_wrk->wrk_index;
1220
1221   return 0;
1222 }
1223
1224 int
1225 application_is_proxy (application_t * app)
1226 {
1227   return (app->flags & APP_OPTIONS_FLAGS_IS_PROXY);
1228 }
1229
1230 int
1231 application_is_builtin (application_t * app)
1232 {
1233   return (app->flags & APP_OPTIONS_FLAGS_IS_BUILTIN);
1234 }
1235
1236 int
1237 application_is_builtin_proxy (application_t * app)
1238 {
1239   return (application_is_proxy (app) && application_is_builtin (app));
1240 }
1241
1242 u8
1243 application_has_local_scope (application_t * app)
1244 {
1245   return app->flags & APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
1246 }
1247
1248 u8
1249 application_has_global_scope (application_t * app)
1250 {
1251   return app->flags & APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
1252 }
1253
1254 u8
1255 application_use_mq_for_ctrl (application_t * app)
1256 {
1257   return app->flags & APP_OPTIONS_FLAGS_USE_MQ_FOR_CTRL_MSGS;
1258 }
1259
1260 static clib_error_t *
1261 application_start_stop_proxy_fib_proto (application_t * app, u8 fib_proto,
1262                                         u8 transport_proto, u8 is_start)
1263 {
1264   app_namespace_t *app_ns = app_namespace_get (app->ns_index);
1265   u8 is_ip4 = (fib_proto == FIB_PROTOCOL_IP4);
1266   session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
1267   transport_connection_t *tc;
1268   app_worker_t *app_wrk;
1269   app_listener_t *al;
1270   session_t *s;
1271   u32 flags;
1272
1273   /* TODO decide if we want proxy to be enabled for all workers */
1274   app_wrk = application_get_default_worker (app);
1275   if (is_start)
1276     {
1277       s = app_worker_first_listener (app_wrk, fib_proto, transport_proto);
1278       if (!s)
1279         {
1280           sep.is_ip4 = is_ip4;
1281           sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
1282           sep.sw_if_index = app_ns->sw_if_index;
1283           sep.transport_proto = transport_proto;
1284           sep.app_wrk_index = app_wrk->wrk_index;       /* only default */
1285
1286           /* force global scope listener */
1287           flags = app->flags;
1288           app->flags &= ~APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
1289           app_listener_alloc_and_init (app, &sep, &al);
1290           app->flags = flags;
1291
1292           app_worker_start_listen (app_wrk, al);
1293           s = listen_session_get (al->session_index);
1294           s->enqueue_epoch = SESSION_PROXY_LISTENER_INDEX;
1295         }
1296     }
1297   else
1298     {
1299       s = app_worker_proxy_listener (app_wrk, fib_proto, transport_proto);
1300       ASSERT (s);
1301     }
1302
1303   tc = listen_session_get_transport (s);
1304
1305   if (!ip_is_zero (&tc->lcl_ip, 1))
1306     {
1307       u32 sti;
1308       sep.is_ip4 = is_ip4;
1309       sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
1310       sep.transport_proto = transport_proto;
1311       sep.port = 0;
1312       sti = session_lookup_get_index_for_fib (fib_proto, sep.fib_index);
1313       if (is_start)
1314         session_lookup_add_session_endpoint (sti,
1315                                              (session_endpoint_t *) & sep,
1316                                              s->session_index);
1317       else
1318         session_lookup_del_session_endpoint (sti,
1319                                              (session_endpoint_t *) & sep);
1320     }
1321
1322   return 0;
1323 }
1324
1325 static void
1326 application_start_stop_proxy_local_scope (application_t * app,
1327                                           u8 transport_proto, u8 is_start)
1328 {
1329   session_endpoint_t sep = SESSION_ENDPOINT_NULL;
1330   app_namespace_t *app_ns;
1331   app_ns = app_namespace_get (app->ns_index);
1332   sep.is_ip4 = 1;
1333   sep.transport_proto = transport_proto;
1334   sep.port = 0;
1335
1336   if (is_start)
1337     {
1338       session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
1339                                            app->app_index);
1340       sep.is_ip4 = 0;
1341       session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
1342                                            app->app_index);
1343     }
1344   else
1345     {
1346       session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
1347       sep.is_ip4 = 0;
1348       session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
1349     }
1350 }
1351
1352 void
1353 application_start_stop_proxy (application_t * app,
1354                               transport_proto_t transport_proto, u8 is_start)
1355 {
1356   if (application_has_local_scope (app))
1357     application_start_stop_proxy_local_scope (app, transport_proto, is_start);
1358
1359   if (application_has_global_scope (app))
1360     {
1361       application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP4,
1362                                               transport_proto, is_start);
1363       application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP6,
1364                                               transport_proto, is_start);
1365     }
1366 }
1367
1368 void
1369 application_setup_proxy (application_t * app)
1370 {
1371   u16 transports = app->proxied_transports;
1372   transport_proto_t tp;
1373
1374   ASSERT (application_is_proxy (app));
1375
1376   /* *INDENT-OFF* */
1377   transport_proto_foreach (tp, ({
1378     if (transports & (1 << tp))
1379       application_start_stop_proxy (app, tp, 1);
1380   }));
1381   /* *INDENT-ON* */
1382 }
1383
1384 void
1385 application_remove_proxy (application_t * app)
1386 {
1387   u16 transports = app->proxied_transports;
1388   transport_proto_t tp;
1389
1390   ASSERT (application_is_proxy (app));
1391
1392   /* *INDENT-OFF* */
1393   transport_proto_foreach (tp, ({
1394     if (transports & (1 << tp))
1395       application_start_stop_proxy (app, tp, 0);
1396   }));
1397   /* *INDENT-ON* */
1398 }
1399
1400 segment_manager_properties_t *
1401 application_segment_manager_properties (application_t * app)
1402 {
1403   return &app->sm_properties;
1404 }
1405
1406 segment_manager_properties_t *
1407 application_get_segment_manager_properties (u32 app_index)
1408 {
1409   application_t *app = application_get (app_index);
1410   return &app->sm_properties;
1411 }
1412
1413 clib_error_t *
1414 vnet_app_add_tls_cert (vnet_app_add_tls_cert_args_t * a)
1415 {
1416   application_t *app;
1417   app = application_get (a->app_index);
1418   if (!app)
1419     return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1420                                    0, "app %u doesn't exist", a->app_index);
1421   app->tls_cert = vec_dup (a->cert);
1422   return 0;
1423 }
1424
1425 clib_error_t *
1426 vnet_app_add_tls_key (vnet_app_add_tls_key_args_t * a)
1427 {
1428   application_t *app;
1429   app = application_get (a->app_index);
1430   if (!app)
1431     return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1432                                    0, "app %u doesn't exist", a->app_index);
1433   app->tls_key = vec_dup (a->key);
1434   return 0;
1435 }
1436
1437 static void
1438 application_format_listeners (application_t * app, int verbose)
1439 {
1440   vlib_main_t *vm = vlib_get_main ();
1441   app_worker_map_t *wrk_map;
1442   app_worker_t *app_wrk;
1443   u32 sm_index;
1444   u64 handle;
1445
1446   if (!app)
1447     {
1448       vlib_cli_output (vm, "%U", format_app_worker_listener, 0 /* header */ ,
1449                        0, 0, verbose);
1450       return;
1451     }
1452
1453   /* *INDENT-OFF* */
1454   pool_foreach (wrk_map, app->worker_maps, ({
1455     app_wrk = app_worker_get (wrk_map->wrk_index);
1456     if (hash_elts (app_wrk->listeners_table) == 0)
1457       continue;
1458     hash_foreach (handle, sm_index, app_wrk->listeners_table, ({
1459       vlib_cli_output (vm, "%U", format_app_worker_listener, app_wrk,
1460                        handle, sm_index, verbose);
1461     }));
1462   }));
1463   /* *INDENT-ON* */
1464 }
1465
1466 static void
1467 application_format_connects (application_t * app, int verbose)
1468 {
1469   app_worker_map_t *wrk_map;
1470   app_worker_t *app_wrk;
1471
1472   if (!app)
1473     {
1474       app_worker_format_connects (0, verbose);
1475       return;
1476     }
1477
1478   /* *INDENT-OFF* */
1479   pool_foreach (wrk_map, app->worker_maps, ({
1480     app_wrk = app_worker_get (wrk_map->wrk_index);
1481     app_worker_format_connects (app_wrk, verbose);
1482   }));
1483   /* *INDENT-ON* */
1484 }
1485
1486 static void
1487 application_format_local_sessions (application_t * app, int verbose)
1488 {
1489   vlib_main_t *vm = vlib_get_main ();
1490   app_worker_map_t *wrk_map;
1491   app_worker_t *app_wrk;
1492   transport_proto_t tp;
1493   local_session_t *ls;
1494   u8 *conn = 0;
1495
1496   if (!app)
1497     {
1498       app_worker_format_local_sessions (0, verbose);
1499       return;
1500     }
1501
1502   /*
1503    * Format local listeners
1504    */
1505
1506   /* *INDENT-OFF* */
1507   pool_foreach (ls, app->local_listen_sessions, ({
1508     tp = session_type_transport_proto (ls->listener_session_type);
1509     conn = format (0, "[L][%U] *:%u", format_transport_proto_short, tp,
1510                    ls->port);
1511     vlib_cli_output (vm, "%-40v%-15u%-20s", conn, ls->app_wrk_index, "*");
1512     vec_reset_length (conn);
1513   }));
1514   /* *INDENT-ON* */
1515
1516   /*
1517    * Format local accepted/connected sessions
1518    */
1519   /* *INDENT-OFF* */
1520   pool_foreach (wrk_map, app->worker_maps, ({
1521     app_wrk = app_worker_get (wrk_map->wrk_index);
1522     app_worker_format_local_sessions (app_wrk, verbose);
1523   }));
1524   /* *INDENT-ON* */
1525 }
1526
1527 static void
1528 application_format_local_connects (application_t * app, int verbose)
1529 {
1530   app_worker_map_t *wrk_map;
1531   app_worker_t *app_wrk;
1532
1533   if (!app)
1534     {
1535       app_worker_format_local_connects (0, verbose);
1536       return;
1537     }
1538
1539   /* *INDENT-OFF* */
1540   pool_foreach (wrk_map, app->worker_maps, ({
1541     app_wrk = app_worker_get (wrk_map->wrk_index);
1542     app_worker_format_local_connects (app_wrk, verbose);
1543   }));
1544   /* *INDENT-ON* */
1545 }
1546
1547 u8 *
1548 format_application (u8 * s, va_list * args)
1549 {
1550   application_t *app = va_arg (*args, application_t *);
1551   CLIB_UNUSED (int verbose) = va_arg (*args, int);
1552   segment_manager_properties_t *props;
1553   const u8 *app_ns_name, *app_name;
1554   app_worker_map_t *wrk_map;
1555   app_worker_t *app_wrk;
1556
1557   if (app == 0)
1558     {
1559       if (!verbose)
1560         s = format (s, "%-10s%-20s%-40s", "Index", "Name", "Namespace");
1561       return s;
1562     }
1563
1564   app_name = app_get_name (app);
1565   app_ns_name = app_namespace_id_from_index (app->ns_index);
1566   props = application_segment_manager_properties (app);
1567   if (!verbose)
1568     {
1569       s = format (s, "%-10u%-20s%-40s", app->app_index, app_name,
1570                   app_ns_name);
1571       return s;
1572     }
1573
1574   s = format (s, "app-name %s app-index %u ns-index %u seg-size %U\n",
1575               app_name, app->app_index, app->ns_index,
1576               format_memory_size, props->add_segment_size);
1577   s = format (s, "rx-fifo-size %U tx-fifo-size %U workers:\n",
1578               format_memory_size, props->rx_fifo_size,
1579               format_memory_size, props->tx_fifo_size);
1580
1581   /* *INDENT-OFF* */
1582   pool_foreach (wrk_map, app->worker_maps, ({
1583       app_wrk = app_worker_get (wrk_map->wrk_index);
1584       s = format (s, "%U", format_app_worker, app_wrk);
1585   }));
1586   /* *INDENT-ON* */
1587
1588   return s;
1589 }
1590
1591 void
1592 application_format_all_listeners (vlib_main_t * vm, int do_local, int verbose)
1593 {
1594   application_t *app;
1595
1596   if (!pool_elts (app_main.app_pool))
1597     {
1598       vlib_cli_output (vm, "No active server bindings");
1599       return;
1600     }
1601
1602   if (do_local)
1603     {
1604       application_format_local_sessions (0, verbose);
1605       /* *INDENT-OFF* */
1606       pool_foreach (app, app_main.app_pool, ({
1607         application_format_local_sessions (app, verbose);
1608       }));
1609       /* *INDENT-ON* */
1610     }
1611   else
1612     {
1613       application_format_listeners (0, verbose);
1614
1615       /* *INDENT-OFF* */
1616       pool_foreach (app, app_main.app_pool, ({
1617         application_format_listeners (app, verbose);
1618       }));
1619       /* *INDENT-ON* */
1620     }
1621 }
1622
1623 void
1624 application_format_all_clients (vlib_main_t * vm, int do_local, int verbose)
1625 {
1626   application_t *app;
1627
1628   if (!pool_elts (app_main.app_pool))
1629     {
1630       vlib_cli_output (vm, "No active apps");
1631       return;
1632     }
1633
1634   if (do_local)
1635     {
1636       application_format_local_connects (0, verbose);
1637
1638       /* *INDENT-OFF* */
1639       pool_foreach (app, app_main.app_pool, ({
1640         application_format_local_connects (app, verbose);
1641       }));
1642       /* *INDENT-ON* */
1643     }
1644   else
1645     {
1646       application_format_connects (0, verbose);
1647
1648       /* *INDENT-OFF* */
1649       pool_foreach (app, app_main.app_pool, ({
1650         application_format_connects (app, verbose);
1651       }));
1652       /* *INDENT-ON* */
1653     }
1654 }
1655
1656 static clib_error_t *
1657 show_app_command_fn (vlib_main_t * vm, unformat_input_t * input,
1658                      vlib_cli_command_t * cmd)
1659 {
1660   int do_server = 0, do_client = 0, do_local = 0;
1661   application_t *app;
1662   u32 app_index = ~0;
1663   int verbose = 0;
1664
1665   session_cli_return_if_not_enabled ();
1666
1667   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1668     {
1669       if (unformat (input, "server"))
1670         do_server = 1;
1671       else if (unformat (input, "client"))
1672         do_client = 1;
1673       else if (unformat (input, "local"))
1674         do_local = 1;
1675       else if (unformat (input, "%u", &app_index))
1676         ;
1677       else if (unformat (input, "verbose"))
1678         verbose = 1;
1679       else
1680         return clib_error_return (0, "unknown input `%U'",
1681                                   format_unformat_error, input);
1682     }
1683
1684   if (do_server)
1685     {
1686       application_format_all_listeners (vm, do_local, verbose);
1687       return 0;
1688     }
1689
1690   if (do_client)
1691     {
1692       application_format_all_clients (vm, do_local, verbose);
1693       return 0;
1694     }
1695
1696   if (app_index != ~0)
1697     {
1698       app = application_get_if_valid (app_index);
1699       if (!app)
1700         return clib_error_return (0, "No app with index %u", app_index);
1701
1702       vlib_cli_output (vm, "%U", format_application, app, /* verbose */ 1);
1703       return 0;
1704     }
1705
1706   /* Print app related info */
1707   if (!do_server && !do_client)
1708     {
1709       vlib_cli_output (vm, "%U", format_application, 0, 0);
1710       /* *INDENT-OFF* */
1711       pool_foreach (app, app_main.app_pool, ({
1712         vlib_cli_output (vm, "%U", format_application, app, 0);
1713       }));
1714       /* *INDENT-ON* */
1715     }
1716
1717   return 0;
1718 }
1719
1720 /* *INDENT-OFF* */
1721 VLIB_CLI_COMMAND (show_app_command, static) =
1722 {
1723   .path = "show app",
1724   .short_help = "show app [server|client] [verbose]",
1725   .function = show_app_command_fn,
1726 };
1727 /* *INDENT-ON* */
1728
1729 /*
1730  * fd.io coding-style-patch-verification: ON
1731  *
1732  * Local Variables:
1733  * eval: (c-set-style "gnu")
1734  * End:
1735  */