blob: 82b6dfef5340fb35ea49c0feb32ea4fdc70c43bd [file] [log] [blame]
Dave Barach68b0fb02017-02-28 15:15:56 -05001/*
Florin Coras288eaab2019-02-03 15:26:14 -08002 * Copyright (c) 2017-2019 Cisco and/or its affiliates.
Dave Barach68b0fb02017-02-28 15:15:56 -05003 * 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>
Florin Coras6cf30ad2017-04-04 23:08:23 -070017#include <vnet/session/application_interface.h>
Florin Corascea194d2017-10-02 00:18:51 -070018#include <vnet/session/application_namespace.h>
Florin Corasba7d8f52019-02-22 13:11:38 -080019#include <vnet/session/application_local.h>
Dave Barach68b0fb02017-02-28 15:15:56 -050020#include <vnet/session/session.h>
21
Florin Coras15531972018-08-12 23:50:53 -070022static app_main_t app_main;
Dave Barach68b0fb02017-02-28 15:15:56 -050023
Florin Corasc1a42652019-02-08 18:27:29 -080024#define app_interface_check_thread_and_barrier(_fn, _arg) \
25 if (PREDICT_FALSE (!vlib_thread_is_main_w_barrier ())) \
26 { \
27 vlib_rpc_call_main_thread (_fn, (u8 *) _arg, sizeof(*_arg)); \
28 return 0; \
29 }
30
Florin Corasab2f6db2018-08-31 14:31:41 -070031static app_listener_t *
32app_listener_alloc (application_t * app)
33{
34 app_listener_t *app_listener;
35 pool_get (app->listeners, app_listener);
Dave Barachb7b92992018-10-17 10:38:51 -040036 clib_memset (app_listener, 0, sizeof (*app_listener));
Florin Corasab2f6db2018-08-31 14:31:41 -070037 app_listener->al_index = app_listener - app->listeners;
Florin Corasc9940fc2019-02-05 20:55:11 -080038 app_listener->app_index = app->app_index;
39 app_listener->session_index = SESSION_INVALID_INDEX;
40 app_listener->local_index = SESSION_INVALID_INDEX;
Florin Corasab2f6db2018-08-31 14:31:41 -070041 return app_listener;
42}
43
Florin Corasc9940fc2019-02-05 20:55:11 -080044app_listener_t *
Florin Corasab2f6db2018-08-31 14:31:41 -070045app_listener_get (application_t * app, u32 app_listener_index)
46{
47 return pool_elt_at_index (app->listeners, app_listener_index);
48}
49
Florin Coras92311f62019-03-01 19:26:31 -080050static app_listener_t *
51app_listener_get_if_valid (application_t * app, u32 app_listener_index)
52{
53 if (pool_is_free_index (app->listeners, app_listener_index))
54 return 0;
55 return pool_elt_at_index (app->listeners, app_listener_index);
56}
57
Florin Corasab2f6db2018-08-31 14:31:41 -070058static void
59app_listener_free (application_t * app, app_listener_t * app_listener)
60{
61 clib_bitmap_free (app_listener->workers);
62 pool_put (app->listeners, app_listener);
63 if (CLIB_DEBUG)
Dave Barachb7b92992018-10-17 10:38:51 -040064 clib_memset (app_listener, 0xfa, sizeof (*app_listener));
Florin Corasab2f6db2018-08-31 14:31:41 -070065}
66
Florin Corasc9940fc2019-02-05 20:55:11 -080067static u32
68app_listener_id (app_listener_t * al)
69{
70 ASSERT (al->app_index < 1 << 16 && al->al_index < 1 << 16);
71 return (al->app_index << 16 | al->al_index);
72}
73
74session_handle_t
75app_listener_handle (app_listener_t * al)
76{
77 return ((u64) SESSION_LISTENER_PREFIX << 32 | (u64) app_listener_id (al));
Florin Corasab2f6db2018-08-31 14:31:41 -070078}
79
80static void
Florin Corasc9940fc2019-02-05 20:55:11 -080081app_listener_id_parse (u32 listener_id, u32 * app_index,
82 u32 * app_listener_index)
Florin Corasab2f6db2018-08-31 14:31:41 -070083{
Florin Corasc9940fc2019-02-05 20:55:11 -080084 *app_index = listener_id >> 16;
85 *app_listener_index = listener_id & 0xFFFF;
86}
87
88void
89app_listener_handle_parse (session_handle_t handle, u32 * app_index,
90 u32 * app_listener_index)
91{
92 app_listener_id_parse (handle & 0xFFFFFFFF, app_index, app_listener_index);
93}
94
95static app_listener_t *
96app_listener_get_w_id (u32 listener_id)
97{
98 u32 app_index, app_listener_index;
99 application_t *app;
100
101 app_listener_id_parse (listener_id, &app_index, &app_listener_index);
102 app = application_get_if_valid (app_index);
103 if (!app)
104 return 0;
Florin Coras92311f62019-03-01 19:26:31 -0800105 return app_listener_get_if_valid (app, app_listener_index);
Florin Corasc9940fc2019-02-05 20:55:11 -0800106}
107
108app_listener_t *
109app_listener_get_w_session (session_t * ls)
110{
111 application_t *app;
112
113 app = application_get_if_valid (ls->app_index);
114 if (!app)
115 return 0;
116 return app_listener_get (app, ls->al_index);
117}
118
119app_listener_t *
120app_listener_get_w_handle (session_handle_t handle)
121{
122
123 if (handle >> 32 != SESSION_LISTENER_PREFIX)
124 return 0;
125
126 return app_listener_get_w_id (handle & 0xFFFFFFFF);
127}
128
129app_listener_t *
130app_listener_lookup (application_t * app, session_endpoint_cfg_t * sep_ext)
131{
132 u32 table_index, fib_proto;
133 session_endpoint_t *sep;
134 session_handle_t handle;
Florin Corasc9940fc2019-02-05 20:55:11 -0800135 session_t *ls;
136
137 sep = (session_endpoint_t *) sep_ext;
138 if (application_has_local_scope (app) && session_endpoint_is_local (sep))
139 {
140 table_index = application_local_session_table (app);
141 handle = session_lookup_endpoint_listener (table_index, sep, 1);
142 if (handle != SESSION_INVALID_HANDLE)
143 {
Florin Corasd4295e62019-02-22 13:11:38 -0800144 ls = listen_session_get_from_handle (handle);
145 return app_listener_get_w_session (ls);
Florin Corasc9940fc2019-02-05 20:55:11 -0800146 }
147 }
148
149 fib_proto = session_endpoint_fib_proto (sep);
150 table_index = application_session_table (app, fib_proto);
151 handle = session_lookup_endpoint_listener (table_index, sep, 1);
152 if (handle != SESSION_INVALID_HANDLE)
153 {
154 ls = listen_session_get_from_handle (handle);
155 return app_listener_get_w_session ((session_t *) ls);
156 }
157
158 return 0;
159}
160
161int
162app_listener_alloc_and_init (application_t * app,
163 session_endpoint_cfg_t * sep,
164 app_listener_t ** listener)
165{
166 app_listener_t *app_listener;
Florin Corasd4295e62019-02-22 13:11:38 -0800167 transport_connection_t *tc;
Florin Corasc9940fc2019-02-05 20:55:11 -0800168 session_handle_t lh;
169 session_type_t st;
170 session_t *ls = 0;
Florin Corasa27a46e2019-02-18 13:02:28 -0800171 u32 al_index;
Florin Corasc9940fc2019-02-05 20:55:11 -0800172 int rv;
173
174 app_listener = app_listener_alloc (app);
Florin Corasa27a46e2019-02-18 13:02:28 -0800175 al_index = app_listener->al_index;
Florin Corasc9940fc2019-02-05 20:55:11 -0800176 st = session_type_from_proto_and_ip (sep->transport_proto, sep->is_ip4);
177
178 /*
179 * Add session endpoint to local session table. Only binds to "inaddr_any"
180 * (i.e., zero address) are added to local scope table.
181 */
182 if (application_has_local_scope (app)
183 && session_endpoint_is_local ((session_endpoint_t *) sep))
184 {
Florin Corasd4295e62019-02-22 13:11:38 -0800185 session_type_t local_st;
Florin Corasc9940fc2019-02-05 20:55:11 -0800186 u32 table_index;
187
Florin Corasd4295e62019-02-22 13:11:38 -0800188 local_st = session_type_from_proto_and_ip (TRANSPORT_PROTO_NONE,
189 sep->is_ip4);
190 ls = listen_session_alloc (0, local_st);
191 ls->app_index = app->app_index;
192 ls->app_wrk_index = sep->app_wrk_index;
193 lh = session_handle (ls);
194
195 if ((rv = session_listen (ls, sep)))
196 {
197 ls = session_get_from_handle (lh);
198 session_free (ls);
199 return rv;
200 }
201
202 ls = session_get_from_handle (lh);
203 app_listener = app_listener_get (app, al_index);
204 app_listener->local_index = ls->session_index;
205 ls->al_index = al_index;
206
Florin Corasc9940fc2019-02-05 20:55:11 -0800207 table_index = application_local_session_table (app);
Florin Corasc9940fc2019-02-05 20:55:11 -0800208 session_lookup_add_session_endpoint (table_index,
209 (session_endpoint_t *) sep, lh);
Florin Corasc9940fc2019-02-05 20:55:11 -0800210 }
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 */
Florin Corasba7d8f52019-02-22 13:11:38 -0800220 ls = listen_session_alloc (0, st);
Florin Corasc9940fc2019-02-05 20:55:11 -0800221 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) */
Florin Corasd4295e62019-02-22 13:11:38 -0800226 lh = listen_session_get_handle (ls);
Florin Corasc9940fc2019-02-05 20:55:11 -0800227
228 if ((rv = session_listen (ls, sep)))
229 {
Florin Corasd4295e62019-02-22 13:11:38 -0800230 ls = listen_session_get_from_handle (lh);
Florin Corasc9940fc2019-02-05 20:55:11 -0800231 session_free (ls);
232 return rv;
233 }
Florin Corasd4295e62019-02-22 13:11:38 -0800234 ls = listen_session_get_from_handle (lh);
Florin Corasa27a46e2019-02-18 13:02:28 -0800235 app_listener = app_listener_get (app, al_index);
Florin Corasc9940fc2019-02-05 20:55:11 -0800236 app_listener->session_index = ls->session_index;
Florin Corasa27a46e2019-02-18 13:02:28 -0800237 ls->al_index = al_index;
Florin Corasd4295e62019-02-22 13:11:38 -0800238
239 /* Add to the global lookup table after transport was initialized.
240 * Lookup table needs to be populated only now because sessions
241 * with cut-through transport are are added to app local tables that
242 * are not related to network fibs, i.e., cannot be added as
243 * connections */
244 tc = session_get_transport (ls);
245 session_lookup_add_connection (tc, lh);
Florin Corasc9940fc2019-02-05 20:55:11 -0800246 }
247
Florin Coras2b81e3c2019-02-27 07:55:46 -0800248 if (!ls)
Florin Corasc9940fc2019-02-05 20:55:11 -0800249 {
250 app_listener_free (app, app_listener);
251 return -1;
252 }
253
254 *listener = app_listener;
255 return 0;
256}
257
258void
259app_listener_cleanup (app_listener_t * al)
260{
261 application_t *app = application_get (al->app_index);
Florin Corasd4295e62019-02-22 13:11:38 -0800262 session_t *ls;
Florin Corasc9940fc2019-02-05 20:55:11 -0800263
264 if (al->session_index != SESSION_INVALID_INDEX)
265 {
Florin Corasd4295e62019-02-22 13:11:38 -0800266 ls = session_get (al->session_index, 0);
Florin Corasc9940fc2019-02-05 20:55:11 -0800267 session_stop_listen (ls);
Florin Corasba7d8f52019-02-22 13:11:38 -0800268 listen_session_free (ls);
Florin Corasc9940fc2019-02-05 20:55:11 -0800269 }
270 if (al->local_index != SESSION_INVALID_INDEX)
271 {
272 session_endpoint_t sep = SESSION_ENDPOINT_NULL;
Florin Corasc9940fc2019-02-05 20:55:11 -0800273 u32 table_index;
274
275 table_index = application_local_session_table (app);
Florin Corasd4295e62019-02-22 13:11:38 -0800276 ls = listen_session_get (al->local_index);
Florin Coras2b81e3c2019-02-27 07:55:46 -0800277 ct_session_endpoint (ls, &sep);
Florin Corasc9940fc2019-02-05 20:55:11 -0800278 session_lookup_del_session_endpoint (table_index, &sep);
Florin Coras2b81e3c2019-02-27 07:55:46 -0800279 session_stop_listen (ls);
Florin Corasd4295e62019-02-22 13:11:38 -0800280 listen_session_free (ls);
Florin Corasc9940fc2019-02-05 20:55:11 -0800281 }
282 app_listener_free (app, al);
283}
284
Florin Coras11e2cf52019-03-06 12:04:24 -0800285static app_worker_t *
286app_listener_select_worker (application_t * app, app_listener_t * al)
Florin Corasc9940fc2019-02-05 20:55:11 -0800287{
Florin Corasc9940fc2019-02-05 20:55:11 -0800288 u32 wrk_index;
289
290 app = application_get (al->app_index);
291 wrk_index = clib_bitmap_next_set (al->workers, al->accept_rotor + 1);
292 if (wrk_index == ~0)
293 wrk_index = clib_bitmap_first_set (al->workers);
294
295 ASSERT (wrk_index != ~0);
296 al->accept_rotor = wrk_index;
297 return application_get_worker (app, wrk_index);
298}
299
300session_t *
301app_listener_get_session (app_listener_t * al)
302{
303 if (al->session_index == SESSION_INVALID_INDEX)
304 return 0;
305
306 return listen_session_get (al->session_index);
Florin Corasab2f6db2018-08-31 14:31:41 -0700307}
308
Florin Corasd4295e62019-02-22 13:11:38 -0800309session_t *
310app_listener_get_local_session (app_listener_t * al)
311{
312 if (al->local_index == SESSION_INVALID_INDEX)
313 return 0;
314 return listen_session_get (al->local_index);
315}
316
Florin Coras15531972018-08-12 23:50:53 -0700317static app_worker_map_t *
318app_worker_map_alloc (application_t * app)
319{
320 app_worker_map_t *map;
321 pool_get (app->worker_maps, map);
Dave Barachb7b92992018-10-17 10:38:51 -0400322 clib_memset (map, 0, sizeof (*map));
Florin Coras15531972018-08-12 23:50:53 -0700323 return map;
324}
Dave Barach68b0fb02017-02-28 15:15:56 -0500325
Florin Coras15531972018-08-12 23:50:53 -0700326static u32
327app_worker_map_index (application_t * app, app_worker_map_t * map)
328{
329 return (map - app->worker_maps);
330}
331
332static void
333app_worker_map_free (application_t * app, app_worker_map_t * map)
334{
335 pool_put (app->worker_maps, map);
336}
337
338static app_worker_map_t *
339app_worker_map_get (application_t * app, u32 map_index)
340{
Florin Coras01f3f892018-12-02 12:45:53 -0800341 if (pool_is_free_index (app->worker_maps, map_index))
342 return 0;
Florin Coras15531972018-08-12 23:50:53 -0700343 return pool_elt_at_index (app->worker_maps, map_index);
344}
Florin Coras0bee9ce2018-03-22 21:24:31 -0700345
Florin Coras053a0e42018-11-13 15:52:38 -0800346static const u8 *
Florin Coras0bee9ce2018-03-22 21:24:31 -0700347app_get_name (application_t * app)
348{
Florin Coras0bee9ce2018-03-22 21:24:31 -0700349 return app->name;
350}
351
Florin Corascea194d2017-10-02 00:18:51 -0700352u32
353application_session_table (application_t * app, u8 fib_proto)
354{
355 app_namespace_t *app_ns;
356 app_ns = app_namespace_get (app->ns_index);
357 if (!application_has_global_scope (app))
358 return APP_INVALID_INDEX;
359 if (fib_proto == FIB_PROTOCOL_IP4)
360 return session_lookup_get_index_for_fib (fib_proto,
361 app_ns->ip4_fib_index);
362 else
363 return session_lookup_get_index_for_fib (fib_proto,
364 app_ns->ip6_fib_index);
365}
366
367u32
368application_local_session_table (application_t * app)
369{
370 app_namespace_t *app_ns;
371 if (!application_has_local_scope (app))
372 return APP_INVALID_INDEX;
373 app_ns = app_namespace_get (app->ns_index);
374 return app_ns->local_table_index;
375}
376
Florin Corascea194d2017-10-02 00:18:51 -0700377/**
Florin Coras053a0e42018-11-13 15:52:38 -0800378 * Returns app name for app-index
Florin Corascea194d2017-10-02 00:18:51 -0700379 */
Florin Coras053a0e42018-11-13 15:52:38 -0800380const u8 *
Florin Corascea194d2017-10-02 00:18:51 -0700381application_name_from_index (u32 app_index)
382{
383 application_t *app = application_get (app_index);
384 if (!app)
385 return 0;
Florin Coras053a0e42018-11-13 15:52:38 -0800386 return app_get_name (app);
387}
388
389static void
390application_api_table_add (u32 app_index, u32 api_client_index)
391{
Florin Corasc1f5a432018-11-20 11:31:26 -0800392 if (api_client_index != APP_INVALID_INDEX)
393 hash_set (app_main.app_by_api_client_index, api_client_index, app_index);
Florin Coras053a0e42018-11-13 15:52:38 -0800394}
395
396static void
397application_api_table_del (u32 api_client_index)
398{
399 hash_unset (app_main.app_by_api_client_index, api_client_index);
Florin Corascea194d2017-10-02 00:18:51 -0700400}
401
Dave Barach68b0fb02017-02-28 15:15:56 -0500402static void
Florin Corasc1f5a432018-11-20 11:31:26 -0800403application_name_table_add (application_t * app)
Dave Barach68b0fb02017-02-28 15:15:56 -0500404{
Florin Corasc1f5a432018-11-20 11:31:26 -0800405 hash_set_mem (app_main.app_by_name, app->name, app->app_index);
Dave Barach68b0fb02017-02-28 15:15:56 -0500406}
407
408static void
Florin Corasc1f5a432018-11-20 11:31:26 -0800409application_name_table_del (application_t * app)
Dave Barach68b0fb02017-02-28 15:15:56 -0500410{
Florin Corasc1f5a432018-11-20 11:31:26 -0800411 hash_unset_mem (app_main.app_by_name, app->name);
Dave Barach68b0fb02017-02-28 15:15:56 -0500412}
413
414application_t *
415application_lookup (u32 api_client_index)
416{
417 uword *p;
Florin Coras15531972018-08-12 23:50:53 -0700418 p = hash_get (app_main.app_by_api_client_index, api_client_index);
Dave Barach68b0fb02017-02-28 15:15:56 -0500419 if (p)
Florin Coras053a0e42018-11-13 15:52:38 -0800420 return application_get_if_valid (p[0]);
Dave Barach68b0fb02017-02-28 15:15:56 -0500421
422 return 0;
423}
424
Florin Coras6cf30ad2017-04-04 23:08:23 -0700425application_t *
Florin Coras0bee9ce2018-03-22 21:24:31 -0700426application_lookup_name (const u8 * name)
427{
428 uword *p;
Florin Coras15531972018-08-12 23:50:53 -0700429 p = hash_get_mem (app_main.app_by_name, name);
Florin Coras0bee9ce2018-03-22 21:24:31 -0700430 if (p)
431 return application_get (p[0]);
432
433 return 0;
434}
435
Florin Corasc1a42652019-02-08 18:27:29 -0800436static application_t *
Florin Coras15531972018-08-12 23:50:53 -0700437application_alloc (void)
Florin Coras6cf30ad2017-04-04 23:08:23 -0700438{
439 application_t *app;
Florin Coras15531972018-08-12 23:50:53 -0700440 pool_get (app_main.app_pool, app);
Dave Barachb7b92992018-10-17 10:38:51 -0400441 clib_memset (app, 0, sizeof (*app));
Florin Coras15531972018-08-12 23:50:53 -0700442 app->app_index = app - app_main.app_pool;
Florin Coras6cf30ad2017-04-04 23:08:23 -0700443 return app;
444}
445
Florin Coras15531972018-08-12 23:50:53 -0700446application_t *
447application_get (u32 app_index)
Dave Barach68b0fb02017-02-28 15:15:56 -0500448{
Florin Coras15531972018-08-12 23:50:53 -0700449 if (app_index == APP_INVALID_INDEX)
450 return 0;
451 return pool_elt_at_index (app_main.app_pool, app_index);
452}
Dave Barach68b0fb02017-02-28 15:15:56 -0500453
Florin Coras15531972018-08-12 23:50:53 -0700454application_t *
455application_get_if_valid (u32 app_index)
456{
457 if (pool_is_free_index (app_main.app_pool, app_index))
458 return 0;
Florin Corasa5464812017-04-19 13:00:05 -0700459
Florin Coras15531972018-08-12 23:50:53 -0700460 return pool_elt_at_index (app_main.app_pool, app_index);
461}
Florin Coras7999e832017-10-31 01:51:04 -0700462
Florin Corasd79b41e2017-03-04 05:37:52 -0800463static void
Florin Coras6cf30ad2017-04-04 23:08:23 -0700464application_verify_cb_fns (session_cb_vft_t * cb_fns)
Florin Corasd79b41e2017-03-04 05:37:52 -0800465{
Florin Coras6cf30ad2017-04-04 23:08:23 -0700466 if (cb_fns->session_accept_callback == 0)
Florin Corasd79b41e2017-03-04 05:37:52 -0800467 clib_warning ("No accept callback function provided");
Florin Coras6cf30ad2017-04-04 23:08:23 -0700468 if (cb_fns->session_connected_callback == 0)
Florin Corasd79b41e2017-03-04 05:37:52 -0800469 clib_warning ("No session connected callback function provided");
470 if (cb_fns->session_disconnect_callback == 0)
471 clib_warning ("No session disconnect callback function provided");
472 if (cb_fns->session_reset_callback == 0)
473 clib_warning ("No session reset callback function provided");
474}
475
Florin Corasb384b542018-01-15 01:08:33 -0800476/**
477 * Check app config for given segment type
478 *
479 * Returns 1 on success and 0 otherwise
480 */
481static u8
482application_verify_cfg (ssvm_segment_type_t st)
483{
484 u8 is_valid;
485 if (st == SSVM_SEGMENT_MEMFD)
486 {
Florin Coras31c99552019-03-01 13:00:58 -0800487 is_valid = (session_main_get_evt_q_segment () != 0);
Florin Corasb384b542018-01-15 01:08:33 -0800488 if (!is_valid)
489 clib_warning ("memfd seg: vpp's event qs IN binary api svm region");
490 return is_valid;
491 }
492 else if (st == SSVM_SEGMENT_SHM)
493 {
Florin Coras31c99552019-03-01 13:00:58 -0800494 is_valid = (session_main_get_evt_q_segment () == 0);
Florin Corasb384b542018-01-15 01:08:33 -0800495 if (!is_valid)
496 clib_warning ("shm seg: vpp's event qs NOT IN binary api svm region");
497 return is_valid;
498 }
499 else
500 return 1;
501}
502
Florin Corasc1a42652019-02-08 18:27:29 -0800503static int
Florin Coras15531972018-08-12 23:50:53 -0700504application_alloc_and_init (app_init_args_t * a)
Dave Barach68b0fb02017-02-28 15:15:56 -0500505{
Florin Corasa332c462018-01-31 06:52:17 -0800506 ssvm_segment_type_t seg_type = SSVM_SEGMENT_MEMFD;
Florin Corasb384b542018-01-15 01:08:33 -0800507 segment_manager_properties_t *props;
508 vl_api_registration_t *reg;
Florin Coras15531972018-08-12 23:50:53 -0700509 application_t *app;
510 u64 *options;
Dave Barach68b0fb02017-02-28 15:15:56 -0500511
Florin Coras15531972018-08-12 23:50:53 -0700512 app = application_alloc ();
513 options = a->options;
Florin Corasb384b542018-01-15 01:08:33 -0800514 /*
515 * Make sure we support the requested configuration
516 */
Florin Corasa332c462018-01-31 06:52:17 -0800517 if (!(options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_IS_BUILTIN))
518 {
Florin Coras15531972018-08-12 23:50:53 -0700519 reg = vl_api_client_index_to_registration (a->api_client_index);
Florin Corasa332c462018-01-31 06:52:17 -0800520 if (!reg)
521 return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
522 if (vl_api_registration_file_index (reg) == VL_API_INVALID_FI)
523 seg_type = SSVM_SEGMENT_SHM;
524 }
525 else
526 {
527 seg_type = SSVM_SEGMENT_PRIVATE;
528 }
Florin Corasb384b542018-01-15 01:08:33 -0800529
Florin Corasd8402ae2019-03-03 16:46:52 -0800530 if ((options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_EVT_MQ_USE_EVENTFD)
531 && seg_type != SSVM_SEGMENT_MEMFD)
532 {
533 clib_warning ("mq eventfds can only be used if socket transport is "
534 "used for binary api");
535 return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
536 }
537
Florin Corasa332c462018-01-31 06:52:17 -0800538 if (!application_verify_cfg (seg_type))
Florin Corasb384b542018-01-15 01:08:33 -0800539 return VNET_API_ERROR_APP_UNSUPPORTED_CFG;
Dave Barach68b0fb02017-02-28 15:15:56 -0500540
Florin Coras15531972018-08-12 23:50:53 -0700541 /* Check that the obvious things are properly set up */
542 application_verify_cb_fns (a->session_cb_vft);
543
Florin Coras15531972018-08-12 23:50:53 -0700544 app->flags = options[APP_OPTIONS_FLAGS];
545 app->cb_fns = *a->session_cb_vft;
546 app->ns_index = options[APP_OPTIONS_NAMESPACE];
547 app->proxied_transports = options[APP_OPTIONS_PROXY_TRANSPORT];
548 app->name = vec_dup (a->name);
549
550 /* If no scope enabled, default to global */
551 if (!application_has_global_scope (app)
552 && !application_has_local_scope (app))
553 app->flags |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
554
Florin Corasa332c462018-01-31 06:52:17 -0800555 props = application_segment_manager_properties (app);
556 segment_manager_properties_init (props);
Florin Coras15531972018-08-12 23:50:53 -0700557 props->segment_size = options[APP_OPTIONS_ADD_SEGMENT_SIZE];
558 props->prealloc_fifos = options[APP_OPTIONS_PREALLOC_FIFO_PAIRS];
Florin Corasb384b542018-01-15 01:08:33 -0800559 if (options[APP_OPTIONS_ADD_SEGMENT_SIZE])
560 {
561 props->add_segment_size = options[APP_OPTIONS_ADD_SEGMENT_SIZE];
562 props->add_segment = 1;
563 }
564 if (options[APP_OPTIONS_RX_FIFO_SIZE])
565 props->rx_fifo_size = options[APP_OPTIONS_RX_FIFO_SIZE];
566 if (options[APP_OPTIONS_TX_FIFO_SIZE])
567 props->tx_fifo_size = options[APP_OPTIONS_TX_FIFO_SIZE];
Florin Corasf8f516a2018-02-08 15:10:09 -0800568 if (options[APP_OPTIONS_EVT_QUEUE_SIZE])
569 props->evt_q_size = options[APP_OPTIONS_EVT_QUEUE_SIZE];
Florin Coras99368312018-08-02 10:45:44 -0700570 if (options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_EVT_MQ_USE_EVENTFD)
571 props->use_mq_eventfd = 1;
Florin Coras58d36f02018-03-09 13:05:53 -0800572 if (options[APP_OPTIONS_TLS_ENGINE])
573 app->tls_engine = options[APP_OPTIONS_TLS_ENGINE];
Florin Corasa332c462018-01-31 06:52:17 -0800574 props->segment_type = seg_type;
Dave Barach68b0fb02017-02-28 15:15:56 -0500575
Florin Coras15531972018-08-12 23:50:53 -0700576 /* Add app to lookup by api_client_index table */
Florin Corasc1f5a432018-11-20 11:31:26 -0800577 if (!application_is_builtin (app))
578 application_api_table_add (app->app_index, a->api_client_index);
579 else
580 application_name_table_add (app);
581
582 a->app_index = app->app_index;
Florin Corasa332c462018-01-31 06:52:17 -0800583
Florin Coras15531972018-08-12 23:50:53 -0700584 APP_DBG ("New app name: %v api index: %u index %u", app->name,
585 app->api_client_index, app->app_index);
586
587 return 0;
588}
589
Florin Corasc1a42652019-02-08 18:27:29 -0800590static void
Florin Coras15531972018-08-12 23:50:53 -0700591application_free (application_t * app)
592{
593 app_worker_map_t *wrk_map;
594 app_worker_t *app_wrk;
595
596 /*
597 * The app event queue allocated in first segment is cleared with
598 * the segment manager. No need to explicitly free it.
599 */
600 APP_DBG ("Delete app name %v api index: %d index: %d", app->name,
601 app->api_client_index, app->app_index);
602
603 if (application_is_proxy (app))
604 application_remove_proxy (app);
605
Florin Corasab2f6db2018-08-31 14:31:41 -0700606 /*
607 * Free workers
608 */
609
Florin Coras15531972018-08-12 23:50:53 -0700610 /* *INDENT-OFF* */
611 pool_flush (wrk_map, app->worker_maps, ({
612 app_wrk = app_worker_get (wrk_map->wrk_index);
613 app_worker_free (app_wrk);
614 }));
615 /* *INDENT-ON* */
616 pool_free (app->worker_maps);
617
Florin Corasab2f6db2018-08-31 14:31:41 -0700618 /*
Florin Corasab2f6db2018-08-31 14:31:41 -0700619 * Cleanup remaining state
620 */
Florin Corasc1f5a432018-11-20 11:31:26 -0800621 if (application_is_builtin (app))
622 application_name_table_del (app);
Florin Coras15531972018-08-12 23:50:53 -0700623 vec_free (app->name);
624 vec_free (app->tls_cert);
625 vec_free (app->tls_key);
626 pool_put (app_main.app_pool, app);
627}
628
Florin Corasc1a42652019-02-08 18:27:29 -0800629static void
Florin Coras053a0e42018-11-13 15:52:38 -0800630application_detach_process (application_t * app, u32 api_client_index)
631{
632 vnet_app_worker_add_del_args_t _args = { 0 }, *args = &_args;
633 app_worker_map_t *wrk_map;
634 u32 *wrks = 0, *wrk_index;
635 app_worker_t *app_wrk;
636
637 if (api_client_index == ~0)
638 {
639 application_free (app);
640 return;
641 }
642
643 APP_DBG ("Detaching for app %v index %u api client index %u", app->name,
644 app->app_index, app->api_client_index);
645
646 /* *INDENT-OFF* */
647 pool_foreach (wrk_map, app->worker_maps, ({
648 app_wrk = app_worker_get (wrk_map->wrk_index);
Florin Corasc1f5a432018-11-20 11:31:26 -0800649 if (app_wrk->api_client_index == api_client_index)
Florin Coras053a0e42018-11-13 15:52:38 -0800650 vec_add1 (wrks, app_wrk->wrk_index);
651 }));
652 /* *INDENT-ON* */
653
654 if (!vec_len (wrks))
655 {
656 clib_warning ("no workers for app %u api_index %u", app->app_index,
657 api_client_index);
658 return;
659 }
660
661 args->app_index = app->app_index;
Florin Corasc1f5a432018-11-20 11:31:26 -0800662 args->api_client_index = api_client_index;
Florin Coras053a0e42018-11-13 15:52:38 -0800663 vec_foreach (wrk_index, wrks)
664 {
665 app_wrk = app_worker_get (wrk_index[0]);
Florin Coras349f8ca2018-11-20 16:52:49 -0800666 args->wrk_map_index = app_wrk->wrk_map_index;
Florin Coras053a0e42018-11-13 15:52:38 -0800667 args->is_add = 0;
668 vnet_app_worker_add_del (args);
669 }
670 vec_free (wrks);
671}
672
Florin Coras15531972018-08-12 23:50:53 -0700673app_worker_t *
674application_get_worker (application_t * app, u32 wrk_map_index)
675{
676 app_worker_map_t *map;
677 map = app_worker_map_get (app, wrk_map_index);
678 if (!map)
679 return 0;
680 return app_worker_get (map->wrk_index);
681}
682
683app_worker_t *
684application_get_default_worker (application_t * app)
685{
686 return application_get_worker (app, 0);
687}
688
Florin Coras053a0e42018-11-13 15:52:38 -0800689u32
690application_n_workers (application_t * app)
691{
692 return pool_elts (app->worker_maps);
693}
694
Florin Coras15531972018-08-12 23:50:53 -0700695app_worker_t *
Florin Corasc9940fc2019-02-05 20:55:11 -0800696application_listener_select_worker (session_t * ls)
Florin Corasab2f6db2018-08-31 14:31:41 -0700697{
Florin Coras11e2cf52019-03-06 12:04:24 -0800698 application_t *app;
Florin Corasc9940fc2019-02-05 20:55:11 -0800699 app_listener_t *al;
Florin Corasab2f6db2018-08-31 14:31:41 -0700700
Florin Coras11e2cf52019-03-06 12:04:24 -0800701 app = application_get (ls->app_index);
702 al = app_listener_get (app, ls->al_index);
703 return app_listener_select_worker (app, al);
Florin Corasab2f6db2018-08-31 14:31:41 -0700704}
705
Florin Coras15531972018-08-12 23:50:53 -0700706int
Florin Coras623eb562019-02-03 19:28:34 -0800707application_alloc_worker_and_init (application_t * app, app_worker_t ** wrk)
Florin Coras15531972018-08-12 23:50:53 -0700708{
709 app_worker_map_t *wrk_map;
710 app_worker_t *app_wrk;
711 segment_manager_t *sm;
712 int rv;
713
714 app_wrk = app_worker_alloc (app);
715 wrk_map = app_worker_map_alloc (app);
716 wrk_map->wrk_index = app_wrk->wrk_index;
717 app_wrk->wrk_map_index = app_worker_map_index (app, wrk_map);
718
719 /*
720 * Setup first segment manager
721 */
722 sm = segment_manager_new ();
723 sm->app_wrk_index = app_wrk->wrk_index;
724
725 if ((rv = segment_manager_init (sm, app->sm_properties.segment_size,
726 app->sm_properties.prealloc_fifos)))
727 {
728 app_worker_free (app_wrk);
729 return rv;
730 }
Florin Corasc87c91d2017-08-16 19:55:49 -0700731 sm->first_is_protected = 1;
Dave Barach68b0fb02017-02-28 15:15:56 -0500732
Florin Corascea194d2017-10-02 00:18:51 -0700733 /*
Florin Coras15531972018-08-12 23:50:53 -0700734 * Setup app worker
Florin Corascea194d2017-10-02 00:18:51 -0700735 */
Florin Coras15531972018-08-12 23:50:53 -0700736 app_wrk->first_segment_manager = segment_manager_index (sm);
737 app_wrk->listeners_table = hash_create (0, sizeof (u64));
738 app_wrk->event_queue = segment_manager_event_queue (sm);
739 app_wrk->app_is_builtin = application_is_builtin (app);
Dave Barach68b0fb02017-02-28 15:15:56 -0500740
Florin Coras15531972018-08-12 23:50:53 -0700741 *wrk = app_wrk;
Florin Corasf8f516a2018-02-08 15:10:09 -0800742
Florin Coras6cf30ad2017-04-04 23:08:23 -0700743 return 0;
Dave Barach68b0fb02017-02-28 15:15:56 -0500744}
745
Florin Coras623eb562019-02-03 19:28:34 -0800746int
Florin Corasc1a42652019-02-08 18:27:29 -0800747vnet_app_worker_add_del (vnet_app_worker_add_del_args_t * a)
748{
749 svm_fifo_segment_private_t *fs;
750 app_worker_map_t *wrk_map;
751 app_worker_t *app_wrk;
752 segment_manager_t *sm;
753 application_t *app;
754 int rv;
755
756 app = application_get (a->app_index);
757 if (!app)
758 return VNET_API_ERROR_INVALID_VALUE;
759
760 if (a->is_add)
761 {
762 if ((rv = application_alloc_worker_and_init (app, &app_wrk)))
763 return rv;
764
765 /* Map worker api index to the app */
766 app_wrk->api_client_index = a->api_client_index;
767 application_api_table_add (app->app_index, a->api_client_index);
768
769 sm = segment_manager_get (app_wrk->first_segment_manager);
770 fs = segment_manager_get_segment_w_lock (sm, 0);
771 a->segment = &fs->ssvm;
772 a->segment_handle = segment_manager_segment_handle (sm, fs);
773 segment_manager_segment_reader_unlock (sm);
774 a->evt_q = app_wrk->event_queue;
775 a->wrk_map_index = app_wrk->wrk_map_index;
776 }
777 else
778 {
779 wrk_map = app_worker_map_get (app, a->wrk_map_index);
780 if (!wrk_map)
781 return VNET_API_ERROR_INVALID_VALUE;
782
783 app_wrk = app_worker_get (wrk_map->wrk_index);
784 if (!app_wrk)
785 return VNET_API_ERROR_INVALID_VALUE;
786
787 application_api_table_del (app_wrk->api_client_index);
788 app_worker_free (app_wrk);
789 app_worker_map_free (app, wrk_map);
790 if (application_n_workers (app) == 0)
791 application_free (app);
792 }
793 return 0;
794}
795
796static int
797app_validate_namespace (u8 * namespace_id, u64 secret, u32 * app_ns_index)
798{
799 app_namespace_t *app_ns;
800 if (vec_len (namespace_id) == 0)
801 {
802 /* Use default namespace */
803 *app_ns_index = 0;
804 return 0;
805 }
806
807 *app_ns_index = app_namespace_index_from_id (namespace_id);
808 if (*app_ns_index == APP_NAMESPACE_INVALID_INDEX)
809 return VNET_API_ERROR_APP_INVALID_NS;
810 app_ns = app_namespace_get (*app_ns_index);
811 if (!app_ns)
812 return VNET_API_ERROR_APP_INVALID_NS;
813 if (app_ns->ns_secret != secret)
814 return VNET_API_ERROR_APP_WRONG_NS_SECRET;
815 return 0;
816}
817
818static u8 *
819app_name_from_api_index (u32 api_client_index)
820{
821 vl_api_registration_t *regp;
822 regp = vl_api_client_index_to_registration (api_client_index);
823 if (regp)
Florin Corasbee97682019-04-09 16:13:18 -0700824 return format (0, "%s", regp->name);
Florin Corasc1a42652019-02-08 18:27:29 -0800825
826 clib_warning ("api client index %u does not have an api registration!",
827 api_client_index);
Florin Corasbee97682019-04-09 16:13:18 -0700828 return format (0, "unknown");
Florin Corasc1a42652019-02-08 18:27:29 -0800829}
830
831/**
832 * Attach application to vpp
833 *
834 * Allocates a vpp app, i.e., a structure that keeps back pointers
835 * to external app and a segment manager for shared memory fifo based
836 * communication with the external app.
837 */
838int
839vnet_application_attach (vnet_app_attach_args_t * a)
840{
841 svm_fifo_segment_private_t *fs;
842 application_t *app = 0;
843 app_worker_t *app_wrk;
844 segment_manager_t *sm;
845 u32 app_ns_index = 0;
846 u8 *app_name = 0;
847 u64 secret;
848 int rv;
849
850 if (a->api_client_index != APP_INVALID_INDEX)
851 app = application_lookup (a->api_client_index);
852 else if (a->name)
853 app = application_lookup_name (a->name);
854 else
855 return VNET_API_ERROR_INVALID_VALUE;
856
857 if (app)
858 return VNET_API_ERROR_APP_ALREADY_ATTACHED;
859
860 if (a->api_client_index != APP_INVALID_INDEX)
861 {
862 app_name = app_name_from_api_index (a->api_client_index);
863 a->name = app_name;
864 }
865
866 secret = a->options[APP_OPTIONS_NAMESPACE_SECRET];
867 if ((rv = app_validate_namespace (a->namespace_id, secret, &app_ns_index)))
868 return rv;
869 a->options[APP_OPTIONS_NAMESPACE] = app_ns_index;
870
871 if ((rv = application_alloc_and_init ((app_init_args_t *) a)))
872 return rv;
873
874 app = application_get (a->app_index);
875 if ((rv = application_alloc_worker_and_init (app, &app_wrk)))
876 return rv;
877
878 a->app_evt_q = app_wrk->event_queue;
879 app_wrk->api_client_index = a->api_client_index;
880 sm = segment_manager_get (app_wrk->first_segment_manager);
881 fs = segment_manager_get_segment_w_lock (sm, 0);
882
883 if (application_is_proxy (app))
884 application_setup_proxy (app);
885
886 ASSERT (vec_len (fs->ssvm.name) <= 128);
887 a->segment = &fs->ssvm;
888 a->segment_handle = segment_manager_segment_handle (sm, fs);
889
890 segment_manager_segment_reader_unlock (sm);
891 vec_free (app_name);
892 return 0;
893}
894
895/**
896 * Detach application from vpp
897 */
898int
899vnet_application_detach (vnet_app_detach_args_t * a)
900{
901 application_t *app;
902
903 app = application_get_if_valid (a->app_index);
904 if (!app)
905 {
906 clib_warning ("app not attached");
907 return VNET_API_ERROR_APPLICATION_NOT_ATTACHED;
908 }
909
910 app_interface_check_thread_and_barrier (vnet_application_detach, a);
911 application_detach_process (app, a->api_client_index);
912 return 0;
913}
914
915
916static u8
917session_endpoint_in_ns (session_endpoint_t * sep)
918{
919 u8 is_lep = session_endpoint_is_local (sep);
920 if (!is_lep && sep->sw_if_index != ENDPOINT_INVALID_INDEX
921 && !ip_interface_has_address (sep->sw_if_index, &sep->ip, sep->is_ip4))
922 {
923 clib_warning ("sw_if_index %u not configured with ip %U",
924 sep->sw_if_index, format_ip46_address, &sep->ip,
925 sep->is_ip4);
926 return 0;
927 }
928 return (is_lep || ip_is_local (sep->fib_index, &sep->ip, sep->is_ip4));
929}
930
931static void
932session_endpoint_update_for_app (session_endpoint_cfg_t * sep,
933 application_t * app, u8 is_connect)
934{
935 app_namespace_t *app_ns;
936 u32 ns_index, fib_index;
937
938 ns_index = app->ns_index;
939
940 /* App is a transport proto, so fetch the calling app's ns */
941 if (app->flags & APP_OPTIONS_FLAGS_IS_TRANSPORT_APP)
Florin Coras8a140612019-02-18 22:39:39 -0800942 ns_index = sep->ns_index;
Florin Corasc1a42652019-02-08 18:27:29 -0800943
Florin Corasc1a42652019-02-08 18:27:29 -0800944 app_ns = app_namespace_get (ns_index);
945 if (!app_ns)
946 return;
947
948 /* Ask transport and network to bind to/connect using local interface
949 * that "supports" app's namespace. This will fix our local connection
950 * endpoint.
951 */
952
953 /* If in default namespace and user requested a fib index use it */
954 if (ns_index == 0 && sep->fib_index != ENDPOINT_INVALID_INDEX)
955 fib_index = sep->fib_index;
956 else
957 fib_index = sep->is_ip4 ? app_ns->ip4_fib_index : app_ns->ip6_fib_index;
958 sep->peer.fib_index = fib_index;
959 sep->fib_index = fib_index;
960
961 if (!is_connect)
962 {
963 sep->sw_if_index = app_ns->sw_if_index;
964 }
965 else
966 {
967 if (app_ns->sw_if_index != APP_NAMESPACE_INVALID_INDEX
968 && sep->peer.sw_if_index != ENDPOINT_INVALID_INDEX
969 && sep->peer.sw_if_index != app_ns->sw_if_index)
970 clib_warning ("Local sw_if_index different from app ns sw_if_index");
971
972 sep->peer.sw_if_index = app_ns->sw_if_index;
973 }
974}
975
976int
977vnet_listen (vnet_listen_args_t * a)
978{
979 app_listener_t *app_listener;
980 app_worker_t *app_wrk;
981 application_t *app;
982 int rv;
983
984 app = application_get_if_valid (a->app_index);
985 if (!app)
986 return VNET_API_ERROR_APPLICATION_NOT_ATTACHED;
987
988 app_wrk = application_get_worker (app, a->wrk_map_index);
989 if (!app_wrk)
990 return VNET_API_ERROR_INVALID_VALUE;
991
992 a->sep_ext.app_wrk_index = app_wrk->wrk_index;
993
994 session_endpoint_update_for_app (&a->sep_ext, app, 0 /* is_connect */ );
995 if (!session_endpoint_in_ns (&a->sep))
996 return VNET_API_ERROR_INVALID_VALUE_2;
997
998 /*
999 * Check if we already have an app listener
1000 */
1001 app_listener = app_listener_lookup (app, &a->sep_ext);
1002 if (app_listener)
1003 {
1004 if (app_listener->app_index != app->app_index)
1005 return VNET_API_ERROR_ADDRESS_IN_USE;
1006 if (app_worker_start_listen (app_wrk, app_listener))
1007 return -1;
1008 a->handle = app_listener_handle (app_listener);
1009 return 0;
1010 }
1011
1012 /*
1013 * Create new app listener
1014 */
1015 if ((rv = app_listener_alloc_and_init (app, &a->sep_ext, &app_listener)))
1016 return rv;
1017
1018 if ((rv = app_worker_start_listen (app_wrk, app_listener)))
1019 {
1020 app_listener_cleanup (app_listener);
1021 return rv;
1022 }
1023
1024 a->handle = app_listener_handle (app_listener);
1025 return 0;
1026}
1027
1028int
1029vnet_connect (vnet_connect_args_t * a)
1030{
Florin Coras2b81e3c2019-02-27 07:55:46 -08001031 app_worker_t *client_wrk;
Florin Corasc1a42652019-02-08 18:27:29 -08001032 application_t *client;
Florin Corasc1a42652019-02-08 18:27:29 -08001033
1034 if (session_endpoint_is_zero (&a->sep))
1035 return VNET_API_ERROR_INVALID_VALUE;
1036
1037 client = application_get (a->app_index);
1038 session_endpoint_update_for_app (&a->sep_ext, client, 1 /* is_connect */ );
1039 client_wrk = application_get_worker (client, a->wrk_map_index);
1040
1041 /*
1042 * First check the local scope for locally attached destinations.
1043 * If we have local scope, we pass *all* connects through it since we may
1044 * have special policy rules even for non-local destinations, think proxy.
1045 */
1046 if (application_has_local_scope (client))
1047 {
Florin Coras2b81e3c2019-02-27 07:55:46 -08001048 int rv;
Florin Corasc1a42652019-02-08 18:27:29 -08001049
Florin Coras2b81e3c2019-02-27 07:55:46 -08001050 a->sep_ext.original_tp = a->sep_ext.transport_proto;
1051 a->sep_ext.transport_proto = TRANSPORT_PROTO_NONE;
1052 rv = app_worker_connect_session (client_wrk, &a->sep, a->api_context);
1053 if (rv <= 0)
1054 return rv;
Florin Corasc1a42652019-02-08 18:27:29 -08001055 }
Florin Corasc1a42652019-02-08 18:27:29 -08001056 /*
1057 * Not connecting to a local server, propagate to transport
1058 */
1059 if (app_worker_connect_session (client_wrk, &a->sep, a->api_context))
1060 return VNET_API_ERROR_SESSION_CONNECT;
1061 return 0;
1062}
1063
1064int
1065vnet_unlisten (vnet_unlisten_args_t * a)
1066{
1067 app_worker_t *app_wrk;
1068 app_listener_t *al;
1069 application_t *app;
1070
1071 if (!(app = application_get_if_valid (a->app_index)))
1072 return VNET_API_ERROR_APPLICATION_NOT_ATTACHED;
1073
Florin Coras92311f62019-03-01 19:26:31 -08001074 if (!(al = app_listener_get_w_handle (a->handle)))
1075 return -1;
1076
Florin Corasc1a42652019-02-08 18:27:29 -08001077 if (al->app_index != app->app_index)
1078 {
1079 clib_warning ("app doesn't own handle %llu!", a->handle);
1080 return -1;
1081 }
1082
1083 app_wrk = application_get_worker (app, a->wrk_map_index);
1084 if (!app_wrk)
1085 {
1086 clib_warning ("no app %u worker %u", app->app_index, a->wrk_map_index);
1087 return -1;
1088 }
1089
1090 return app_worker_stop_listen (app_wrk, al);
1091}
1092
1093int
1094vnet_disconnect_session (vnet_disconnect_args_t * a)
1095{
Florin Coras2b81e3c2019-02-27 07:55:46 -08001096 app_worker_t *app_wrk;
1097 session_t *s;
Florin Corasc1a42652019-02-08 18:27:29 -08001098
Florin Coras2b81e3c2019-02-27 07:55:46 -08001099 s = session_get_from_handle_if_valid (a->handle);
1100 if (!s)
1101 return VNET_API_ERROR_INVALID_VALUE;
1102 app_wrk = app_worker_get (s->app_wrk_index);
1103 if (app_wrk->app_index != a->app_index)
1104 return VNET_API_ERROR_INVALID_VALUE;
Florin Corasc1a42652019-02-08 18:27:29 -08001105
Florin Coras2b81e3c2019-02-27 07:55:46 -08001106 /* We're peeking into another's thread pool. Make sure */
1107 ASSERT (s->session_index == session_index_from_handle (a->handle));
Florin Corasc1a42652019-02-08 18:27:29 -08001108
Florin Coras2b81e3c2019-02-27 07:55:46 -08001109 session_close (s);
Florin Corasc1a42652019-02-08 18:27:29 -08001110 return 0;
1111}
1112
1113int
Florin Coras623eb562019-02-03 19:28:34 -08001114application_change_listener_owner (session_t * s, app_worker_t * app_wrk)
1115{
1116 app_worker_t *old_wrk = app_worker_get (s->app_wrk_index);
1117 app_listener_t *app_listener;
1118 application_t *app;
1119
1120 if (!old_wrk)
1121 return -1;
1122
1123 hash_unset (old_wrk->listeners_table, listen_session_get_handle (s));
1124 if (session_transport_service_type (s) == TRANSPORT_SERVICE_CL
1125 && s->rx_fifo)
Florin Coras19223e02019-03-03 14:56:05 -08001126 segment_manager_dealloc_fifos (s->rx_fifo, s->tx_fifo);
Florin Coras623eb562019-02-03 19:28:34 -08001127
Florin Coras623eb562019-02-03 19:28:34 -08001128 app = application_get (old_wrk->app_index);
1129 if (!app)
1130 return -1;
1131
Florin Corasc9940fc2019-02-05 20:55:11 -08001132 app_listener = app_listener_get (app, s->al_index);
1133
1134 /* Only remove from lb for now */
Florin Coras623eb562019-02-03 19:28:34 -08001135 app_listener->workers = clib_bitmap_set (app_listener->workers,
1136 old_wrk->wrk_map_index, 0);
Florin Coras623eb562019-02-03 19:28:34 -08001137
Florin Corasc9940fc2019-02-05 20:55:11 -08001138 if (app_worker_start_listen (app_wrk, app_listener))
1139 return -1;
Florin Corasab2f6db2018-08-31 14:31:41 -07001140
Florin Corasc9940fc2019-02-05 20:55:11 -08001141 s->app_wrk_index = app_wrk->wrk_index;
Florin Coras6cf30ad2017-04-04 23:08:23 -07001142
Dave Barach68b0fb02017-02-28 15:15:56 -05001143 return 0;
1144}
1145
Dave Barach52851e62017-08-07 09:35:25 -04001146int
1147application_is_proxy (application_t * app)
1148{
Florin Coras7999e832017-10-31 01:51:04 -07001149 return (app->flags & APP_OPTIONS_FLAGS_IS_PROXY);
1150}
1151
1152int
1153application_is_builtin (application_t * app)
1154{
1155 return (app->flags & APP_OPTIONS_FLAGS_IS_BUILTIN);
1156}
1157
1158int
1159application_is_builtin_proxy (application_t * app)
1160{
1161 return (application_is_proxy (app) && application_is_builtin (app));
Dave Barach52851e62017-08-07 09:35:25 -04001162}
1163
Florin Corascea194d2017-10-02 00:18:51 -07001164u8
1165application_has_local_scope (application_t * app)
1166{
1167 return app->flags & APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
1168}
1169
1170u8
1171application_has_global_scope (application_t * app)
1172{
1173 return app->flags & APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
1174}
1175
Florin Coras7999e832017-10-31 01:51:04 -07001176static clib_error_t *
1177application_start_stop_proxy_fib_proto (application_t * app, u8 fib_proto,
1178 u8 transport_proto, u8 is_start)
1179{
Florin Coras7999e832017-10-31 01:51:04 -07001180 app_namespace_t *app_ns = app_namespace_get (app->ns_index);
1181 u8 is_ip4 = (fib_proto == FIB_PROTOCOL_IP4);
Florin Coras5665ced2018-10-25 18:03:45 -07001182 session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
Florin Coras7999e832017-10-31 01:51:04 -07001183 transport_connection_t *tc;
Florin Coras15531972018-08-12 23:50:53 -07001184 app_worker_t *app_wrk;
Florin Corasc9940fc2019-02-05 20:55:11 -08001185 app_listener_t *al;
Florin Coras288eaab2019-02-03 15:26:14 -08001186 session_t *s;
Florin Corasc9940fc2019-02-05 20:55:11 -08001187 u32 flags;
Florin Coras7999e832017-10-31 01:51:04 -07001188
Florin Coras15531972018-08-12 23:50:53 -07001189 /* TODO decide if we want proxy to be enabled for all workers */
1190 app_wrk = application_get_default_worker (app);
Florin Coras7999e832017-10-31 01:51:04 -07001191 if (is_start)
1192 {
Florin Coras15531972018-08-12 23:50:53 -07001193 s = app_worker_first_listener (app_wrk, fib_proto, transport_proto);
Florin Coras19b1f6a2017-12-11 03:37:03 -08001194 if (!s)
1195 {
1196 sep.is_ip4 = is_ip4;
1197 sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
1198 sep.sw_if_index = app_ns->sw_if_index;
1199 sep.transport_proto = transport_proto;
Florin Corasab2f6db2018-08-31 14:31:41 -07001200 sep.app_wrk_index = app_wrk->wrk_index; /* only default */
Florin Corasc9940fc2019-02-05 20:55:11 -08001201
1202 /* force global scope listener */
1203 flags = app->flags;
1204 app->flags &= ~APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
1205 app_listener_alloc_and_init (app, &sep, &al);
1206 app->flags = flags;
1207
1208 app_worker_start_listen (app_wrk, al);
1209 s = listen_session_get (al->session_index);
Florin Corasd5c604d2019-03-18 09:06:35 -07001210 s->flags |= SESSION_F_PROXY;
Florin Coras19b1f6a2017-12-11 03:37:03 -08001211 }
Florin Coras7999e832017-10-31 01:51:04 -07001212 }
1213 else
1214 {
Florin Coras623eb562019-02-03 19:28:34 -08001215 s = app_worker_proxy_listener (app_wrk, fib_proto, transport_proto);
Florin Coras19b1f6a2017-12-11 03:37:03 -08001216 ASSERT (s);
Florin Coras7999e832017-10-31 01:51:04 -07001217 }
Florin Coras19b1f6a2017-12-11 03:37:03 -08001218
Florin Coras7999e832017-10-31 01:51:04 -07001219 tc = listen_session_get_transport (s);
1220
1221 if (!ip_is_zero (&tc->lcl_ip, 1))
1222 {
Florin Corasdbd44562017-11-09 19:30:17 -08001223 u32 sti;
1224 sep.is_ip4 = is_ip4;
1225 sep.fib_index = app_namespace_get_fib_index (app_ns, fib_proto);
1226 sep.transport_proto = transport_proto;
1227 sep.port = 0;
1228 sti = session_lookup_get_index_for_fib (fib_proto, sep.fib_index);
Florin Coras19b1f6a2017-12-11 03:37:03 -08001229 if (is_start)
Florin Corasab2f6db2018-08-31 14:31:41 -07001230 session_lookup_add_session_endpoint (sti,
1231 (session_endpoint_t *) & sep,
1232 s->session_index);
Florin Coras19b1f6a2017-12-11 03:37:03 -08001233 else
Florin Corasab2f6db2018-08-31 14:31:41 -07001234 session_lookup_del_session_endpoint (sti,
1235 (session_endpoint_t *) & sep);
Florin Coras7999e832017-10-31 01:51:04 -07001236 }
Florin Coras19b1f6a2017-12-11 03:37:03 -08001237
Florin Coras7999e832017-10-31 01:51:04 -07001238 return 0;
1239}
1240
Florin Coras19b1f6a2017-12-11 03:37:03 -08001241static void
1242application_start_stop_proxy_local_scope (application_t * app,
1243 u8 transport_proto, u8 is_start)
1244{
1245 session_endpoint_t sep = SESSION_ENDPOINT_NULL;
1246 app_namespace_t *app_ns;
1247 app_ns = app_namespace_get (app->ns_index);
1248 sep.is_ip4 = 1;
1249 sep.transport_proto = transport_proto;
1250 sep.port = 0;
1251
1252 if (is_start)
1253 {
1254 session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
Florin Coras15531972018-08-12 23:50:53 -07001255 app->app_index);
Florin Coras19b1f6a2017-12-11 03:37:03 -08001256 sep.is_ip4 = 0;
1257 session_lookup_add_session_endpoint (app_ns->local_table_index, &sep,
Florin Coras15531972018-08-12 23:50:53 -07001258 app->app_index);
Florin Coras19b1f6a2017-12-11 03:37:03 -08001259 }
1260 else
1261 {
1262 session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
1263 sep.is_ip4 = 0;
1264 session_lookup_del_session_endpoint (app_ns->local_table_index, &sep);
1265 }
1266}
1267
Florin Coras7999e832017-10-31 01:51:04 -07001268void
Florin Coras561af9b2017-12-09 10:19:43 -08001269application_start_stop_proxy (application_t * app,
1270 transport_proto_t transport_proto, u8 is_start)
Florin Coras7999e832017-10-31 01:51:04 -07001271{
Florin Coras7999e832017-10-31 01:51:04 -07001272 if (application_has_local_scope (app))
Florin Coras19b1f6a2017-12-11 03:37:03 -08001273 application_start_stop_proxy_local_scope (app, transport_proto, is_start);
Florin Coras7999e832017-10-31 01:51:04 -07001274
1275 if (application_has_global_scope (app))
1276 {
1277 application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP4,
1278 transport_proto, is_start);
1279 application_start_stop_proxy_fib_proto (app, FIB_PROTOCOL_IP6,
1280 transport_proto, is_start);
1281 }
1282}
1283
1284void
1285application_setup_proxy (application_t * app)
1286{
1287 u16 transports = app->proxied_transports;
Florin Coras561af9b2017-12-09 10:19:43 -08001288 transport_proto_t tp;
1289
Florin Coras7999e832017-10-31 01:51:04 -07001290 ASSERT (application_is_proxy (app));
Florin Coras561af9b2017-12-09 10:19:43 -08001291
1292 /* *INDENT-OFF* */
1293 transport_proto_foreach (tp, ({
1294 if (transports & (1 << tp))
1295 application_start_stop_proxy (app, tp, 1);
1296 }));
1297 /* *INDENT-ON* */
Florin Coras7999e832017-10-31 01:51:04 -07001298}
1299
1300void
1301application_remove_proxy (application_t * app)
1302{
1303 u16 transports = app->proxied_transports;
Florin Coras561af9b2017-12-09 10:19:43 -08001304 transport_proto_t tp;
1305
Florin Coras7999e832017-10-31 01:51:04 -07001306 ASSERT (application_is_proxy (app));
Florin Coras561af9b2017-12-09 10:19:43 -08001307
1308 /* *INDENT-OFF* */
1309 transport_proto_foreach (tp, ({
1310 if (transports & (1 << tp))
1311 application_start_stop_proxy (app, tp, 0);
1312 }));
1313 /* *INDENT-ON* */
Florin Coras7999e832017-10-31 01:51:04 -07001314}
1315
Florin Corasa332c462018-01-31 06:52:17 -08001316segment_manager_properties_t *
1317application_segment_manager_properties (application_t * app)
1318{
1319 return &app->sm_properties;
1320}
1321
1322segment_manager_properties_t *
1323application_get_segment_manager_properties (u32 app_index)
1324{
1325 application_t *app = application_get (app_index);
1326 return &app->sm_properties;
1327}
1328
Florin Coras371ca502018-02-21 12:07:41 -08001329clib_error_t *
1330vnet_app_add_tls_cert (vnet_app_add_tls_cert_args_t * a)
1331{
1332 application_t *app;
1333 app = application_get (a->app_index);
1334 if (!app)
1335 return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1336 0, "app %u doesn't exist", a->app_index);
1337 app->tls_cert = vec_dup (a->cert);
1338 return 0;
1339}
1340
1341clib_error_t *
1342vnet_app_add_tls_key (vnet_app_add_tls_key_args_t * a)
1343{
1344 application_t *app;
1345 app = application_get (a->app_index);
1346 if (!app)
1347 return clib_error_return_code (0, VNET_API_ERROR_APPLICATION_NOT_ATTACHED,
1348 0, "app %u doesn't exist", a->app_index);
1349 app->tls_key = vec_dup (a->key);
1350 return 0;
1351}
1352
Florin Coras15531972018-08-12 23:50:53 -07001353static void
1354application_format_listeners (application_t * app, int verbose)
1355{
1356 vlib_main_t *vm = vlib_get_main ();
1357 app_worker_map_t *wrk_map;
1358 app_worker_t *app_wrk;
1359 u32 sm_index;
1360 u64 handle;
1361
1362 if (!app)
1363 {
1364 vlib_cli_output (vm, "%U", format_app_worker_listener, 0 /* header */ ,
1365 0, 0, verbose);
1366 return;
1367 }
1368
1369 /* *INDENT-OFF* */
1370 pool_foreach (wrk_map, app->worker_maps, ({
1371 app_wrk = app_worker_get (wrk_map->wrk_index);
1372 if (hash_elts (app_wrk->listeners_table) == 0)
1373 continue;
1374 hash_foreach (handle, sm_index, app_wrk->listeners_table, ({
1375 vlib_cli_output (vm, "%U", format_app_worker_listener, app_wrk,
1376 handle, sm_index, verbose);
1377 }));
1378 }));
1379 /* *INDENT-ON* */
1380}
1381
1382static void
Florin Coras15531972018-08-12 23:50:53 -07001383application_format_connects (application_t * app, int verbose)
1384{
1385 app_worker_map_t *wrk_map;
1386 app_worker_t *app_wrk;
1387
1388 if (!app)
1389 {
1390 app_worker_format_connects (0, verbose);
1391 return;
1392 }
1393
1394 /* *INDENT-OFF* */
1395 pool_foreach (wrk_map, app->worker_maps, ({
1396 app_wrk = app_worker_get (wrk_map->wrk_index);
1397 app_worker_format_connects (app_wrk, verbose);
1398 }));
1399 /* *INDENT-ON* */
1400}
1401
Florin Coras6cf30ad2017-04-04 23:08:23 -07001402u8 *
1403format_application (u8 * s, va_list * args)
1404{
1405 application_t *app = va_arg (*args, application_t *);
1406 CLIB_UNUSED (int verbose) = va_arg (*args, int);
Florin Corasad0c77f2017-11-09 18:00:15 -08001407 segment_manager_properties_t *props;
Florin Coras053a0e42018-11-13 15:52:38 -08001408 const u8 *app_ns_name, *app_name;
Florin Coras349f8ca2018-11-20 16:52:49 -08001409 app_worker_map_t *wrk_map;
1410 app_worker_t *app_wrk;
Florin Coras6cf30ad2017-04-04 23:08:23 -07001411
1412 if (app == 0)
1413 {
Florin Coras349f8ca2018-11-20 16:52:49 -08001414 if (!verbose)
Florin Corasc1f5a432018-11-20 11:31:26 -08001415 s = format (s, "%-10s%-20s%-40s", "Index", "Name", "Namespace");
Florin Coras6cf30ad2017-04-04 23:08:23 -07001416 return s;
1417 }
1418
Florin Coras0bee9ce2018-03-22 21:24:31 -07001419 app_name = app_get_name (app);
Florin Corascea194d2017-10-02 00:18:51 -07001420 app_ns_name = app_namespace_id_from_index (app->ns_index);
Florin Corasa332c462018-01-31 06:52:17 -08001421 props = application_segment_manager_properties (app);
Florin Coras349f8ca2018-11-20 16:52:49 -08001422 if (!verbose)
1423 {
Florin Corasfa7512e2019-04-04 22:31:50 -07001424 s = format (s, "%-10u%-20v%-40s", app->app_index, app_name,
Florin Coras349f8ca2018-11-20 16:52:49 -08001425 app_ns_name);
1426 return s;
1427 }
1428
Florin Corasfa7512e2019-04-04 22:31:50 -07001429 s = format (s, "app-name %v app-index %u ns-index %u seg-size %U\n",
Florin Coras349f8ca2018-11-20 16:52:49 -08001430 app_name, app->app_index, app->ns_index,
1431 format_memory_size, props->add_segment_size);
1432 s = format (s, "rx-fifo-size %U tx-fifo-size %U workers:\n",
1433 format_memory_size, props->rx_fifo_size,
1434 format_memory_size, props->tx_fifo_size);
1435
1436 /* *INDENT-OFF* */
1437 pool_foreach (wrk_map, app->worker_maps, ({
1438 app_wrk = app_worker_get (wrk_map->wrk_index);
Florin Coras623eb562019-02-03 19:28:34 -08001439 s = format (s, "%U", format_app_worker, app_wrk);
Florin Coras349f8ca2018-11-20 16:52:49 -08001440 }));
1441 /* *INDENT-ON* */
1442
Dave Barach68b0fb02017-02-28 15:15:56 -05001443 return s;
1444}
1445
Florin Corasf8f516a2018-02-08 15:10:09 -08001446void
Florin Coras2b81e3c2019-02-27 07:55:46 -08001447application_format_all_listeners (vlib_main_t * vm, int verbose)
Florin Corasf8f516a2018-02-08 15:10:09 -08001448{
1449 application_t *app;
Florin Corasf8f516a2018-02-08 15:10:09 -08001450
Florin Coras15531972018-08-12 23:50:53 -07001451 if (!pool_elts (app_main.app_pool))
Florin Corasf8f516a2018-02-08 15:10:09 -08001452 {
1453 vlib_cli_output (vm, "No active server bindings");
1454 return;
1455 }
1456
Florin Coras2b81e3c2019-02-27 07:55:46 -08001457 application_format_listeners (0, verbose);
Florin Corasf8f516a2018-02-08 15:10:09 -08001458
Florin Coras2b81e3c2019-02-27 07:55:46 -08001459 /* *INDENT-OFF* */
1460 pool_foreach (app, app_main.app_pool, ({
1461 application_format_listeners (app, verbose);
1462 }));
1463 /* *INDENT-ON* */
Florin Corasf8f516a2018-02-08 15:10:09 -08001464}
1465
1466void
Florin Coras2b81e3c2019-02-27 07:55:46 -08001467application_format_all_clients (vlib_main_t * vm, int verbose)
Florin Corasf8f516a2018-02-08 15:10:09 -08001468{
1469 application_t *app;
1470
Florin Coras15531972018-08-12 23:50:53 -07001471 if (!pool_elts (app_main.app_pool))
Florin Corasf8f516a2018-02-08 15:10:09 -08001472 {
1473 vlib_cli_output (vm, "No active apps");
1474 return;
1475 }
1476
Florin Coras2b81e3c2019-02-27 07:55:46 -08001477 application_format_connects (0, verbose);
Florin Corasf8f516a2018-02-08 15:10:09 -08001478
Florin Coras2b81e3c2019-02-27 07:55:46 -08001479 /* *INDENT-OFF* */
1480 pool_foreach (app, app_main.app_pool, ({
1481 application_format_connects (app, verbose);
1482 }));
1483 /* *INDENT-ON* */
Florin Corasf8f516a2018-02-08 15:10:09 -08001484}
1485
Dave Barach68b0fb02017-02-28 15:15:56 -05001486static clib_error_t *
1487show_app_command_fn (vlib_main_t * vm, unformat_input_t * input,
1488 vlib_cli_command_t * cmd)
1489{
Florin Coras2b81e3c2019-02-27 07:55:46 -08001490 int do_server = 0, do_client = 0;
Dave Barach68b0fb02017-02-28 15:15:56 -05001491 application_t *app;
Florin Coras349f8ca2018-11-20 16:52:49 -08001492 u32 app_index = ~0;
Dave Barach68b0fb02017-02-28 15:15:56 -05001493 int verbose = 0;
1494
Florin Corascea194d2017-10-02 00:18:51 -07001495 session_cli_return_if_not_enabled ();
Florin Corase04c2992017-03-01 08:17:34 -08001496
Dave Barach68b0fb02017-02-28 15:15:56 -05001497 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1498 {
1499 if (unformat (input, "server"))
1500 do_server = 1;
1501 else if (unformat (input, "client"))
1502 do_client = 1;
Florin Coras349f8ca2018-11-20 16:52:49 -08001503 else if (unformat (input, "%u", &app_index))
1504 ;
Dave Barach68b0fb02017-02-28 15:15:56 -05001505 else if (unformat (input, "verbose"))
1506 verbose = 1;
1507 else
Florin Coras349f8ca2018-11-20 16:52:49 -08001508 return clib_error_return (0, "unknown input `%U'",
1509 format_unformat_error, input);
Dave Barach68b0fb02017-02-28 15:15:56 -05001510 }
1511
1512 if (do_server)
Florin Coras349f8ca2018-11-20 16:52:49 -08001513 {
Florin Coras2b81e3c2019-02-27 07:55:46 -08001514 application_format_all_listeners (vm, verbose);
Florin Coras349f8ca2018-11-20 16:52:49 -08001515 return 0;
1516 }
Dave Barach68b0fb02017-02-28 15:15:56 -05001517
1518 if (do_client)
Florin Coras349f8ca2018-11-20 16:52:49 -08001519 {
Florin Coras2b81e3c2019-02-27 07:55:46 -08001520 application_format_all_clients (vm, verbose);
Florin Coras349f8ca2018-11-20 16:52:49 -08001521 return 0;
1522 }
1523
1524 if (app_index != ~0)
1525 {
Florin Coras47c40e22018-11-26 17:01:36 -08001526 app = application_get_if_valid (app_index);
Florin Coras349f8ca2018-11-20 16:52:49 -08001527 if (!app)
1528 return clib_error_return (0, "No app with index %u", app_index);
1529
1530 vlib_cli_output (vm, "%U", format_application, app, /* verbose */ 1);
1531 return 0;
1532 }
Dave Barach68b0fb02017-02-28 15:15:56 -05001533
Florin Coras6cf30ad2017-04-04 23:08:23 -07001534 /* Print app related info */
1535 if (!do_server && !do_client)
1536 {
Florin Coras349f8ca2018-11-20 16:52:49 -08001537 vlib_cli_output (vm, "%U", format_application, 0, 0);
Florin Corascea194d2017-10-02 00:18:51 -07001538 /* *INDENT-OFF* */
Florin Coras15531972018-08-12 23:50:53 -07001539 pool_foreach (app, app_main.app_pool, ({
Florin Coras349f8ca2018-11-20 16:52:49 -08001540 vlib_cli_output (vm, "%U", format_application, app, 0);
Florin Corascea194d2017-10-02 00:18:51 -07001541 }));
1542 /* *INDENT-ON* */
Florin Coras6cf30ad2017-04-04 23:08:23 -07001543 }
1544
Dave Barach68b0fb02017-02-28 15:15:56 -05001545 return 0;
1546}
1547
Florin Corase04c2992017-03-01 08:17:34 -08001548/* *INDENT-OFF* */
Dave Barach68b0fb02017-02-28 15:15:56 -05001549VLIB_CLI_COMMAND (show_app_command, static) =
1550{
Florin Corase04c2992017-03-01 08:17:34 -08001551 .path = "show app",
1552 .short_help = "show app [server|client] [verbose]",
1553 .function = show_app_command_fn,
1554};
1555/* *INDENT-ON* */
Dave Barach68b0fb02017-02-28 15:15:56 -05001556
1557/*
1558 * fd.io coding-style-patch-verification: ON
1559 *
1560 * Local Variables:
1561 * eval: (c-set-style "gnu")
1562 * End:
1563 */