FD.io VPP  v18.01-8-g0eacf49
Vector Packet Processing
application_interface.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2016 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  */
16 
17 #include <vnet/session/session.h>
18 #include <vlibmemory/api.h>
19 #include <vnet/dpo/load_balance.h>
20 
21 /** @file
22  VPP's application/session API bind/unbind/connect/disconnect calls
23 */
24 
25 static u8
27 {
28  return (ip_is_zero (&sep->ip, sep->is_ip4)
29  || ip_is_local_host (&sep->ip, sep->is_ip4));
30 }
31 
32 static u8
34 {
35  return ip_is_zero (&sep->ip, sep->is_ip4);
36 }
37 
38 u8
40 {
41  u8 is_zero = ip_is_zero (&sep->ip, sep->is_ip4);
42  if (!is_zero && sep->sw_if_index != ENDPOINT_INVALID_INDEX
43  && !ip_interface_has_address (sep->sw_if_index, &sep->ip, sep->is_ip4))
44  {
45  clib_warning ("sw_if_index %u not configured with ip %U",
46  sep->sw_if_index, format_ip46_address, &sep->ip,
47  sep->is_ip4);
48  return 0;
49  }
50  return (is_zero || ip_is_local (sep->fib_index, &sep->ip, sep->is_ip4));
51 }
52 
53 int
54 api_parse_session_handle (u64 handle, u32 * session_index, u32 * thread_index)
55 {
57  stream_session_t *pool;
58 
59  *thread_index = handle & 0xFFFFFFFF;
60  *session_index = handle >> 32;
61 
62  if (*thread_index >= vec_len (smm->sessions))
63  return VNET_API_ERROR_INVALID_VALUE;
64 
65  pool = smm->sessions[*thread_index];
66 
67  if (pool_is_free_index (pool, *session_index))
68  return VNET_API_ERROR_INVALID_VALUE_2;
69 
70  return 0;
71 }
72 
73 static void
75  application_t * app)
76 {
77  app_namespace_t *app_ns;
78  app_ns = app_namespace_get (app->ns_index);
79  if (app_ns)
80  {
81  /* Ask transport and network to bind to/connect using local interface
82  * that "supports" app's namespace. This will fix our local connection
83  * endpoint.
84  */
85  sep->sw_if_index = app_ns->sw_if_index;
86  sep->fib_index =
87  sep->is_ip4 ? app_ns->ip4_fib_index : app_ns->ip6_fib_index;
88  }
89 }
90 
91 static int
93 {
94  application_t *app;
95  u32 table_index;
96  u64 listener;
97  int rv, have_local = 0;
98 
99  app = application_get_if_valid (app_index);
100  if (!app)
101  {
102  SESSION_DBG ("app not attached");
103  return VNET_API_ERROR_APPLICATION_NOT_ATTACHED;
104  }
105 
107  if (!session_endpoint_in_ns (sep))
108  return VNET_API_ERROR_INVALID_VALUE_2;
109 
110  table_index = application_session_table (app,
112  listener = session_lookup_endpoint_listener (table_index, sep, 1);
113  if (listener != SESSION_INVALID_HANDLE)
114  return VNET_API_ERROR_ADDRESS_IN_USE;
115 
116  /*
117  * Add session endpoint to local session table. Only binds to "inaddr_any"
118  * (i.e., zero address) are added to local scope table.
119  */
121  {
122  table_index = application_local_session_table (app);
123  listener = session_lookup_endpoint_listener (table_index, sep, 1);
124  if (listener != SESSION_INVALID_HANDLE)
125  return VNET_API_ERROR_ADDRESS_IN_USE;
126  session_lookup_add_session_endpoint (table_index, sep, app->index);
128  have_local = 1;
129  }
130 
131  if (!application_has_global_scope (app))
132  return (have_local - 1);
133 
134  /*
135  * Add session endpoint to global session table
136  */
137 
138  /* Setup listen path down to transport */
139  rv = application_start_listen (app, sep, handle);
140  if (rv && have_local)
141  session_lookup_del_session_endpoint (table_index, sep);
142  return rv;
143 }
144 
145 int
147 {
148  application_t *app = application_get_if_valid (app_index);
149  stream_session_t *listener = 0;
150  u32 table_index;
151 
152  if (!app)
153  {
154  SESSION_DBG ("app (%d) not attached", app_index);
155  return VNET_API_ERROR_APPLICATION_NOT_ATTACHED;
156  }
157 
158  /*
159  * Clean up local session table. If we have a listener session use it to
160  * find the port and proto. If not, the handle must be a local table handle
161  * so parse it.
162  */
163 
164  if (application_has_local_scope (app))
165  {
167  if (!session_lookup_local_is_handle (handle))
168  listener = listen_session_get_from_handle (handle);
169  if (listener)
170  {
171  if (listen_session_get_local_session_endpoint (listener, &sep))
172  {
173  clib_warning ("broken listener");
174  return -1;
175  }
176  }
177  else
178  {
180  {
181  clib_warning ("can't parse handle");
182  return -1;
183  }
184  }
185  table_index = application_local_session_table (app);
186  session_lookup_del_session_endpoint (table_index, &sep);
187  }
188 
189  /*
190  * Clear the global scope table of the listener
191  */
193  return application_stop_listen (app, handle);
194  return 0;
195 }
196 
197 static int
199 {
200  return server->cb_fns.redirect_connect_callback (server->api_client_index,
201  mp);
202 }
203 
204 int
206  void *mp)
207 {
208  application_t *server, *app;
209  u32 table_index, server_index;
210  stream_session_t *listener;
211 
212  if (session_endpoint_is_zero (sep))
213  return VNET_API_ERROR_INVALID_VALUE;
214 
215  app = application_get (app_index);
217 
218  /*
219  * First check the the local scope for locally attached destinations.
220  * If we have local scope, we pass *all* connects through it since we may
221  * have special policy rules even for non-local destinations, think proxy.
222  */
223  if (application_has_local_scope (app))
224  {
225  table_index = application_local_session_table (app);
226  server_index = session_lookup_local_endpoint (table_index, sep);
227  if (server_index == APP_DROP_INDEX)
228  return VNET_API_ERROR_APP_CONNECT_FILTERED;
229 
230  /*
231  * Break loop if rule in local table points to connecting app. This
232  * can happen if client is a generic proxy. Route connect through
233  * global table instead.
234  */
235  if (server_index != app_index)
236  {
237  server = application_get (server_index);
238  /*
239  * Server is willing to have a direct fifo connection created
240  * instead of going through the state machine, etc.
241  */
242  if (server && (server->flags & APP_OPTIONS_FLAGS_ACCEPT_REDIRECT))
243  return app_connect_redirect (server, mp);
244  }
245  }
246 
247  /*
248  * If nothing found, check the global scope for locally attached
249  * destinations. Make sure first that we're allowed to.
250  */
251  if (session_endpoint_is_local (sep))
252  return VNET_API_ERROR_SESSION_CONNECT;
253 
254  if (!application_has_global_scope (app))
255  return VNET_API_ERROR_APP_CONNECT_SCOPE;
256 
257  table_index = application_session_table (app,
259  listener = session_lookup_listener (table_index, sep);
260  if (listener)
261  {
262  server = application_get (listener->app_index);
263  if (server && (server->flags & APP_OPTIONS_FLAGS_ACCEPT_REDIRECT))
264  return app_connect_redirect (server, mp);
265  }
266 
267  /*
268  * Not connecting to a local server, propagate to transport
269  */
270  if (application_open_session (app, sep, api_context))
271  return VNET_API_ERROR_SESSION_CONNECT;
272  return 0;
273 }
274 
275 /**
276  * unformat a vnet URI
277  *
278  * fifo://name
279  * tcp://ip46-addr:port
280  * udp://ip46-addr:port
281  *
282  * u8 ip46_address[16];
283  * u16 port_in_host_byte_order;
284  * stream_session_type_t sst;
285  * u8 *fifo_name;
286  *
287  * if (unformat (input, "%U", unformat_vnet_uri, &ip46_address,
288  * &sst, &port, &fifo_name))
289  * etc...
290  *
291  */
292 uword
293 unformat_vnet_uri (unformat_input_t * input, va_list * args)
294 {
295  session_endpoint_t *sep = va_arg (*args, session_endpoint_t *);
296 
297  if (unformat (input, "tcp://%U/%d", unformat_ip4_address, &sep->ip.ip4,
298  &sep->port))
299  {
300  sep->transport_proto = TRANSPORT_PROTO_TCP;
301  sep->port = clib_host_to_net_u16 (sep->port);
302  sep->is_ip4 = 1;
303  return 1;
304  }
305  if (unformat (input, "udp://%U/%d", unformat_ip4_address, &sep->ip.ip4,
306  &sep->port))
307  {
308  sep->transport_proto = TRANSPORT_PROTO_UDP;
309  sep->port = clib_host_to_net_u16 (sep->port);
310  sep->is_ip4 = 1;
311  return 1;
312  }
313  if (unformat (input, "udp://%U/%d", unformat_ip6_address, &sep->ip.ip6,
314  &sep->port))
315  {
316  sep->transport_proto = TRANSPORT_PROTO_UDP;
317  sep->port = clib_host_to_net_u16 (sep->port);
318  sep->is_ip4 = 0;
319  return 1;
320  }
321  if (unformat (input, "tcp://%U/%d", unformat_ip6_address, &sep->ip.ip6,
322  &sep->port))
323  {
324  sep->transport_proto = TRANSPORT_PROTO_TCP;
325  sep->port = clib_host_to_net_u16 (sep->port);
326  sep->is_ip4 = 0;
327  return 1;
328  }
329 
330  return 0;
331 }
332 
333 static u8 *cache_uri;
335 
336 int
337 parse_uri (char *uri, session_endpoint_t * sep)
338 {
339  unformat_input_t _input, *input = &_input;
340 
341  if (cache_uri && !strncmp (uri, (char *) cache_uri, vec_len (cache_uri)))
342  {
343  *sep = *cache_sep;
344  return 0;
345  }
346 
347  /* Make sure */
348  uri = (char *) format (0, "%s%c", uri, 0);
349 
350  /* Parse uri */
351  unformat_init_string (input, uri, strlen (uri));
352  if (!unformat (input, "%U", unformat_vnet_uri, sep))
353  {
354  unformat_free (input);
355  return VNET_API_ERROR_INVALID_VALUE;
356  }
357  unformat_free (input);
358 
360  cache_uri = (u8 *) uri;
361  if (cache_sep)
363  cache_sep = clib_mem_alloc (sizeof (*sep));
364  *cache_sep = *sep;
365 
366  return 0;
367 }
368 
369 static int
370 session_validate_namespace (u8 * namespace_id, u64 secret, u32 * app_ns_index)
371 {
372  app_namespace_t *app_ns;
373  if (vec_len (namespace_id) == 0)
374  {
375  /* Use default namespace */
376  *app_ns_index = 0;
377  return 0;
378  }
379 
380  *app_ns_index = app_namespace_index_from_id (namespace_id);
381  if (*app_ns_index == APP_NAMESPACE_INVALID_INDEX)
382  return VNET_API_ERROR_APP_INVALID_NS;
383  app_ns = app_namespace_get (*app_ns_index);
384  if (!app_ns)
385  return VNET_API_ERROR_APP_INVALID_NS;
386  if (app_ns->ns_secret != secret)
387  return VNET_API_ERROR_APP_WRONG_NS_SECRET;
388  return 0;
389 }
390 
391 /**
392  * Attach application to vpp
393  *
394  * Allocates a vpp app, i.e., a structure that keeps back pointers
395  * to external app and a segment manager for shared memory fifo based
396  * communication with the external app.
397  */
398 clib_error_t *
400 {
401  application_t *app = 0;
402  segment_manager_t *sm;
403  u8 *seg_name;
404  u64 secret;
405  u32 app_ns_index = 0;
406  int rv;
407 
408  app = application_lookup (a->api_client_index);
409  if (app)
410  return clib_error_return_code (0, VNET_API_ERROR_APP_ALREADY_ATTACHED,
411  0, "app already attached");
412 
413  secret = a->options[APP_OPTIONS_NAMESPACE_SECRET];
414  if ((rv = session_validate_namespace (a->namespace_id, secret,
415  &app_ns_index)))
416  return clib_error_return_code (0, rv, 0, "namespace validation: %d", rv);
417  a->options[APP_OPTIONS_NAMESPACE] = app_ns_index;
418  app = application_new ();
419  if ((rv = application_init (app, a->api_client_index, a->options,
420  a->session_cb_vft)))
421  return clib_error_return_code (0, rv, 0, "app init: %d", rv);
422 
423  a->app_event_queue_address = pointer_to_uword (app->event_queue);
424  sm = segment_manager_get (app->first_segment_manager);
425  segment_manager_get_segment_info (sm->segment_indices[0],
426  &seg_name, &a->segment_size);
427 
428  if (application_is_proxy (app))
430 
431  a->segment_name_length = vec_len (seg_name);
432  a->segment_name = seg_name;
433  ASSERT (vec_len (a->segment_name) <= 128);
434  a->app_index = app->index;
435  return 0;
436 }
437 
438 /**
439  * Detach application from vpp
440  */
441 int
443 {
444  application_t *app;
445  app = application_get_if_valid (a->app_index);
446 
447  if (!app)
448  {
449  clib_warning ("app not attached");
450  return VNET_API_ERROR_APPLICATION_NOT_ATTACHED;
451  }
452 
453  application_del (app);
454  return 0;
455 }
456 
457 int
459 {
461  int rv;
462 
463  rv = parse_uri (a->uri, &sep);
464  if (rv)
465  return rv;
466 
467  return vnet_bind_i (a->app_index, &sep, &a->handle);
468 }
469 
470 int
472 {
473  stream_session_t *listener;
475  int rv;
476 
477  rv = parse_uri (a->uri, &sep);
478  if (rv)
479  return rv;
480 
481  /* NOTE: only default table supported for uri */
482  listener = session_lookup_listener (0, &sep);
483  if (!listener)
484  return VNET_API_ERROR_ADDRESS_NOT_IN_USE;
485 
486  return vnet_unbind_i (a->app_index, listen_session_get_handle (listener));
487 }
488 
489 clib_error_t *
491 {
493  int rv;
494 
495  /* Parse uri */
496  a->sep = sep_null;
497  rv = parse_uri (a->uri, &a->sep);
498  if (rv)
499  return clib_error_return_code (0, rv, 0, "app init: %d", rv);
500  if ((rv = vnet_connect_i (a->app_index, a->api_context, &a->sep, a->mp)))
501  return clib_error_return_code (0, rv, 0, "connect failed");
502  return 0;
503 }
504 
505 int
507 {
508  u32 index, thread_index;
509  stream_session_t *s;
510 
511  session_parse_handle (a->handle, &index, &thread_index);
512  s = session_get_if_valid (index, thread_index);
513 
514  if (!s || s->app_index != a->app_index)
515  return VNET_API_ERROR_INVALID_VALUE;
516 
517  /* We're peeking into another's thread pool. Make sure */
518  ASSERT (s->session_index == index);
519 
521  thread_index);
522  return 0;
523 }
524 
525 clib_error_t *
527 {
528  int rv;
529  if ((rv = vnet_bind_i (a->app_index, &a->sep, &a->handle)))
530  return clib_error_return_code (0, rv, 0, "bind failed");
531  return 0;
532 }
533 
534 clib_error_t *
536 {
537  int rv;
538  if ((rv = vnet_unbind_i (a->app_index, a->handle)))
539  return clib_error_return_code (0, rv, 0, "unbind failed");
540  return 0;
541 }
542 
543 clib_error_t *
545 {
546  int rv;
547  if ((rv = vnet_connect_i (a->app_index, a->api_context, &a->sep, a->mp)))
548  return clib_error_return_code (0, rv, 0, "connect failed");
549  return 0;
550 }
551 
552 /*
553  * fd.io coding-style-patch-verification: ON
554  *
555  * Local Variables:
556  * eval: (c-set-style "gnu")
557  * End:
558  */
int application_open_session(application_t *app, session_endpoint_t *sep, u32 api_context)
Definition: application.c:421
u8 session_endpoint_in_ns(session_endpoint_t *sep)
#define APP_NAMESPACE_INVALID_INDEX
int application_stop_listen(application_t *srv, u64 handle)
Stop listening on session associated to handle.
Definition: application.c:385
static stream_session_t * listen_session_get_from_handle(u64 handle)
Definition: session.h:474
a
Definition: bitmap.h:516
u64 session_lookup_endpoint_listener(u32 table_index, session_endpoint_t *sep, u8 use_rules)
Lookup listener for session endpoint in table.
static u8 session_endpoint_is_local(session_endpoint_t *sep)
u8 application_has_global_scope(application_t *app)
Definition: application.c:499
struct _vnet_connect_args vnet_connect_args_t
int vnet_bind_uri(vnet_bind_args_t *a)
u8 ip_is_local(u32 fib_index, ip46_address_t *ip46_address, u8 is_ip4)
Checks that an ip is local to the requested fib.
Definition: ip.c:41
clib_error_t * vnet_unbind(vnet_unbind_args_t *a)
u8 ip_interface_has_address(u32 sw_if_index, ip46_address_t *ip, u8 is_ip4)
Definition: ip.c:85
application_t * application_new()
Definition: application.c:133
application_t * application_lookup(u32 api_client_index)
Definition: application.c:122
format_function_t format_ip46_address
Definition: format.h:61
static int vnet_bind_i(u32 app_index, session_endpoint_t *sep, u64 *handle)
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
static u8 * cache_uri
stream_session_t * session_lookup_listener(u32 table_index, session_endpoint_t *sep)
#define SESSION_ENDPOINT_NULL
void segment_manager_get_segment_info(u32 index, u8 **name, u32 *size)
int application_is_proxy(application_t *app)
Definition: application.c:460
int vnet_unbind_uri(vnet_unbind_args_t *a)
unformat_function_t unformat_ip4_address
Definition: format.h:76
struct _vnet_disconnect_args_t vnet_disconnect_args_t
struct _vnet_unbind_args_t vnet_unbind_args_t
unsigned long u64
Definition: types.h:89
u32 app_namespace_index_from_id(const u8 *ns_id)
struct _stream_session_t stream_session_t
void session_send_session_evt_to_thread(u64 session_handle, fifo_event_type_t evt_type, u32 thread_index)
Definition: session.c:59
int parse_uri(char *uri, session_endpoint_t *sep)
static uword pointer_to_uword(const void *p)
Definition: types.h:131
void unformat_init_string(unformat_input_t *input, char *string, int string_len)
Definition: unformat.c:1023
struct _vnet_app_attach_args_t vnet_app_attach_args_t
static void session_parse_handle(u64 handle, u32 *index, u32 *thread_index)
Definition: session.h:267
static session_manager_main_t * vnet_get_session_manager_main()
Definition: session.h:207
struct _session_endpoint session_endpoint_t
struct _unformat_input_t unformat_input_t
int application_start_listen(application_t *srv, session_endpoint_t *sep, u64 *res)
Start listening local transport endpoint for requested transport.
Definition: application.c:347
app_namespace_t * app_namespace_get(u32 index)
clib_error_t * vnet_connect(vnet_connect_args_t *a)
static stream_session_t * session_get_if_valid(u64 si, u32 thread_index)
Definition: session.h:236
u32 application_session_table(application_t *app, u8 fib_proto)
Definition: application.c:52
struct _session_manager_main session_manager_main_t
Definition: session.h:103
unformat_function_t unformat_ip6_address
Definition: format.h:94
#define SESSION_DBG(_fmt, _args...)
u32 application_local_session_table(application_t *app)
Definition: application.c:67
static int app_connect_redirect(application_t *server, void *mp)
static session_endpoint_t * cache_sep
int session_lookup_local_listener_parse_handle(u64 handle, session_endpoint_t *sep)
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:336
#define ENDPOINT_INVALID_INDEX
Definition: transport.h:97
int session_lookup_del_session_endpoint(u32 table_index, session_endpoint_t *sep)
#define clib_warning(format, args...)
Definition: error.h:59
#define SESSION_INVALID_HANDLE
Definition: session_table.h:57
u64 session_lookup_local_listener_make_handle(session_endpoint_t *sep)
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:268
struct _application application_t
int vnet_disconnect_session(vnet_disconnect_args_t *a)
int api_parse_session_handle(u64 handle, u32 *session_index, u32 *thread_index)
int session_lookup_add_session_endpoint(u32 table_index, session_endpoint_t *sep, u64 value)
struct _app_namespace app_namespace_t
int vnet_connect_i(u32 app_index, u32 api_context, session_endpoint_t *sep, void *mp)
#define ASSERT(truth)
unsigned int u32
Definition: types.h:88
clib_error_t * vnet_bind(vnet_bind_args_t *a)
static void session_endpoint_update_for_app(session_endpoint_t *sep, application_t *app)
static u8 session_endpoint_fib_proto(session_endpoint_t *sep)
int application_init(application_t *app, u32 api_client_index, u64 *options, session_cb_vft_t *cb_fns)
Definition: application.c:231
static void clib_mem_free(void *p)
Definition: mem.h:179
static u8 session_endpoint_is_zero(session_endpoint_t *sep)
int listen_session_get_local_session_endpoint(stream_session_t *listener, session_endpoint_t *sep)
Definition: session.c:1046
void application_del(application_t *app)
Definition: application.c:147
static int app_index
struct _vnet_app_detach_args_t vnet_app_detach_args_t
static void * clib_mem_alloc(uword size)
Definition: mem.h:112
u64 uword
Definition: types.h:112
u8 ip_is_zero(ip46_address_t *ip46_address, u8 is_ip4)
Definition: ip.c:20
uword unformat_vnet_uri(unformat_input_t *input, va_list *args)
unformat a vnet URI
clib_error_t * vnet_connect_uri(vnet_connect_args_t *a)
int vnet_unbind_i(u32 app_index, u64 handle)
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
unsigned char u8
Definition: types.h:56
clib_error_t * vnet_application_attach(vnet_app_attach_args_t *a)
Attach application to vpp.
application_t * application_get(u32 index)
Definition: application.c:297
static void unformat_free(unformat_input_t *i)
Definition: format.h:161
u8 ip_is_local_host(ip46_address_t *ip46_address, u8 is_ip4)
Definition: ip.c:29
struct _segment_manager segment_manager_t
u8 session_lookup_local_is_handle(u64 handle)
#define clib_error_return_code(e, code, flags, args...)
Definition: error.h:93
u32 session_lookup_local_endpoint(u32 table_index, session_endpoint_t *sep)
Look up endpoint in local session table.
void application_setup_proxy(application_t *app)
Definition: application.c:652
static int session_validate_namespace(u8 *namespace_id, u64 secret, u32 *app_ns_index)
int vnet_application_detach(vnet_app_detach_args_t *a)
Detach application from vpp.
struct _vnet_bind_args_t vnet_bind_args_t
static segment_manager_t * segment_manager_get(u32 index)
static u64 listen_session_get_handle(stream_session_t *s)
Definition: session.h:467
application_t * application_get_if_valid(u32 index)
Definition: application.c:305
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:972
#define APP_DROP_INDEX
Definition: application.h:106
u8 application_has_local_scope(application_t *app)
Definition: application.c:493